Skip to main content
Version: 4.0

Upgrading Auth Connect

note

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 4 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 4 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:

import { IonicAuth } from '@ionic-enterprise/auth';

export class MyAuthentication extends IonicAuth {
constructor() {
super({
authConfig: 'auth0',
platform: 'capacitor',
clientID: 'my-magic-id',
discoveryUrl: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.myapp://login',
logoutUrl: 'com.example.myapp://logout'
})
}

async login() {
try {
await super.login();
const accessToken = await this.getAccessToken();
const refreshToken = await this.getRefreshToken();
const idToken = await this.getIdToken();
} catch (e) {
console.error('Login Failed', e);
}
}
}

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 4 APIs:

import { Auth0Provider, AuthConnect } from '@ionic-enterprise/auth';

export class MyAuthentication {
private readonly provider = new Auth0Provider();
private readonly options: ProviderOptions = {
platform: 'capacitor',
clientID: 'my-magic-id',
discoveryUrl: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.myapp://login',
logoutUrl: 'com.example.myapp://logout'
}

async login() {
try {
const result = await AuthConnect.login(this.provider, this.options);
const { accessToken, refreshToken, idToken } = result;
} catch (e) {
console.log('Login Failed', e);
}
}
}

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 4 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 4. 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

npm 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 4 in favor of adding code to your build.gradle file.

Remove the following code from your AndroidManifest.xml file:

<intent-filter>
<data android:scheme="$AUTH_URL_SCHEME"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter>

Add the following code to your build.gradle file:

android {
...
defaultConfig {
...
manifestPlaceholders = [
'AUTH_URL_SCHEME': 'mycustomscheme' /// CHANGE THIS TO YOUR CUSTOM AUTH SCHEME
]
}
}

Cordova

ionic cordova plugin remove @ionic-enterprise/auth
ionic cordova plugin add @capacitor/plugin-cordova-compat
ionic cordova plugin add @ionic-enterprise/auth --variable AUTH_URL_SCHEME='mycustomscheme'
note

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

const providerConfig = {
authConfig: 'auth0'
}

After

const 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.

import { AuthProvider, Manifest, ProviderOptions, AuthConnectConfig, Params } from '@ionic-enterprise/auth';

export class MyCustomProvider extends AuthProvider {
async authorizeRequest(
manifest: Manifest,
options: ProviderOptions,
config: Pick<
AuthConnectConfig,
'ios' | 'android' | 'web' | 'platform' | 'logLevel'
>
) {
const url = 'https://example.com/oauth2/authorize';
const params: Params = {};
params['client_id'] = '123';

return {
url,
params
};
}
}

If you need to add some additional parameters to any of the requests, you can simply extend your existing provider to do so.

import { AuthProvider, Manifest, ProviderOptions, AuthConnectConfig, Params } from '@ionic-enterprise/auth';

export class MyCustomProvider extends Auth0Provider {
async authorizeRequest(
manifest: Manifest,
options: ProviderOptions,
config: Pick<
AuthConnectConfig,
'ios' | 'android' | 'web' | 'platform' | 'logLevel'
>
) {
const { url, params } = await super.authorizeRequest(manifest, options, config);
return {
url,
params: {
...params,
['my-custom']: 'param'
}
}
}
}

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:

import { AuthResult } from '@ionic-enterprise/auth';

export async function storeAuthResult(data: AuthResult) {
await identityVault.setValue('my-unique-key', JSON.stringify(data));
}

export async function getAuthResult(): AuthResult | null {
const data = await identityVault.getValue('my-unique-key');
if (!data) return null;
return JSON.parse(data);
}

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.

await AuthConnect.setup({
platform: 'capacitor',
logLevel: 'DEBUG',
ios: {},
android: {},
...
})

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

class MyAuth extends IonicAuth {
constructor() {
super({
...options
})
}

async login() {
await super.login();
}

async logout() {
await super.logout();
}

async getAccessToken() {
await super.getAccessToken();
}

async getRefreshToken() {
await super.getRefreshToken();
}
}

After

class MyAuth {
private provider = new Auth0Provider();
private options = {
...options
}
private result: AuthResult | null;

async login() {
this.result = await AuthConnect.login(this.provider, this.options);
}

async logout() {
if (!this.result) return;
await AuthConnect.logout(this.result);
}

// This no longer needs to be async, but you might leave it if your code
// is already expecting it, or perhaps you pull from an async storage
// mechanism if not held in memory.
async getAccessToken() {
if (!this.result) return null;
return this.result.accessToken;
}

async getRefreshToken() {
if (!this.result) return null;
return this.result.refreshToken;
}
}
note

Auth Connect 3 took a class based approach that is no longer required for Auth Connect 4. 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.

async function isAuthenticated() {
// Use your own provider here
const provider: AuthProvider = new Auth0Provider();
try {
const authResult = await identityVault.getValue('my-auth-result');
// If no id token, the login is invalid
const { idToken } = authResult;
if (!idToken) throw new Error('No ID Token');

// If token is not expired, user is currently authenticated
const expired = await AuthConnect.isAccessTokenExpired(authResult);
if (!expired) return true;

// Token is expired, so try to refresh the session, will throw if fails
const newAuthResult = await AuthConnect.refreshSession(provider, authResult);
/**
* Call your storage provider and save your new authResult object
*/
await identityVault.setValue('my-auth-result', JSON.stringify(newAuthResult));
return true;
} catch (e) {
console.error(e);
/**
* Call your storage provider and remove your authResult object
*/
await identityVault.removeValue('my-auth-result');
return false;
}
}
danger

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
/**
* This is just an example of how you might retrieve these values from your storage provider.
* You will need to implement this yourself based on your storage provider.
*/
async function getLegacySessionFromStorageProvider() {
const accessToken = await identityVault.getValue('_ionicAuth.accessToken');
const refreshToken = await identityVault.getValue('_ionicAuth.refreshToken');
const idToken = await identityVault.getValue('_ionicAuth.idToken');
return { accessToken, refreshToken, idToken };
}

async function migrateAuthConnectSession() {
const provider = new Auth0Provider();
const options: ProviderOptions = {...};
const { accessToken, refreshToken, idToken } = await getLegacySessionFromStorageProvider();
const tempAuthResult: AuthResult = await AuthConnect.buildAuthResult(provider, options, {
accessToken,
refreshToken,
idToken,
})
const newAuthResult = await AuthConnect.refreshSession(provider, tempAuthResult);
}

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.