Upgrading Auth Connect
Upgrading from version 2? This same guide will still apply, however some of the described APIs from version 3 may differ slightly from what you might have.
Overview
Auth Connect 5 provides an updated API that improves the flexibility with how you choose to integrate it with your application. While these changes may appear large at first, as we walk through this guide, you'll see that most API calls in your application map over exactly how you currently use Auth Connect.
Stateless Architecture
The biggest change you'll see with Auth Connect 5 is that the library has moved to a stateless architecture. This means that instead of Auth Connect managing how all your tokens and provider info are stored, it instead leaves that up to your application to determine. Let's look at a simple example of this.
In Auth Connect 3 and earlier, setting up the provider in Angular and calling login might look something similar to the following:
_25import { IonicAuth } from '@ionic-enterprise/auth';_25_25export class MyAuthentication extends IonicAuth {_25 constructor() {_25 super({_25 authConfig: 'auth0',_25 platform: 'capacitor',_25 clientID: 'my-magic-id',_25 discoveryUrl: 'https://example.com/.well-known/openid-configuration',_25 redirectUri: 'com.example.myapp://login',_25 logoutUrl: 'com.example.myapp://logout'_25 })_25 }_25_25 async login() {_25 try {_25 await super.login();_25 const accessToken = await this.getAccessToken();_25 const refreshToken = await this.getRefreshToken();_25 const idToken = await this.getIdToken();_25 } catch (e) {_25 console.error('Login Failed', e);_25 }_25 }_25}
You'll notice that calling login had no return and threw errors that you might catch if anything went wrong. If the method resolves how you would expect it to, then you make subsequent calls to get the various points of data returned by the provided. Auth Connect also handled storing these internally for you automatically. You could override this by providing a tokenStorageProvider
as part of the constructor configuration, but by default, it stored everything for you in localStorage.
The following example provides the same functionality in an Angular application, but using the new Auth Connect 5 APIs:
_21import { Auth0Provider, AuthConnect } from '@ionic-enterprise/auth';_21_21export class MyAuthentication {_21 private readonly provider = new Auth0Provider();_21 private readonly options: ProviderOptions = {_21 clientID: 'my-magic-id',_21 audience: 'https://example.com/',_21 discoveryUrl: 'https://example.com/.well-known/openid-configuration',_21 redirectUri: 'com.example.myapp://login',_21 logoutUrl: 'com.example.myapp://logout'_21 }_21_21 async login() {_21 try {_21 const result = await AuthConnect.login(this.provider, this.options);_21 const { accessToken, refreshToken, idToken } = result;_21 } catch (e) {_21 console.log('Login Failed', e);_21 }_21 }_21}
The first thing to notice is that the component that is calling into the service that provides the functionality from Auth Connect hasn't needed to change at all. All of the refactoring we've done is all done inside the service. This should make it easy to update and application that has properly abstracted their authentication into a single location.
Next, and likely most importantly, you'll notice that the call to login
now returns a result, whereas before it returned void
on success. This result object that is returned contains everything Auth Connect knows about the current authentication state. Any tokens you may have are stored here, information about your provider is stored here, expiration of your tokens, ect. Your application should store in information securely using a solution such as Identity Vault. For more information related to storing your tokens, check out securing your tokens. This result object is serializable, and should be stored and retrieved to restore a user's authentication session.
Capacitor-First Architecture
The new version of Auth Connect has been rebuilt from the ground up on Capacitor. This means for the best experience, we definitely recommend building your application with Capacitor, however, Auth Connect 5 does still maintain support for Cordova projects as well through a new required plugin for Cordova projects, @capacitor/plugin-cordova-compat
. This plugin is required for Auth Connect to work with Cordova projects, and should be installed as part of your project's setup.
Upgrade Steps
The following steps will describe generally how you would upgrade a project using Auth Connection 3 to the latest version of Auth Connect 5. While your project may differ in structure from the example here, the general principles should still apply. If you have any questions or need help migrating your project, please reach out to your support contact.
Install the latest version
Capacitor
_10npm install @ionic-enterprise/auth@latest
The previous version of Auth Connect required changes to the AndroidManifest.xml file in your Android project. This has been removed in Auth Connect 5 in favor of adding code to your build.gradle
file.
Remove the following code from your AndroidManifest.xml
file:
_11<intent-filter>_11 <data android:scheme="$AUTH_URL_SCHEME"/>_11 <action android:name="android.intent.action.VIEW"/>_11 <category android:name="android.intent.category.DEFAULT"/>_11 <category android:name="android.intent.category.BROWSABLE"/>_11</intent-filter>_11<intent-filter>_11 <action android:name="android.intent.action.SEND"/>_11 <category android:name="android.intent.category.DEFAULT"/>_11 <data android:mimeType="text/*"/>_11</intent-filter>
Add the following code to your build.gradle
file:
_10android {_10 ..._10 defaultConfig {_10 ..._10 manifestPlaceholders = [_10 'AUTH_URL_SCHEME': 'mycustomscheme' /// CHANGE THIS TO YOUR CUSTOM AUTH SCHEME_10 ]_10 }_10}
Cordova
_10ionic cordova plugin remove @ionic-enterprise/auth_10ionic cordova plugin add @capacitor/plugin-cordova-compat_10ionic cordova plugin add @ionic-enterprise/auth --variable AUTH_URL_SCHEME='mycustomscheme'
If you are currently building using Cordova, we recommend that you upgrade to Capacitor. Our Visual Studio Code extension makes the process simple! [LINK NEEDED, pending PR merge]
Configure your provider
Your provider is now represented as a class as opposed to a string like it was in version 3.
Before
_10const providerConfig = {_10 authConfig: 'auth0'_10}
After
_10const provider = new Auth0Provider();
If you're using a provider without support built into Auth Connect, it's now incredibly easy to tell Auth Connect how to integrate with them by extending the AuthProvider
class and implementing the required methods. Full documentation on how to do this can be found in the Custom Providers section of the documentation.
_21import { AuthProvider, Manifest, ProviderOptions, AuthConnectConfig, Params } from '@ionic-enterprise/auth';_21_21export class MyCustomProvider extends AuthProvider {_21 async authorizeRequest(_21 manifest: Manifest,_21 options: ProviderOptions,_21 config: Pick<_21 AuthConnectConfig,_21 'ios' | 'android' | 'web' | 'platform' | 'logLevel'_21 >_21 ) {_21 const url = 'https://example.com/oauth2/authorize';_21 const params: Params = {};_21 params['client_id'] = '123';_21_21 return {_21 url,_21 params_21 };_21 }_21}
If you need to add some additional parameters to any of the requests, you can simply extend your existing provider to do so.
_21import { AuthProvider, Manifest, ProviderOptions, AuthConnectConfig, Params } from '@ionic-enterprise/auth';_21_21export class MyCustomProvider extends Auth0Provider {_21 async authorizeRequest(_21 manifest: Manifest,_21 options: ProviderOptions,_21 config: Pick<_21 AuthConnectConfig,_21 'ios' | 'android' | 'web' | 'platform' | 'logLevel'_21 >_21 ) {_21 const { url, params } = await super.authorizeRequest(manifest, options, config);_21 return {_21 url,_21 params: {_21 ...params,_21 ['my-custom']: 'param'_21 }_21 }_21 }_21}
Configuring your token storage provider
You should decide how you plan to store your authentication result, including all your tokens, inside your application. If you were originally using the default storage mechanism with Auth Connect, you were using localStorage
. We recommend using Identity Vault to store your tokens and protect them with the biometric hardware available on phones. If you had previously configured a tokenStorageProvider
for Auth Connect, this same interface can work well for your application. Something simple might look as follows:
_11import { AuthResult } from '@ionic-enterprise/auth';_11_11export async function storeAuthResult(data: AuthResult) {_11 await identityVault.setValue('my-unique-key', JSON.stringify(data));_11}_11_11export async function getAuthResult(): AuthResult | null {_11 const data = await identityVault.getValue('my-unique-key');_11 if (!data) return null;_11 return JSON.parse(data);_11}
Initialize Auth Connect
Now that you have your provider and token storage provider configured, you can initialize Auth Connect. Auth Connect provides a static setup(options)
method that will configure the behavior of the library within your application. This method should be called before any other Auth Connect methods are called. Subsequent calls to this method are ignored.
_10await AuthConnect.setup({_10 platform: 'capacitor',_10 logLevel: 'DEBUG',_10 ios: {},_10 android: {},_10 ..._10})
Convert Auth Connect to stateless
Now we have everything in place to do the bulk of the work. In this step, you should work through your code, translating any calls to auth connect with the new updated methods. In most cases, the change should be nearly 1-to-1, with the only main difference being that the new functions accept the AuthResult
object as a parameter now.
Before
_23class MyAuth extends IonicAuth {_23 constructor() {_23 super({_23 ...options_23 })_23 }_23_23 async login() {_23 await super.login();_23 }_23_23 async logout() {_23 await super.logout();_23 }_23_23 async getAccessToken() {_23 await super.getAccessToken();_23 }_23_23 async getRefreshToken() {_23 await super.getRefreshToken();_23 }_23}
After
_29class MyAuth {_29 private provider = new Auth0Provider();_29 private options = {_29 ...options_29 }_29 private result: AuthResult | null;_29_29 async login() {_29 this.result = await AuthConnect.login(this.provider, this.options);_29 }_29_29 async logout() {_29 if (!this.result) return;_29 await AuthConnect.logout(this.provider, this.result);_29 }_29_29 // This no longer needs to be async, but you might leave it if your code_29 // is already expecting it, or perhaps you pull from an async storage_29 // mechanism if not held in memory._29 async getAccessToken() {_29 if (!this.result) return null;_29 return this.result.accessToken;_29 }_29_29 async getRefreshToken() {_29 if (!this.result) return null;_29 return this.result.refreshToken;_29 }_29}
Auth Connect 3 took a class based approach that is no longer required for Auth Connect 5. Working in fameworks such as React you may find that a larger refactor away from the class based structure can significantly improve your code. If you're interested in learning more about this, check out the Getting Started guides for your framework for choice, such as for React.
For refernce on any of the additional methods that Auth Connect provides, see the reference document that describes all the methods available on the class.
Handling IsAuthenticated checks
You may have noticed at this point, that one method in particular that is missing in the new version of Auth Connect, isAuthenticated()
. In previous versions, Auth Connect shipped with this method that provided a convenient default behavior for checking if a user was authenticated. However, this previous method only covered the "happy path", meaning that it didn't handle edge cases well, such as a no network situation. This often caught teams using Auth Connect off guard and wasn't something that would be noticed until an app was already deployed in production where users often find themselves outside the happy path.
Moving forward, we very highly recommend teams use the various other methods provided by Auth Connect to build out their own logic of what it means for a user "to be authenticated" in your system, as this definition can vary wildly. We would recommend reading through the isAuthenticated
guide documentation to learn more about the considerations involved when building out this check. If you want to continue to use the old logic, the snippet below should replicate the old functionality.
_29async function isAuthenticated() {_29 // Use your own provider here_29 const provider: AuthProvider = new Auth0Provider();_29 try {_29 const authResult = await identityVault.getValue('my-auth-result');_29 // If no id token, the login is invalid_29 const { idToken } = authResult;_29 if (!idToken) throw new Error('No ID Token');_29_29 // If token is not expired, user is currently authenticated_29 const expired = await AuthConnect.isAccessTokenExpired(authResult);_29 if (!expired) return true;_29_29 // Token is expired, so try to refresh the session, will throw if fails_29 const newAuthResult = await AuthConnect.refreshSession(provider, authResult);_29 /**_29 * Call your storage provider and save your new authResult object_29 */_29 await identityVault.setValue('my-auth-result', JSON.stringify(newAuthResult));_29 return true;_29 } catch (e) {_29 console.error(e);_29 /**_29 * Call your storage provider and remove your authResult object_29 */_29 await identityVault.removeValue('my-auth-result');_29 return false;_29 }_29}
This implementation assumes that each time you need your AuthResult you read it from your storage provider. If you cache this value in memory, your implementation should be updated to reflect that.
Migrating a user session from version 3 to 4
If you are okay with your users having to re-authenticate, you can skip this section. However, if you want to migrate your users to the new version of Auth Connect, you will need to migrate their old Auth Connect session data to the new format. This can be done by manually retrieving your existing session data from your storage provider and then refreshing the session with the new version of Auth Connect. This will result in a new AuthResult object that you can then save to your storage provider.
Previously, Auth Connect 3 would have stored session information under the following keys:
_ionicAuth.accessToken
_ionicAuth.refreshToken
_ionicAuth.idToken
_22/**_22 * This is just an example of how you might retrieve these values from your storage provider._22 * You will need to implement this yourself based on your storage provider._22 */_22async function getLegacySessionFromStorageProvider() {_22 const accessToken = await identityVault.getValue('_ionicAuth.accessToken');_22 const refreshToken = await identityVault.getValue('_ionicAuth.refreshToken');_22 const idToken = await identityVault.getValue('_ionicAuth.idToken');_22 return { accessToken, refreshToken, idToken };_22}_22_22async function migrateAuthConnectSession() {_22 const provider = new Auth0Provider();_22 const options: ProviderOptions = {...};_22 const { accessToken, refreshToken, idToken } = await getLegacySessionFromStorageProvider();_22 const tempAuthResult: AuthResult = await AuthConnect.buildAuthResult(provider, options, {_22 accessToken,_22 refreshToken,_22 idToken,_22 })_22 const newAuthResult = await AuthConnect.refreshSession(provider, tempAuthResult);_22}
Need Help Upgrading?
If you need help upgrading your application to the new version of Auth Connect, please reach out to Ionic Enterprise Support with any questions you may have.