Issue
By default, the angular-oauth2-oidc library stores tokens within session storage.
The library offers an ability to pass in your own storage provider (via the OAuthStorage class), however it expects you to be working with a storage provider that can return data synchronously.
I'm building a mobile hybrid application using Ionic and CapacitorJS, and I need to store tokens using the CapacitorJS Storage plug-in.
This CapacitorJS Storage plug-in only offers asynchronous methods for retrieving data from storage, ie:
get(options: GetOptions) => Promise<GetResult>
This doesn't play nice with the expectation of the angular-oauth2-oidc library; for example, their logic to retrieve an access token is as follows:
public getAccessToken(): string {
return this._storage ? this._storage.getItem('access_token') : null;
}
Is there any way for me to create an implementation of the OAuthStorage class which has a synchronous getItem
method, which can call the asynchronous CapacitorjS.Storage.get
method?
Or I guess more generally, is there anyway to return data from an asynchronous method to a synchronous method, when you don't have the ability to change the caller?
Solution
There's no way to wait for an asynchronous method to complete in a synchronous one. So what you're asking for isn't directly possible. Below is probably a bad solution that tries to work around this issue.
The class below will act as a synchronous wrapper for Capicitor's Storage API. To the consumer it will appear to use multiple keys, but it will create one entry in storage. This allows the underlying keys that are stored to be unknown at startup, since they are a child of the known key. Every time the data is changed, the Storage API will be updated.
export class StorageFacade {
private data: Record<string, any> = {};
constructor(private storageKey: string) { }
/** load initial data from storage. */
async init() {
const res = await Storage.get(this.storageKey);
this.data = (res) ? JSON.parse(res) : {};
}
getItem(key: string) {
return this.data[key];
}
removeItem(key: string) {
delete this.data[key];
Storage.set(this.storageKey, JSON.stringify(this.data));
}
setItem(key: string, value: any) {
this.data[key] = value;
Storage.set(this.storageKey, JSON.stringify(this.data));
}
}
There's still the issue of everything being ready before the keys are needed for authentication. You can use an APP_INITIALIZER to load the initial data on the storage so that it is ready before any authentication occurs:
async function initAuthStorage(storage: OAuthStorage) {
await this.storage.init();
}
@NgModule({
providers: [
{ provide OAuthStorage, useValue: new StorageFacade('authStorage') }
{
provide: APP_INITIALIZER,
useFactory: initAuthStorage,
deps: [OAuthStorage],
multi: true,
},
],
})
I'm sure there are ways to clean this up a bit, like only persisting when the app is being unloaded instead of after every change, but this should be a good starting point.
Answered By - Daniel Gimenez
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.