5

I'm trying to initialize the secure storage plugin. When this fails it means the user does not have a secure lockscreen set. Using the github page i'm trying to recreate the sample provided:

var ss;
var _init = function () {
    ss = new cordova.plugins.SecureStorage(
        function () {
            console.log('OK');
        },
        function () {
            navigator.notification.alert(
                'Please enable the screen lock on your device. This app cannot operate securely without it.',
                function () {
                    ss.secureDevice(
                        function () {
                            _init();
                        },
                        function () {
                            _init();
                        }
                    );
                },
                'Screen lock is disabled'
            );
        },
        'my_app');
};
_init();

This is my attempt:

private createSecureStorage() {
    this.secureStorageAPI.create(this.storeName).then( 
        (storage: SecureStorageObject) => {
            this.secureStorage = storage;
    }).catch( 
        (error) => {
            this.dialogs.alert( 'Please enable the screen lock on your device. This app cannot operate securely without it.').then( 
                () => {
                // Alert Dismissed, should open the secure lockscreen settings here
                  this.secureStorage.secureDevice().then( 
                      () => {
                          // Try again
                          this.createSecureStorage();
                      }
                  ).catch( () => { 
                    // Try again
                    this.createSecureStorage();
                  })
               } )
      } );
  }

The problem i'm having is that when the secureStorageApi.create call fails, secureStorage will be undefined so I can't use it to call call secureDevice().

Any help would be much appreciated.

Juxture
  • 253
  • 3
  • 15
  • No one able to help with this one? I think this might be a faulty design because you need the SecureStorage object in order to call secureDevice() – Juxture Oct 03 '17 at 11:17

3 Answers3

3

For everyone that needs this to work right now modify:

node_modules\@ionic-native\secure-storage\index.js

Working code

It should look like this:

 var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Injectable } from '@angular/core';
import { CordovaInstance, Plugin, CordovaCheck, IonicNativePlugin } from '@ionic-native/core';
/**
 * @hidden
 */
var SecureStorageObject = (function () {
    function SecureStorageObject(_objectInstance) {
        this._objectInstance = _objectInstance;
    }
    /**
     * Gets a stored item
     * @param key {string}
     * @returns {Promise<string>}
     */
    SecureStorageObject.prototype.get = function (key) { return; };
    /**
     * Stores a value
     * @param key {string}
     * @param value {string}
     * @returns {Promise<any>}
     */
    SecureStorageObject.prototype.set = function (key, value) { return; };
    /**
     * Removes a single stored item
     * @param key {string}
     * @returns {Promise<string>} returns a promise that resolves with the key that was removed
     */
    SecureStorageObject.prototype.remove = function (key) { return; };
    /**
     * Get all references from the storage.
     * @returns {Promise<string[]>} returns a promise that resolves with array of keys storage
     */
    SecureStorageObject.prototype.keys = function () { return; };
    /**
     * Clear all references from the storage.
     * @returns {Promise<any>}
     */
    SecureStorageObject.prototype.clear = function () { return; };
    return SecureStorageObject;
}());
/**
 * @hidden
 */
var SecureDeviceObject = (function () {
    function SecureDeviceObject(_objectInstance) {
        this._objectInstance = _objectInstance;
    }

    /**
        * Brings up the screen-lock settings
        * @returns {Promise<any>}
        */
    SecureStorageObject.prototype.secureDevice = function () { return; };
    return SecureDeviceObject;
}());
export { SecureStorageObject, SecureDeviceObject };
__decorate([
    CordovaInstance({
        callbackOrder: 'reverse'
    }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], SecureStorageObject.prototype, "get", null);
__decorate([
    CordovaInstance({
        callbackOrder: 'reverse'
    }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", Promise)
], SecureStorageObject.prototype, "set", null);
__decorate([
    CordovaInstance({
        callbackOrder: 'reverse'
    }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], SecureStorageObject.prototype, "remove", null);
__decorate([
    CordovaInstance({
        callbackOrder: 'reverse'
    }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], SecureStorageObject.prototype, "keys", null);
__decorate([
    CordovaInstance({
        callbackOrder: 'reverse'
    }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], SecureStorageObject.prototype, "clear", null);
__decorate([
    CordovaInstance(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], SecureDeviceObject.prototype, "secureDevice", null);
/**
 * @name Secure Storage
 * @description
 * This plugin gets, sets and removes key,value pairs from a device's secure storage.
 *
 * Requires Cordova plugin: `cordova-plugin-secure-storage`. For more info, please see the [Cordova Secure Storage docs](https://github.com/Crypho/cordova-plugin-secure-storage).
 *
 * The browser platform is supported as a mock only. Key/values are stored unencrypted in localStorage.
 *
 * @usage
 *
 * ```typescript
 * import { SecureStorage, SecureStorageObject } from '@ionic-native/secure-storage';
 *
 * constructor(private secureStorage: SecureStorage) { }
 *
 * ...
 *
 * this.secureStorage.create('my_store_name')
 *   .then((storage: SecureStorageObject) => {
 *
 *      storage.get('key')
 *        .then(
 *          data => console.log(data),
 *          error => console.log(error)
 *      );
 *
 *      storage.set('key', 'value')
 *        .then(
 *         data => console.log(data),
 *          error => console.log(error)
 *      );
 *
 *      storage.remove('key')
 *      .then(
 *          data => console.log(data),
 *          error => console.log(error)
 *      );
 *
 *   });
 *
 *
 * ```
 * @classes
 * SecureStorageObject
 */
var SecureStorage = SecureStorage_1 = (function (_super) {
    __extends(SecureStorage, _super);
    function SecureStorage() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    /**
     * Creates a namespaced storage.
     * @param store {string}
     * @returns {Promise<SecureStorageObject>}
     */
    SecureStorage.prototype.create = function (store) {
        return new Promise(function (res, rej) {
            var instance = new (SecureStorage_1.getPlugin())(
                function () { 
                    res(new SecureStorageObject(instance)); 
                }, 
                function () {
                    rej(new SecureDeviceObject(instance));
                }, 
                store);
        });
    };
    return SecureStorage;
}(IonicNativePlugin));
SecureStorage.decorators = [
    { type: Injectable },
];
/** @nocollapse */
SecureStorage.ctorParameters = function () { return []; };
__decorate([
    CordovaCheck(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], SecureStorage.prototype, "create", null);
SecureStorage = SecureStorage_1 = __decorate([
    Plugin({
        pluginName: 'SecureStorage',
        plugin: 'cordova-plugin-secure-storage',
        pluginRef: 'cordova.plugins.SecureStorage',
        repo: 'https://github.com/Crypho/cordova-plugin-secure-storage',
        platforms: ['Android', 'Browser', 'iOS', 'Windows']
    })
], SecureStorage);
export { SecureStorage };
var SecureStorage_1;
//# sourceMappingURL=index.js.map

Then you'll be able to use:

 private createSecureStorage() {
        this.secureStorageAPI.create(this.storeName).then( 
            (storage: SecureStorageObject) => {
                console.log("secure");
                this.secureStorage = storage;
        }).catch( 
            (secureDeviceObject) => {
                this.dialogs.alert( 'Please enable the screen lock on your device. This app cannot operate securely without it.').then( 
                    () => {
                        // Alert Dismissed, should open the secure lockscreen settings here
                        secureDeviceObject.secureDevice().then( 
                        () => {
                            // Try again
                            console.log("Success");
                            this.createSecureStorage();
                        } ).catch( () => { 
                            // Try again
                            console.log(" Error ")
                            this.createSecureStorage();
                        })
                    } ); 
            } );
    }

What has changed?

What i've done is moving the secureDevice function to a new object called SecureDeviceObject and changed the decorators. By doing this you cannot use this object to try to call the get and set functions etc.

This is the new object:

var SecureDeviceObject = (function () {
    function SecureDeviceObject(_objectInstance) {
        this._objectInstance = _objectInstance;
    }

    /**
        * Brings up the screen-lock settings
        * @returns {Promise<any>}
        */
    SecureStorageObject.prototype.secureDevice = function () { return; };
    return SecureDeviceObject;
}());

Then i've changed the decorater:

__decorate([
    CordovaInstance(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], SecureDeviceObject.prototype, "secureDevice", null);

The last change is making the reject promise return the secureDeviceObject:

SecureStorage.prototype.create = function (store) {
        return new Promise(function (res, rej) {
            var instance = new (SecureStorage_1.getPlugin())(
                function () { 
                    res(new SecureStorageObject(instance)); 
                }, 
                function () {
                    rej(new SecureDeviceObject(instance));
                }, 
                store);
        });
    };

I suppose this is not the best possible fix, but it was the best i can do :D Tested on android 4 till 8. Working on all these!

Hope it helps someone :)

Thanks @JudgeFudge for pointing me in the right direction

Juxture
  • 253
  • 3
  • 15
  • Hey, good job, can you close this bug with your code please ? :) https://github.com/ionic-team/ionic-native/issues/1625 – Servuc Jan 26 '18 at 21:21
2

This problem is already tracked as an issue, see: https://github.com/ionic-team/ionic-native/issues/1944.

If you need a quick solution for this you can try one of the following steps:

1) Downgrade the Ionic SecureStorage plugin, maybe this issue does not occur in a previous version.

2) Try to fix the problem yourself. You can find the sources in your node_modules folder right here (if you need help with that, I can try to have to look at this later):

node_modules\cordova-plugin-secure-storage\src\android\SecureStorage.java node_modules\cordova-plugin-secure-storage\src\ios\SecureStorage.m

.

JudgeFudge
  • 1,749
  • 13
  • 18
  • Thnx! I could not find that issue. I'll do an attempt to modify the plugin myself. If i have any questions i'll let you know. – Juxture Oct 05 '17 at 07:08
  • After some research i managed to fix the issue. I'll do some extensive testing and then post the code here. – Juxture Oct 05 '17 at 08:25
1

Here i sent a pull request that fix this bug.

Here:

create(store: string): Promise<SecureStorageObject> {
    return getPromise<SecureStorageObject>((res: Function, rej: Function) => {
        const instance = new (SecureStorage.getPlugin())(
            () => res(new SecureStorageObject(instance)),
            rej,
            store
        );
    });
}

Simple change the reject callback:

() => rej(new SecureStorageObject(instance)),

Inside ionic-native/src/@ionic-native/plugins/secure-storage/index.ts

then:

npm install npm run build

and at the end copy the plugin compiled on you npm_modules folder:

cp -r ionic-native/dist/@ionic-native/plugins/secure-storage/ /your_project/node_modules/@ionic-native/ UPDATE:

They merge the PR.

Here how to use it:

{ this.storage = await this.secureStorage.create('my_storage'); } catch (e) { await e.secureDevice(); }

jedi
  • 839
  • 12
  • 33
  • 1
    ionic merged the Pull Request – jedi Mar 05 '20 at 15:08
  • The lack of documentation on @ionic-native, means that this is the best documentation there is on this: try { this.storage = await this.secureStorage.create('my_storage'); } catch (e) { await e.secureDevice(); } – zen May 08 '20 at 02:21
  • Thanks. I also made a PR (merged) with the same change for @ionic-native/plugins/secure-storage-echo – zen May 23 '20 at 22:50