martinkasa / capacitor-secure-storage-plugin Goto Github PK
View Code? Open in Web Editor NEWCapacitor plugin for storing string values securly on iOS and Android.
License: MIT License
Capacitor plugin for storing string values securly on iOS and Android.
License: MIT License
Storage implementations usually offers a way to iterate over all the entries in the storage :
This benefits in being able to only remove the entries you were responsible for (ie. looking for entries with prefixed keys) and not remove the whole storage.
-> For iOS, SwiftKeychainWrapper already implements an .allKeys api (missing in their doc).
-> For Android, I haven't checked yet.
Hi , I love the plugin. Is there any way we could get react hooks like : capacitor-community/react-hooks . Thank's for the plugin !
We’re in the process of updating our Angular / Ionic 6 app so it can use Okta authentication and to do this we are using the ionic-appauth library. That library uses the capacitor-secure-storage-plugin to store it’s authentication token on the device.
We’ve come across a problem that only seems to occur on our Android 11 test devices - other versions of Android work as do all versions of iOS.
The problem occurs when the application is fully closed by the user i.e when they swipe-to-close it so it it not running in background.
On all devices apart from the Android 11 ones, the app opens up and the user is authenticated because their token is retrieved from storage. On the Android 11 device an error occurs (“Valid token could not be found”).
In other areas of the app we use the non-secure capacitor storage plugin and anything stored there is retrieved ok on startup of the app. So it looks like it’s simply an issue with Android 11 and the secure storage plugin.
Has anyone come across this or would have any idea if there is any solution - did Android 11 have specific security settings that other versions do not have? (Android 12 works fine)
Try : SecureStoragePlugin.set({key, value})
In an angular 9 project, execute ng build command and error "TS1005: ':' expected" occur.
How I can fix it ? I already tried all ts lint exclusion like :
Thanks in advance for your return !
Hi, I can't get a key using get method
async doLogin() {
const key = 'password';
const value = 'tomato';
SecureStoragePlugin.set({ key, value }).then(val => {
console.log(val);
});
}
private async getSecure(key) {
await SecureStoragePlugin.get({ key }).then(value => {
// Do things with value
}).catch(error => {
console.log('Item with specified key does not exist.');
});
}
Don't know why but I always get the message Item with specified key does not exist. although the method set returns me TRUE.
Can you help me?
Rafael
Capacitor 3 is being built and is already in beta. In this new version the native plugins have been removed from the core and there are some changes that are necessary in the existing plugins.
Example: Plugin Imports
The Plugins object is deprecated, but will continue to work in Capacitor 3. Capacitor plugins should be updated to use the new plugin registration APIs (see the Upgrade Guide for plugins), which will allow them to be imported directly from the plugin’s package.
Going forward, the Plugins object from @capacitor/core should not be used.
// OLD
import { Plugins } from '@capacitor/core';
const { AnyPlugin } = Plugins;
Importing the plugin directly from the plugin’s package is preferred, but the plugin must be updated to work with Capacitor 3 for this to be possible.
// NEW
import { AnyPlugin } from 'any-plugin';
References:
Updating Capacitor to 3.0 in your plugin
Updating Capacitor to 3.0 in your app
(credits to @mklipe for issue explanation)
Consider below code:
await SecureStoragePlugin.set({ key: 'key', value: 'value' })
const { value: keys } = await SecureStoragePlugin.keys()
// [ 'cap_sec_key' ]
await SecureStoragePlugin.get({ key: keys[0] })
// 'Item with given key does not exist'
It fails, because .keys()
function returns keys with cap_sec_
prefix and .get()
function adds the prefix again, so it looks for cap_sec_cap_sec_key
instead of cap_sec_key
.
This affects web platform only. The above code works fine on Android and iOS platforms.
Hi all,
for use with Capacitor v2 please use fixed version to 0.5.1
"capacitor-secure-storage-plugin": "0.5.1"
for Capacitor 3.0 use latest or 0.6.0
"capacitor-secure-storage-plugin": "^0.6.0"
I have tested both versions, but if you find some bug, feel free to open new issue.
Originally posted by @martinkasa in #30 (comment)
Disclaimer, I'm fairly new to Android and Capacitor development.
For Android, is there a reason EncryptedSharedPreferences aren't used? It seems like this would greatly simplify the plugin and essentially remove the need for the entire PasswordStorageHelper.java
class.
Will you be interested into implementing (or accetting PRs) about adding the ability to use setUserAuthenticationRequired
(and it's iOS corrispective) to secure the data (if biometric sensors are available)?
Inspiration: https://medium.com/beautycoder/android-security-and-fingerprint-ef0f6f344888
Related (exactly the opposite problem): NiklasMerz/cordova-plugin-fingerprint-aio#186
I'm trying to update value of key(which already exist in storage). I tried,
SecureStoragePlugin.set({key,newvalue}).then(success=>{console.log('looks like we did it')});
but it keeps on showing error. I don't want to first delete the key and then store it with updated value which i believe will increase the processing time. Is there any way of updating value of key without deleting it?
When I storing a json and stringtify it and when I pull the data from storage it will return using promise
Unexpected token o in JSON at position 1
Calling Storage.set()
throws an error if called on iPhones (checked on iphone 12 and iphone XR). Note that it works well on the web version.
[email protected]
plugin (maybe this step is not required, in our case the bug occurred after an upgrade).SecureStoragePlugin.set( { key: 'credentials', value: JSON.stringify( { token: '123' } ) } );
[email protected]
SecureStoragePlugin.set( { key: 'credentials', value: JSON.stringify( { token: '123' } ) } );
Device: iPhone XR, iOS 15.4.1
Plugin version: 0.6.4 and 0.7.0 (0.6.2 works well)
Ionic:
Ionic CLI : 6.18.0 (/home/***/.config/yarn/global/node_modules/@ionic/cli)
Ionic Framework : @ionic/angular 6.1.6
@angular-devkit/build-angular : 13.2.6
@angular-devkit/schematics : 13.2.6
@angular/cli : 13.2.6
@ionic/angular-toolkit : 6.1.0
Capacitor:
Capacitor CLI : 3.5.1
@capacitor/android : not installed
@capacitor/core : 3.5.1
@capacitor/ios : 3.5.1
Utility:
cordova-res : not installed globally
native-run : 1.6.0
System:
NodeJS : v16.13.0 (/home/***/.nvm/versions/node/v16.13.0/bin/node)
npm : 8.1.0
I am trying to store my refresh token on ios and android in the secure storage. For some reason, despite the storage reporting success on setting the value, it was not able to get the value on reloading.
On further investigation, it seems that the key and value are being set as undefined, so when i try to get the key 'refresh_token', it indeed does not exist.
Capacitor Doctor:
Latest Dependencies:
@capacitor/cli: 2.2.1
@capacitor/core: 2.2.1
@capacitor/android: 2.2.1
@capacitor/electron: 2.2.1
@capacitor/ios: 2.2.1
Installed Dependencies:
@capacitor/electron not installed
@capacitor/cli 2.1.2
@capacitor/core 2.1.2
@capacitor/android 2.1.2
@capacitor/ios 2.1.2
[success] Android looking great! 👌
Found 6 Capacitor plugins for ios:
@byteowls/capacitor-oauth2 (2.0.0)
capacitor-secure-storage-plugin (0.4.0)
cordova-plugin-androidx-adapter (1.1.1)
cordova-plugin-camera (4.1.0)
cordova-plugin-inappbrowser (4.0.1-dev)
cordova-plugin-video-editor (1.1.3)
My code used to set the key value:
private useAccessToken(response) {
// tslint:disable-next-line:no-string-literal
this.setupUser(response['access_token']);
// store ref token and save it to local secure storage.
this.refreshToken = response['refresh_token'];
const refToken = this.refreshToken;
const refTokenKey = 'refresh_token';
SecureStoragePlugin.set({ refTokenKey, refToken }).then(
success => console.log('Refresh Token Stored: ', success)
)
.catch(error => console.log('Error: ', error));
this.profileInfo = response['profile_info'];
}
Why would be key be being set as undefined?
Hi, I like this plugin so much. Thanks for your great work!
I have a question:
It would be nice to be able to create new symmetric and asymmetric keys directly with this plugin.
I have not found a plugin to do that jet, and this one seems to be the proper one.
It would be possibile to have this feature implemented?
For Android and iOS should be quite simple, not sure about the web compatibility.
Thanks in avance!
I'm getting TypeError for the line import 'capacitor-secure-storage-plugin';
whenever I'm testing with jest and enzyme. Is this line still needed?
The jest error: TypeError: Super expression must either be null or a function
Is there a way to use session storage instead of local storage?
Good morning,
I would like to understand if this plugin rely on Hardware / Secure Enclave / HSM generated keys when data are going to be ciphered to be stored in the Keychain/Keystore storage.
My understanding is that :
Could you provide this information ?
Best regards
When I use this plugin on an Android device/app it seems to be using the web implementation instead of the Android implementation.
Can't see any error in the logs, all seems to compile alright.
Using Capacitor 1.3.0.
Hey, thanks for your lovely work!
Unfortunately this lib is not (yet) compatible with the newest capacitor release:
npm ERR! Found: @capacitor/[email protected]
npm ERR! node_modules/@capacitor/core
npm ERR! optional @capacitor/core@"^4.0.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @capacitor/core@"^3.0.0" from [email protected]
npm ERR! node_modules/capacitor-secure-storage-plugin
npm ERR! optional capacitor-secure-storage-plugin@"^0.7.1" from the root project
I still get the warning Capacitor WebPlugin "SecureStoragePlugin" config object was deprecated in v3 and will be removed in v4.
, even though I use version "^0.6.2" with Capacitor v3
When I try to get an non-existing or removed key on iOS the promise correctly gets rejected but on Android and in the browser it resolves with a weird looking value:
{"value":"\u009eée"}
I can implement a check in the resolve callback as a workaround but this might be something worth looking into.
Expected Behavior: App can access secure storage when device is locked
Actual Behavior: App cannot access secure storage when device is locked with passcode. Happens only on iOS devices.
See title. On testing the app on IOS 11 out of Testflight the storage does not get cleared after an app uninstall and all data still is there if you install again. I suppose that's an IOS thing ... is there a way to force this? Works fine on Android devices though. The data in the store is just one entry as a big(gish) JSON string.
I'm migrating a Cordova project that uses cordova-plugin-secure-storage to Capacitor, and I was wondering if this is different (more secure) than the built-in Capacitor Storage?
I'm not a Swift / Java developer but upon casual inspection of Capacitor Storage's source, it seems that on iOS it's not using Keychain and on Android it's not encrypting the values in SharedPreferences like your plugin seems to be doing.
I have an existing cordova app that we are migrating to capacitor.
The previously cordova plugin we were using allowed the serviceName
to be specified when reading/writing data.
However this plugin looks like the serviceName
is hardcoded to cap_sec here, which means we cannot migrate to this plugin.
The ideal outcome would be to allow the serviceName
optionally be passed into the methods, and to fallback to cap_sec
if its not supplied (to be backwards compatible).
I've installed the plugin on a new Ionic/Capacitor project, removed the android platform and re-created it with npx cap add android
.
When I launch ./gradlew build
inside the android folder, I get an error for the task ":capacitor-secure-storage-plugin:lint".
Here is the log file of the build: build.log
This is the software I use:
This is the package.json of the project:
{ "name": "openalpr", "version": "0.0.1", "author": "Ionic Framework", "homepage": "https://ionicframework.com/", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { "@angular/common": "~13.0.0", "@angular/core": "~13.0.0", "@angular/forms": "~13.0.0", "@angular/platform-browser": "~13.0.0", "@angular/platform-browser-dynamic": "~13.0.0", "@angular/router": "~13.0.0", "@awesome-cordova-plugins/core": "^5.39.0", "@awesome-cordova-plugins/openalpr": "^5.39.0", "@capacitor/android": "^3.4.0", "@capacitor/camera": "^1.2.4", "@capacitor/core": "^3.4.0", "@ionic/angular": "^6.0.0", "capacitor-secure-storage-plugin": "^0.6.2", "cordova-plugin-camera-preview": "^0.12.3", "cordova-plugin-openalpr": "^2.1.0", "rxjs": "~6.6.0", "tslib": "^2.2.0", "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~13.0.1", "@angular-eslint/builder": "~13.0.1", "@angular-eslint/eslint-plugin": "~13.0.1", "@angular-eslint/eslint-plugin-template": "~13.0.1", "@angular-eslint/template-parser": "~13.0.1", "@angular/cli": "~13.0.1", "@angular/compiler": "~13.0.0", "@angular/compiler-cli": "~13.0.0", "@angular/language-service": "~13.0.0", "@capacitor/cli": "^3.4.0", "@ionic/angular-toolkit": "^5.0.0", "@types/jasmine": "~3.6.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "5.3.0", "@typescript-eslint/parser": "5.3.0", "eslint": "^7.6.0", "eslint-plugin-import": "2.22.1", "eslint-plugin-jsdoc": "30.7.6", "eslint-plugin-prefer-arrow": "1.2.2", "jasmine-core": "~3.8.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~6.3.2", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "protractor": "~7.0.0", "serve": "^13.0.2", "ts-node": "~8.3.0", "typescript": "~4.4.4" }, "description": "An Ionic project" }
On Android the plugin hard crashes when calling await SecureStoragePlugin.set({'someKey', undefined})
. This is because of an unhandled NPE that happens when calling value.getBytes
:
Funny enough the iOS version already handles possible null values correctly:
I'm running a ionic project on an ios device (emulator or real) but the plugin only saves values into local storage, to be lost on app removal.
Here visible in the emulator using safari debug tools:
The same happens on a real device, nothing is saved to keychain.
You mentioned in another ticket that this might be related to imports - might it be a problem that I'm also using DeviceInfo import?
import { DeviceInfo } from '@capacitor/core';
[...]
import 'capacitor-secure-storage-plugin';
import { Plugins } from '@capacitor/core';
const { SecureStoragePlugin } = Plugins;
Hi!
Currently we use this plugin so that app user can log in via a digit code. This digit code is set up by the app user during the onboarding in our app.
We have migrated from Capacitor 2 to Capacitor 4 which made us update the version we use of this plugin. When we were running on Capacitor 2 we used version 0.4.0 of this package and after updating to Capacitor 4 we use the most recent (0.8.0) version.
After that version bump all of our app user (iOS & Android, except Web) suddenly lost there digit code which needed them to onboard again.
Investigation showed us that te problem disappeared when we downgraden the version from 0.8.0 to 0.4.0: the digit code was present again! Especially, the problem is caused in the changes made between 0.5.1 and 0.6.0 (see diff)
Reproduction steps:
"dependencies": {
"@capacitor/android": "4.1.0",
"@capacitor/core": "4.1.0",
"@capacitor/ios": "4.1.0",
"capacitor-secure-storage-plugin": "0.5.1"
}
await SecureStoragePlugin.set({ key: 'digitCode', value: '1234' });
SecureStoragePlugin.get({ key: 'digitCode' })
.then(console.log) // '1234'
.catch(console.error) // not called
SecureStoragePlugin.get({ key: 'digitCode' })
.then(console.log) // not called
.catch(console.error) // results in 'Item with given key does not exist'
Expected behaviour
Step 4 must result in logging the digit code which was set before updating to version 0.6.0
In the documentation it states to include the plugin in the mainActivity as such:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initializes the Bridge
this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
// Additional plugins you've installed go here
// Ex: add(TotallyAwesomePlugin.class);
add(SecureStoragePlugin.class);
}});
}
However in the upgrade guide of capacitor 3 it states we no longer need to register plugins that way.
Is it still required to register this plugin in capacitor 3 or not?
I add the plugin in my ionic 4 capacitor app and add the plugin in the main activity but the import fails because the package is not found:
import com.whitestein.securestorage.SecureStoragePlugin;
The app builds and install correctly but i had this error in the logcat:
Capacitor/Plugin: Attempt to invoke virtual method 'byte[] java.lang.String.getBytes(java.nio.charset.Charset)' on a null object reference
java.lang.NullPointerException: Attempt to invoke virtual method 'byte[] java.lang.String.getBytes(java.nio.charset.Charset)' on a null object reference
at com.whitestein.securestorage.SecureStoragePlugin._set(SecureStoragePlugin.java:73)
at com.whitestein.securestorage.SecureStoragePlugin.set(SecureStoragePlugin.java:32)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$1.run(Bridge.java:520)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.os.HandlerThread.run(HandlerThread.java:65)
Hello,
Through process of elimination, removing each pod line and running the pod install, I encounter the below issue when CapacitorSecureStoragePlugin is included in the Podfile.
Is arm64 (M1 Mac) not supported?
Or have I not got something installed.
/usr/local/bin/pod install
CocoaPods : 1.11.2
Ruby : ruby 2.6.8p205 (2021-07-07 revision 67951) [universal.x86_64-darwin21]
RubyGems : 3.0.3.1
Host : macOS 12.1 (21C52)
Xcode : 13.2.1 (13C100)
Git : git version 2.36.0
Ruby lib dir : /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib
Repositories : trunk - CDN - https://cdn.cocoapods.org/
cocoapods-deintegrate : 1.0.5
cocoapods-plugins : 1.0.0
cocoapods-search : 1.0.1
cocoapods-trunk : 1.6.0
cocoapods-try : 1.2.0
platform :ios, '12.0'
use_frameworks!
# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = ""
config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"
end
end
end
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'ByteowlsCapacitorOauth2', :path => '../../node_modules/@byteowls/capacitor-oauth2'
pod 'CapacitorCommunityAppcenterAnalytics', :path => '../../node_modules/@capacitor-community/appcenter-analytics'
pod 'CapacitorCommunityBarcodeScanner', :path => '../../node_modules/@capacitor-community/barcode-scanner'
pod 'CapacitorCommunityHttp', :path => '../../node_modules/@capacitor-community/http'
pod 'CapacitorActionSheet', :path => '../../node_modules/@capacitor/action-sheet'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog'
pod 'CapacitorGeolocation', :path => '../../node_modules/@capacitor/geolocation'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast'
pod 'CapacitorSecureStoragePlugin', :path => '../../node_modules/capacitor-secure-storage-plugin'
end
target 'App' do
capacitor_pods
# Add your Pods here
end
LoadError - dlopen(/Users/azureagent/.gem/ruby/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle, 0x0009): tried: '/Users/azureagent/.gem/ruby/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle' (mach-o file, but is an incompatible architecture (have 'arm64', need 'x86_64')), '/usr/lib/ffi_c.bundle' (no such file) - /Users/azureagent/.gem/ruby/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/Users/azureagent/.gem/ruby/2.6.0/gems/ffi-1.15.5/lib/ffi.rb:5:in `rescue in <top (required)>'
/Users/azureagent/.gem/ruby/2.6.0/gems/ffi-1.15.5/lib/ffi.rb:2:in `<top (required)>'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/Library/Ruby/Gems/2.6.0/gems/ethon-0.15.0/lib/ethon.rb:3:in `<top (required)>'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/Library/Ruby/Gems/2.6.0/gems/typhoeus-1.4.0/lib/typhoeus.rb:2:in `<top (required)>'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/cdn_source.rb:440:in `download_typhoeus_impl_async'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/cdn_source.rb:372:in `download_and_save_with_retries_async'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/cdn_source.rb:365:in `download_file_async'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/cdn_source.rb:338:in `download_file'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/cdn_source.rb:284:in `ensure_versions_file_loaded'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/cdn_source.rb:208:in `search'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/source/aggregate.rb:83:in `block in search'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/source/aggregate.rb:83:in `select'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-core-1.11.2/lib/cocoapods-core/source/aggregate.rb:83:in `search'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:416:in `create_set_from_sources'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:385:in `find_cached_set'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:360:in `specifications_for_dependency'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:165:in `search_for'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:274:in `block in sort_dependencies'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:267:in `each'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:267:in `sort_by'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:267:in `sort_by!'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:267:in `sort_dependencies'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/delegates/specification_provider.rb:60:in `block in sort_dependencies'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/delegates/specification_provider.rb:77:in `with_no_such_dependency_error_handling'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/delegates/specification_provider.rb:59:in `sort_dependencies'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolution.rb:754:in `push_state_for_requirements'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolution.rb:744:in `require_nested_dependencies_for'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolution.rb:727:in `activate_new_spec'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolution.rb:684:in `attempt_to_activate'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolution.rb:254:in `process_topmost_state'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolution.rb:182:in `resolve'
/Library/Ruby/Gems/2.6.0/gems/molinillo-0.8.0/lib/molinillo/resolver.rb:43:in `resolve'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/resolver.rb:94:in `resolve'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer/analyzer.rb:1078:in `block in resolve_dependencies'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/user_interface.rb:64:in `section'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer/analyzer.rb:1076:in `resolve_dependencies'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer/analyzer.rb:124:in `analyze'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer.rb:416:in `analyze'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer.rb:241:in `block in resolve_dependencies'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/user_interface.rb:64:in `section'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer.rb:240:in `resolve_dependencies'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/installer.rb:161:in `install!'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/command/install.rb:52:in `run'
/Library/Ruby/Gems/2.6.0/gems/claide-1.1.0/lib/claide/command.rb:334:in `run'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/command.rb:52:in `run'
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/bin/pod:55:in `<top (required)>'
/usr/local/bin/pod:23:in `load'
/usr/local/bin/pod:23:in `<main>'
Previously I created this issue for the following reason.
This plugin is getting the following error when minifyEnabled
is set to true
in Android.
This error is actually coming always whether or not minifyEnabled
is true.
E/Capacitor: Unable to read file at path public/plugins
E/Capacitor/Plugin: Item with given key does not exist
java.lang.Exception: Item with given key does not exist
at com.whitestein.securestorage.SecureStoragePlugin.Z(:95)
at com.whitestein.securestorage.SecureStoragePlugin.get(:45)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.t0.h(:121)
at com.getcapacitor.a0.D(:584)
at com.getcapacitor.a0.E(Unknown Source:0)
at com.getcapacitor.a.run(Unknown Source:8)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.os.HandlerThread.run(HandlerThread.java:67)
Could you please share the ProGuard rule for this plugin which is needed for proguard-rules.pro
file?
Reproduction:
ionic start testApp
ionic build
ioinic cap add android
npm install capacitor-secure-storage-plugin
ionic cap sync
ionic cap open android
Error:
2020-04-10 11:16:35.673 4524-4524/? I/o.ionic.starte: Late-enabling -Xcheck:jni
2020-04-10 11:16:35.793 4524-4556/io.ionic.starter I/o.ionic.starte: The ClassLoaderContext is a special shared library.
2020-04-10 11:16:36.008 4524-4524/io.ionic.starter I/Perf: Connecting to perf service.
2020-04-10 11:16:36.022 4524-4524/io.ionic.starter W/FirebaseApp: Default FirebaseApp failed to initialize because no default options were found. This usually means that com.google.gms:google-services was not applied to your gradle project.
2020-04-10 11:16:36.022 4524-4524/io.ionic.starter I/FirebaseInitProvider: FirebaseApp initialization unsuccessful
2020-04-10 11:16:36.097 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden method Landroid/graphics/drawable/Drawable;->getOpticalInsets()Landroid/graphics/Insets; (light greylist, linking)
2020-04-10 11:16:36.097 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden field Landroid/graphics/Insets;->left:I (light greylist, linking)
2020-04-10 11:16:36.097 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden field Landroid/graphics/Insets;->right:I (light greylist, linking)
2020-04-10 11:16:36.097 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden field Landroid/graphics/Insets;->top:I (light greylist, linking)
2020-04-10 11:16:36.097 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden field Landroid/graphics/Insets;->bottom:I (light greylist, linking)
2020-04-10 11:16:36.105 4524-4524/io.ionic.starter E/o.ionic.starte: Invalid ID 0x00000000.
2020-04-10 11:16:36.158 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden method Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate; (light greylist, linking)
2020-04-10 11:16:36.166 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
2020-04-10 11:16:36.167 4524-4524/io.ionic.starter W/o.ionic.starte: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
2020-04-10 11:16:36.171 4524-4524/io.ionic.starter D/AndroidRuntime: Shutting down VM
2020-04-10 11:16:36.176 4524-4524/io.ionic.starter E/AndroidRuntime: FATAL EXCEPTION: main
Process: io.ionic.starter, PID: 4524
java.lang.RuntimeException: Unable to start activity ComponentInfo{io.ionic.starter/io.ionic.starter.MainActivity}: android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class android.support.design.widget.CoordinatorLayout
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3047)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3182)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1916)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class android.support.design.widget.CoordinatorLayout
Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class android.support.design.widget.CoordinatorLayout
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.support.design.widget.CoordinatorLayout" on path: DexPathList[[zip file "/data/app/io.ionic.starter-HL1m187oOUhYQheeuueT3A==/base.apk"],nativeLibraryDirectories=[/data/app/io.ionic.starter-HL1m187oOUhYQheeuueT3A==/lib/arm64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.view.LayoutInflater.createView(LayoutInflater.java:606)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:555)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:161)
at com.getcapacitor.BridgeActivity.init(BridgeActivity.java:56)
at io.ionic.starter.MainActivity.onCreate(MainActivity.java:18)
at android.app.Activity.performCreate(Activity.java:7149)
at android.app.Activity.performCreate(Activity.java:7140)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1288)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3027)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3182)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1916)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2020-04-10 11:16:36.201 4524-4612/? D/OSTracker: OS Event: crash
2020-04-10 11:16:36.222 4524-4612/? D/AbstractTracker: Event success
2020-04-10 11:16:36.223 4524-4524/? I/Process: Sending signal. PID: 4524 SIG: 9
Hello,
i'm using your plugin and get the UIWebView API Deprecation when submitting to App Store Connect. I searched my code for the string "UIWebView" and found out that it is referenced in the following file: ios/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVUserAgentUtil.m
Is it possible, that the Warning from Apple occours because of that file?
I also saw that Capacitor made a fix for it, see ionic-team/capacitor@3034885.
Best regards
Jakob
Based on the use cases provided by this plugin it might be easier & faster (in my opinion) to use symmetric AES 256-bit encryption.
That's why I was wondering why we are using an asymmetric encryption? As illustrated here: https://github.com/martinkasa/capacitor-secure-storage-plugin/blob/master/android/src/main/java/com/whitestein/securestorage/PasswordStorageHelper.java#L167 & here: https://github.com/martinkasa/capacitor-secure-storage-plugin/blob/master/android/src/main/java/com/whitestein/securestorage/PasswordStorageHelper.java#L197
Keys method return JSON Serializable Error.
Reason: Return type for KeychainWrapper.allKeys() is Set<String>
.
QuickFix: File Name: Plugin/Plugin.swift
Updated Method: (Convert Set to Array)
@objc func keys(_ call: CAPPluginCall) {
let keys = keychainwrapper.allKeys();
call.success(["values": Array(keys)])
}
Project Config
capacitor-secure-storage-plugin (0.5.0)
@capacitor/core : 2.4.5
Error Screenshot
The value being returned by SecureStoragePlugin.keys() is the string literal "[Ljava.lang.String;" instead of an array of strings. This is broken for both android and iOS. I have forked the the repo to add a simple fix where I just call Arrays.toString(keys) to get the values. This is not ideal however because this is still not a proper stringified JSON object.
Am I doing something wrong? I am on "capacitor-secure-storage-plugin": "^0.5.0", @capacitor/android": "^2.4.4", "@capacitor/core": "^2.4.4", and "@capacitor/ios": "^2.4.4".
below is a hack I am using to get the actual string values.
public JSObject _keys() {
String[] keys = this.passwordStorageHelper.keys();
JSObject ret = new JSObject();
ret.put("value", Arrays.toString(keys));
return ret;
}
Hi,
I am experiencing an error on Android. Somehow I does not allow me to access the stored key. It works fine on IOS.
Saving the key works fine (i get true on success). However, when I am trying to get the stored key I get the following error:
E/Capacitor/Plugin: error java.lang.NullPointerException: Attempt to get length of null array at java.lang.StringFactory.newStringFromBytes(StringFactory.java:249) at com.whitestein.securestorage.SecureStoragePlugin.get(SecureStoragePlugin.java:42) at java.lang.reflect.Method.invoke(Native Method) at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99) at com.getcapacitor.Bridge$2.run(Bridge.java:537) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:214) at android.os.HandlerThread.run(HandlerThread.java:67)
I am trying to save an application access_token (that is valid forever) in the secure storage.
My set javascript method is as follows:
`async setAccessToken(access_token:string){
const key = 'access_token';
const value = access_token;
try {
let success = await SecureStoragePlugin.set({ key, value })
console.log('success saving token '+JSON.stringify(success))
} catch (error) {
return error
}
}`
my get method:
`
async getAccessToken(){
try {
const key = 'access_token';
let object = await SecureStoragePlugin.get({ key });
console.log('Getting access token '+object)
return object.value
} catch (error) {
return error
}
}
`
I am doing the test in the Android emulator in Android Studio and the device has a lockScreen PIN set. Any ideas what the problem might be?
Using capacitor 1.4.0 I copied the code examples from the readme:
import 'capacitor-secure-storage-plugin';
import { Plugins } from '@capacitor/core';
const { SecureStoragePlugin } = Plugins;
[...]
const key = 'moots_token_secure';
SecureStoragePlugin.get({ key })
.then((value: string) => {
console.log(value);
})
.catch((error: any) => {
console.log('Item with specified key does not exist.');
});
But there is never any error thrown. If the value does not exist, a weird {value: "�ée"}
value is returned:
Trying to store (encryptedly) a long string (for instance a JWT token of 2075 chars).
When debugging the process, the issue lies in the "encrypt" function of PasswordStorageHelper_SDK18 class.
javax.crypto.IllegalBlockSizeException: input must be under 256 bytes
at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:299)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
at com.whitestein.securestorage.PasswordStorageHelper$PasswordStorageHelper_SDK18.encrypt(PasswordStorageHelper.java:313)
at com.whitestein.securestorage.PasswordStorageHelper$PasswordStorageHelper_SDK18.setData(PasswordStorageHelper.java:254)
at com.whitestein.securestorage.PasswordStorageHelper.setData(PasswordStorageHelper.java:70)
at com.whitestein.securestorage.SecureStoragePlugin.set(SecureStoragePlugin.java:27)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$2.run(Bridge.java:526)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.os.HandlerThread.run(HandlerThread.java:65)
A work around from the web side, could be splitting the long string into smaller one (245bytes in fact) but this should not be necessary!
Version 0.3.2 is targeted for capacitor < 2.0.0, but the package.json mentions the following dependency:
"@capacitor/core": "latest"
Shouldn't this point to 1.5.x?
Hey Martinkasa,
which package import do I need to reference SecureStoragePlugin.class? I can't find the package name.
In your provided example the import is missing, and I can't figure out what package import I need..
public class MainActivity extends BridgeActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Initializes the Bridge this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{ // Additional plugins you've installed go here // Ex: add(TotallyAwesomePlugin.class); add(SecureStoragePlugin.class); }}); } }
Hello,
Plugin cordova secure storage permit to verify that Screen lock options PIN or Password is enabled and throw "Mobile is not secure" error if it's not. In capacitor plugin I don't see any init method. Is there a way to verify that the mobile is secure thanks to this plugin ?
link :
https://support.google.com/android/answer/9079129?hl=en
https://www.npmjs.com/package/cordova-plugin-secure-storage-echo
var ss = new cordova.plugins.SecureStorage(
function() {
console.log("Success");
},
function(error) {
console.log("Error " + error);
},
"my_app"
);
sample of implementation :
public init(): Promise<any> {
return new Promise((resolve, reject) => {
this.secureStorage = new cordova.plugins.SecureStorage(
() => {
console.log('--> Secure Storage init success');
resolve();
},
(error) => {
console.log('error on init secureStorage ', error);
navigator.notification.alert(
'Please enable the screen lock on your device. This app cannot operate securely without it.',
() => {
console.log('dans l alert');
},
'Screen lock is disabled'
);
reject(error);
},
'MY_APP');
});
}
Thanks in advance for your return,
I am using capacitor 2 and plugin v0.5.1
, the documentation says
set, remove and clear return true in case of success and false in case of error
On my ios device, it does not return false, instead it throws an error if key is not found. Please update the documentation
To support electron platform and cover all desktop platforms, the keytar wrapper may be added into plugin.
Hi,
Delete the app doesn't remove the storage on the iOS app. Do you know why and how to remove that? Thanks!
wondering how to add this plugin in android studio since the class needed isnt referenced in the readme, Thank you
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.