Skip to main content

Getting Started with Auth Connect in @ionic-react

Learn how to get started with Auth Connect by creating a sample project that uses @ionic/react.

This tutorial starts with installing and configuring Auth Connect and moves onto achieving common use-cases: performing login/logout, checking the user's authentication status, obtaining tokens from Auth Connect, and storing tokens using Identity Vault.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_117
import { AuthConnect, ProviderOptions, Auth0Provider, TokenType } from "@ionic-enterprise/auth";
_117
import { isPlatform } from '@ionic/react';
_117
import { PropsWithChildren, createContext, useState, useEffect, useContext } from 'react';
_117
import { SessionVaultContext } from './SessionVaultProvider';
_117
_117
const isNative = isPlatform('hybrid');
_117
_117
const options: ProviderOptions = {
_117
audience: 'https://io.ionic.demo.ac',
_117
clientId: 'yLasZNUGkZ19DGEjTmAITBfGXzqbvd00',
_117
discoveryUrl: 'https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration',
_117
logoutUrl: isNative ? 'msauth://login' : 'http://localhost:8100/login',
_117
redirectUri: isNative ? 'msauth://login' : 'http://localhost:8100/login',
_117
scope: 'openid offline_access email picture profile',
_117
};
_117
_117
const setupAuthConnect = async (): Promise<void> => {
_117
return AuthConnect.setup({
_117
platform: isNative ? 'capacitor' : 'web',
_117
logLevel: 'DEBUG',
_117
ios: { webView: 'private' },
_117
web: { uiMode: 'popup', authFlow: 'implicit' },
_117
});
_117
};
_117
_117
const provider = new Auth0Provider();
_117
_117
export const AuthContext = createContext<{
_117
isAuthenticated: boolean;
_117
getAccessToken: () => Promise<string | undefined>;
_117
getUserName: () => Promise<string | undefined>;
_117
login: () => Promise<void>;
_117
logout: () => Promise<void>;
_117
}>({
_117
isAuthenticated: false,
_117
getAccessToken: () => { throw new Error('Method not implemented.'); },
_117
getUserName: () => { throw new Error('Method not implemented.'); },
_117
login: () => { throw new Error('Method not implemented.'); },
_117
logout: () => { throw new Error('Method not implemented.'); },
_117
});
_117
_117
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_117
const [isSetup, setIsSetup] = useState<boolean>(false);
_117
const { clearSession, getSession, setSession } = useContext(SessionVaultContext);
_117
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
_117
_117
const saveAuthResult = async (authResult: AuthResult | null): Promise<void> => {
_117
if (authResult) {
_117
await setSession(authResult);
_117
setIsAuthenticated(true);
_117
} else {
_117
await clearSession();
_117
setIsAuthenticated(false);
_117
}
_117
};
_117
_117
const refreshAuth = async (authResult: AuthResult): Promise<AuthResult | null> => {
_117
let newAuthResult: AuthResult | null = null;
_117
_117
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_117
try {
_117
newAuthResult = await AuthConnect.refreshSession(provider, authResult);
_117
} catch (err) {
_117
console.log('Error refreshing session.', err);
_117
}
_117
}
_117
_117
return newAuthResult;
_117
};
_117
_117
const getAuthResult = async (): Promise<AuthResult | null> => {
_117
let authResult = await getSession();
_117
_117
if (authResult && (await AuthConnect.isAccessTokenExpired(authResult))) {
_117
const newAuthResult = await refreshAuth(authResult);
_117
await saveAuthResult(newAuthResult);
_117
}
_117
setIsAuthenticated(!!authResult);
_117
return authResult;
_117
};
_117
_117
useEffect(() => {
_117
setupAuthConnect().then(() => setIsSetup(true));
_117
}, []);
_117
_117
const getAccessToken = async (): Promise<string | undefined> => {
_117
const res = await getAuthResult();
_117
return res?.accessToken;
_117
};
_117
_117
const getUserName = async (): Promise<string | undefined> => {
_117
const res = await getAuthResult();
_117
if (res) {
_117
const data = await AuthConnect.decodeToken<{ name: string }>(TokenType.id, res);
_117
return data?.name;
_117
}
_117
};
_117
_117
const login = async (): Promise<void> => {
_117
const authResult = await AuthConnect.login(provider, options);
_117
await saveAuthResult(authResult);
_117
};
_117
_117
const logout = async (): Promise<void> => {
_117
const authResult = await getAuthResult();
_117
if (authResult) {
_117
await AuthConnect.logout(provider, authResult);
_117
await saveAuthResult(null);
_117
}
_117
};
_117
_117
return (
_117
<AuthContext.Provider value={{ isAuthenticated, getAccessToken, getUserName, login, logout }}>
_117
{isSetup && children}
_117
</AuthContext.Provider>
_117
);
_117
};

Generate the Application

Start by creating an Ionic React Tabbed Layout starter.

Terminal

ionic start getting-started-ac-react-tabs --type=react

Change the app identifier

Ionic recommends changing the app identifier before adding any deployment platforms. If you want to change the app identifier after adding iOS or Android, you will have to update native artifacts.

capacitor.config.ts

_10
import { CapacitorConfig } from "@capacitor/cli";
_10
appId: "io.ionic.gettingstartedacreact",

Remove React Strict Mode

Strict mode triggers useEffect twice in development, remove it for this tutorial.

index.tsx

_19
import React from "react";
_19
root.render(<App />);

Add the native platforms

Build the application then install and create the iOS and Android platforms.

Terminal

npm run build
ionic cap add android
ionic cap add ios

Modify the build script

Modify the build npm script to sync Capacitor with each build.

package.json

_24
{
_24
"build": "react-scripts build && cap sync",

Install Auth Connect

In order to install Auth Connect, you will need to use ionic enterprise register to register your product key. This will create a .npmrc file containing the product key.

If you have already performed that step for your production application, you can just copy the .npmrc file from your production project. Since this application is for learning purposes only, you don't need to obtain another key.

Terminal

npm install @ionic-enterprise/auth

Configure Auth Connect

Auth Connect functionality will be exposed through a React Context.

Create AuthProvider.tsx within a folder named src/providers.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_20
import { ProviderOptions } from "@ionic-enterprise/auth";
_20
import { isPlatform } from "@ionic/react";
_20
import { PropsWithChildren, createContext } from "react";
_20
_20
const isNative = isPlatform("hybrid");
_20
_20
const options: ProviderOptions = {
_20
clientId: "",
_20
discoveryUrl: "",
_20
scope: "openid offline_access",
_20
audience: "",
_20
redirectUri: isNative ? "" : "",
_20
logoutUrl: isNative ? "" : "",
_20
};
_20
_20
export const AuthContext = createContext<{}>({});
_20
_20
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_20
return <AuthContext.Provider value={{}}>{children}</AuthContext.Provider>;
_20
};

Don't forget to wrap IonReactRouter with the new provider so the Context can be shared with the Tabs.

src/App.tsx

_78
import { Redirect, Route } from "react-router-dom";
_78
import {
_78
</IonReactRouter>

Auth Connect options

The options object gets passed to the login() function to help establish the authentication session.

Obtaining this information likely takes coordination with whomever administers the backend services.

You can use your own configuration for this step; however, we suggest starting with our configuration, get the application working, and then try your own configuration after that.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_21
import { ProviderOptions } from "@ionic-enterprise/auth";
_21
import { isPlatform } from "@ionic/react";
_21
import { PropsWithChildren, createContext } from "react";
_21
_21
const isNative = isPlatform("hybrid");
_21
_21
const options: ProviderOptions = {
_21
audience: "https://io.ionic.demo.ac",
_21
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_21
discoveryUrl:
_21
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_21
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_21
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_21
scope: "openid offline_access email picture profile",
_21
};
_21
_21
export const AuthContext = createContext<{}>({});
_21
_21
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_21
return <AuthContext.Provider value={{}}>{children}</AuthContext.Provider>;
_21
};

Initialization

Auth Connect needs to be initialized before any functions can be used.

A flag in AuthProvider will prevent components within IonReactRouter from rendering until Auth Connect has initialized.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_40
import { AuthConnect, ProviderOptions } from "@ionic-enterprise/auth";
_40
import { isPlatform } from "@ionic/react";
_40
import { PropsWithChildren, createContext } from "react";
_40
_40
const isNative = isPlatform("hybrid");
_40
_40
const options: ProviderOptions = {
_40
audience: "https://io.ionic.demo.ac",
_40
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_40
discoveryUrl:
_40
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_40
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_40
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_40
scope: "openid offline_access email picture profile",
_40
};
_40
_40
const setupAuthConnect = async (): Promise<void> => {
_40
return AuthConnect.setup({
_40
platform: isNative ? "capacitor" : "web",
_40
logLevel: "DEBUG",
_40
ios: { webView: "private" },
_40
web: { uiMode: "popup", authFlow: "implicit" },
_40
});
_40
};
_40
_40
export const AuthContext = createContext<{}>({});
_40
_40
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_40
const [isSetup, setIsSetup] = useState<boolean>(false);
_40
_40
useEffect(() => {
_40
setupAuthConnect().then(() => setIsSetup(true));
_40
}, []);
_40
_40
return (
_40
<AuthContext.Provider value={{}}>
_40
{isSetup && children}
_40
</AuthContext.Provider>
_40
);
_40
};

The Provider

A provider object specifying details on communicating with the OIDC service is required.

Auth Connect offers several common providers out of the box: Auth0Provider, AzureProvider, CognitoProvider, OktaProvider, and OneLoginProvider. You can also create your own provider, though doing so is beyond the scope of this tutorial.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_42
import { AuthConnect, ProviderOptions, Auth0Provider } from "@ionic-enterprise/auth";
_42
import { isPlatform } from "@ionic/react";
_42
import { PropsWithChildren, createContext } from "react";
_42
_42
const isNative = isPlatform("hybrid");
_42
_42
const options: ProviderOptions = {
_42
audience: "https://io.ionic.demo.ac",
_42
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_42
discoveryUrl:
_42
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_42
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_42
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_42
scope: "openid offline_access email picture profile",
_42
};
_42
_42
const setupAuthConnect = async (): Promise<void> => {
_42
return AuthConnect.setup({
_42
platform: isNative ? "capacitor" : "web",
_42
logLevel: "DEBUG",
_42
ios: { webView: "private" },
_42
web: { uiMode: "popup", authFlow: "implicit" },
_42
});
_42
};
_42
_42
export const provider = new Auth0Provider();
_42
_42
export const AuthContext = createContext<{}>({});
_42
_42
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_42
const [isSetup, setIsSetup] = useState<boolean>(false);
_42
_42
useEffect(() => {
_42
setupAuthConnect().then(() => setIsSetup(true));
_42
}, []);
_42
_42
return (
_42
<AuthContext.Provider value={{}}>
_42
{isSetup && children}
_42
</AuthContext.Provider>
_42
);
_42
};

Login and Logout

Login and logout are the two most fundamental operations in the authentication flow.

Login requires both the provider and options established above. Login resolves an AuthResult if the operation succeeds. The AuthResult contains auth tokens as well as some other information. This object needs to be passed to almost all other AuthConnect functions; as such, it needs to be saved. The login() call rejects with an error if the user cancels the login or if something else prevents the login to complete.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_52
import { AuthConnect, ProviderOptions, Auth0Provider, AuthResult } from "@ionic-enterprise/auth";
_52
import { isPlatform } from "@ionic/react";
_52
import { PropsWithChildren, createContext } from "react";
_52
_52
const isNative = isPlatform("hybrid");
_52
_52
const options: ProviderOptions = {
_52
audience: "https://io.ionic.demo.ac",
_52
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_52
discoveryUrl:
_52
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_52
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_52
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_52
scope: "openid offline_access email picture profile",
_52
};
_52
_52
const setupAuthConnect = async (): Promise<void> => {
_52
return AuthConnect.setup({
_52
platform: isNative ? "capacitor" : "web",
_52
logLevel: "DEBUG",
_52
ios: { webView: "private" },
_52
web: { uiMode: "popup", authFlow: "implicit" },
_52
});
_52
};
_52
_52
export const provider = new Auth0Provider();
_52
_52
export const AuthContext = createContext<{
_52
login: () => Promise<void>;
_52
}>({
_52
login: () => { throw new Error("Method not implemented."); }
_52
});
_52
_52
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_52
const [isSetup, setIsSetup] = useState<boolean>(false);
_52
const [authResult, setAuthResult] = useState<AuthResult | null>(null);
_52
_52
useEffect(() => {
_52
setupAuthConnect().then(() => setIsSetup(true));
_52
}, []);
_52
_52
const login = async (): Promise<void> => {
_52
const authResult = await AuthConnect.login(provider, options);
_52
setAuthResult(authResult);
_52
};
_52
_52
return (
_52
<AuthContext.Provider value={{ login }}>
_52
{isSetup && children}
_52
</AuthContext.Provider>
_52
);
_52
};

Logout requires the provider as well as the AuthResult object returned by the login() call.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_61
import { AuthConnect, ProviderOptions, Auth0Provider, AuthResult } from "@ionic-enterprise/auth";
_61
import { isPlatform } from "@ionic/react";
_61
import { PropsWithChildren, createContext } from "react";
_61
_61
const isNative = isPlatform("hybrid");
_61
_61
const options: ProviderOptions = {
_61
audience: "https://io.ionic.demo.ac",
_61
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_61
discoveryUrl:
_61
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_61
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_61
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_61
scope: "openid offline_access email picture profile",
_61
};
_61
_61
const setupAuthConnect = async (): Promise<void> => {
_61
return AuthConnect.setup({
_61
platform: isNative ? "capacitor" : "web",
_61
logLevel: "DEBUG",
_61
ios: { webView: "private" },
_61
web: { uiMode: "popup", authFlow: "implicit" },
_61
});
_61
};
_61
_61
export const provider = new Auth0Provider();
_61
_61
export const AuthContext = createContext<{
_61
login: () => Promise<void>;
_61
logout: () => Promise<void>;
_61
}>({
_61
login: () => { throw new Error("Method not implemented."); },
_61
logout: () => { throw new Error("Method not implemented."); }
_61
});
_61
_61
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_61
const [isSetup, setIsSetup] = useState<boolean>(false);
_61
const [authResult, setAuthResult] = useState<AuthResult | null>(null);
_61
_61
useEffect(() => {
_61
setupAuthConnect().then(() => setIsSetup(true));
_61
}, []);
_61
_61
const login = async (): Promise<void> => {
_61
const authResult = await AuthConnect.login(provider, options);
_61
setAuthResult(authResult);
_61
};
_61
_61
const logout = async (): Promise<void> => {
_61
if (authResult) {
_61
await AuthConnect.logout(provider, authResult);
_61
setAuthResult(null);
_61
}
_61
};
_61
_61
return (
_61
<AuthContext.Provider value={{ login, logout }}>
_61
{isSetup && children}
_61
</AuthContext.Provider>
_61
);
_61
};

To test these new functions, replace ExploreContainer with "Login" and "Logout" buttons in src/pages/Tab1.tsx.

Using Ionic's Auth0 provider, functionality can be tested with the following credentials:

  • Email Address: test@ionic.io
  • Password: Ion54321
AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_29
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
_29
import ExploreContainer from '../components/ExploreContainer';
_29
import { AuthContext } from '../providers/AuthProvider';
_29
import './Tab1.css';
_29
_29
const Tab1: React.FC = () => {
_29
const { login, logout } = useContext(AuthContext);
_29
_29
return (
_29
<IonPage>
_29
<IonHeader>
_29
<IonToolbar>
_29
<IonTitle>Tab 1</IonTitle>
_29
</IonToolbar>
_29
</IonHeader>
_29
<IonContent fullscreen>
_29
<IonHeader collapse="condense">
_29
<IonToolbar>
_29
<IonTitle size="large">Tab 1</IonTitle>
_29
</IonToolbar>
_29
</IonHeader>
_29
<IonButton onClick={login}>Login</IonButton>
_29
<IonButton onClick={logout}>Logout</IonButton>
_29
</IonContent>
_29
</IonPage>
_29
);
_29
};
_29
_29
export default Tab1;

Configure the native projects

Login will fail when testing on a native device.

This occurs because the native device doesn't know which application(s) handle navigation to the msauth:// scheme (using Ionic's Auth0 provider). To register the application to handle the scheme, modify the build.gradle and Info.plist files as noted here.

Replace $AUTH_URL_SCHEME with msauth when using Ionic's Auth0 provider.

Determining the current auth status

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_71
import { AuthConnect, ProviderOptions, Auth0Provider, AuthResult } from "@ionic-enterprise/auth";
_71
import { isPlatform } from "@ionic/react";
_71
import { PropsWithChildren, createContext } from "react";
_71
_71
const isNative = isPlatform("hybrid");
_71
_71
const options: ProviderOptions = {
_71
audience: "https://io.ionic.demo.ac",
_71
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_71
discoveryUrl:
_71
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_71
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_71
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_71
scope: "openid offline_access email picture profile",
_71
};
_71
_71
const setupAuthConnect = async (): Promise<void> => {
_71
return AuthConnect.setup({
_71
platform: isNative ? "capacitor" : "web",
_71
logLevel: "DEBUG",
_71
ios: { webView: "private" },
_71
web: { uiMode: "popup", authFlow: "implicit" },
_71
});
_71
};
_71
_71
export const provider = new Auth0Provider();
_71
_71
export const AuthContext = createContext<{
_71
isAuthenticated: boolean;
_71
login: () => Promise<void>;
_71
logout: () => Promise<void>;
_71
}>({
_71
isAuthenticated: false,
_71
login: () => { throw new Error("Method not implemented."); },
_71
logout: () => { throw new Error("Method not implemented."); }
_71
});
_71
_71
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_71
const [isSetup, setIsSetup] = useState<boolean>(false);
_71
const [authResult, setAuthResult] = useState<AuthResult | null>(null);
_71
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
_71
_71
const getAuthResult = async (): Promise<AuthResult | null> => {
_71
setIsAuthenticated(!!authResult);
_71
return authResult;
_71
};
_71
_71
useEffect(() => {
_71
setupAuthConnect().then(() => setIsSetup(true));
_71
}, []);
_71
_71
const login = async (): Promise<void> => {
_71
const authResult = await AuthConnect.login(provider, options);
_71
setAuthResult(authResult);
_71
setIsAuthenticated(true);
_71
};
_71
_71
const logout = async (): Promise<void> => {
_71
if (authResult) {
_71
await AuthConnect.logout(provider, authResult);
_71
setAuthResult(null);
_71
setIsAuthenticated(false);
_71
}
_71
};
_71
_71
return (
_71
<AuthContext.Provider value={{ isAuthenticated, login, logout }}>
_71
{isSetup && children}
_71
</AuthContext.Provider>
_71
);
_71
};

Users are shown both the login and logout buttons but you don't really know if the user is logged in or not. Let's change that.

A simple strategy to use is tracking the status via state, updating the value after calling certain AuthConnect methods.

Ignore the extra complexity with the getAuthResult() function -- we will expand on that as we go.

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_44
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
_44
import ExploreContainer from '../components/ExploreContainer';
_44
import { AuthContext } from '../providers/AuthProvider';
_44
import './Tab1.css';
_44
_44
const Tab1: React.FC = () => {
_44
const { isAuthenticated, login, logout } = useContext(AuthContext);
_44
_44
const handleLogin = async () => {
_44
try {
_44
await login();
_44
} catch (err) {
_44
console.log("Error logging in:", err);
_44
}
_44
};
_44
_44
const handleLogout = async () => {
_44
await logout();
_44
};
_44
_44
return (
_44
<IonPage>
_44
<IonHeader>
_44
<IonToolbar>
_44
<IonTitle>Tab 1</IonTitle>
_44
</IonToolbar>
_44
</IonHeader>
_44
<IonContent fullscreen>
_44
<IonHeader collapse="condense">
_44
<IonToolbar>
_44
<IonTitle size="large">Tab 1</IonTitle>
_44
</IonToolbar>
_44
</IonHeader>
_44
{!isAuthenticated ? (
_44
<IonButton onClick={handleLogin}>Login</IonButton>
_44
) : (
_44
<IonButton onClick={handleLogout}>Logout</IonButton>
_44
)}
_44
</IonContent>
_44
</IonPage>
_44
);
_44
};
_44
_44
export default Tab1;

Use isAuthenticated to determine which button to display, depending on the current auth status.

Notice the try...catch in the handleLogin() method. Production apps should have some kind of handling here, but this tutorial will only log the fact.

At this point, you should see the "Login" button if you are not logged in and the "Logout" button if you are.

Get the tokens

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_93
import { AuthConnect, ProviderOptions, Auth0Provider, AuthResult, TokenType } from "@ionic-enterprise/auth";
_93
import { isPlatform } from "@ionic/react";
_93
import { PropsWithChildren, createContext } from "react";
_93
_93
const isNative = isPlatform("hybrid");
_93
_93
const options: ProviderOptions = {
_93
audience: "https://io.ionic.demo.ac",
_93
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_93
discoveryUrl:
_93
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_93
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_93
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_93
scope: "openid offline_access email picture profile",
_93
};
_93
_93
const setupAuthConnect = async (): Promise<void> => {
_93
return AuthConnect.setup({
_93
platform: isNative ? "capacitor" : "web",
_93
logLevel: "DEBUG",
_93
ios: { webView: "private" },
_93
web: { uiMode: "popup", authFlow: "implicit" },
_93
});
_93
};
_93
_93
export const provider = new Auth0Provider();
_93
_93
export const AuthContext = createContext<{
_93
isAuthenticated: boolean;
_93
getAccessToken: () => Promise<string | undefined>;
_93
getUserName: () => Promise<string | undefined>;
_93
login: () => Promise<void>;
_93
logout: () => Promise<void>;
_93
}>({
_93
isAuthenticated: false,
_93
getAccessToken: () => { throw new Error("Method not implemented."); },
_93
getUserName: () => { throw new Error("Method not implemented."); },
_93
login: () => { throw new Error("Method not implemented."); },
_93
logout: () => { throw new Error("Method not implemented."); }
_93
});
_93
_93
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_93
const [isSetup, setIsSetup] = useState<boolean>(false);
_93
const [authResult, setAuthResult] = useState<AuthResult | null>(null);
_93
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
_93
_93
const getAuthResult = async (): Promise<AuthResult | null> => {
_93
setIsAuthenticated(!!authResult);
_93
return authResult;
_93
};
_93
_93
useEffect(() => {
_93
setupAuthConnect().then(() => setIsSetup(true));
_93
}, []);
_93
_93
const login = async (): Promise<void> => {
_93
const authResult = await AuthConnect.login(provider, options);
_93
setAuthResult(authResult);
_93
setIsAuthenticated(true);
_93
};
_93
_93
const logout = async (): Promise<void> => {
_93
if (authResult) {
_93
await AuthConnect.logout(provider, authResult);
_93
setAuthResult(null);
_93
setIsAuthenticated(false);
_93
}
_93
};
_93
_93
const getAccessToken = async (): Promise<string | undefined> => {
_93
const res = await getAuthResult();
_93
return res?.accessToken;
_93
};
_93
_93
const getUserName = async (): Promise<string | undefined> => {
_93
const res = await getAuthResult();
_93
if (res) {
_93
const data = await AuthConnect.decodeToken<{ name: string }>(
_93
TokenType.id,
_93
res
_93
);
_93
return data?.name;
_93
}
_93
};
_93
_93
return (
_93
<AuthContext.Provider value={{
_93
isAuthenticated, getAccessToken, getUserName, login, logout
_93
}}>
_93
{isSetup && children}
_93
</AuthContext.Provider>
_93
);
_93
};

We can now log in and out, but what about getting the tokens that the OIDC provider gave us? That information is stored as part of the AuthResult. Auth Connect provides methods to allow us to easily look at the contents of the tokens.

Note: the format and data stored in the ID token may change based on your provider and configuration. Check the documentation and configuration of your own provider for details.

These methods can be used wherever a specific token is required. For example, if you are accessing a backend API that requires you to include a bearer token, you can use getAccessToken() to create an HTTP interceptor that adds the token to HTTP requests.

An interceptor isn't needed for this app, but as a challenge to you, update Tab1.tsx to show the user's name when they are logged in. You could also display the access token if you want (though you'd never do that in a real app).

Refreshing the authentication session

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_111
import { AuthConnect, ProviderOptions, Auth0Provider } from "@ionic-enterprise/auth";
_111
import { isPlatform } from "@ionic/react";
_111
import { PropsWithChildren, createContext } from "react";
_111
_111
const isNative = isPlatform("hybrid");
_111
_111
const options: ProviderOptions = {
_111
audience: "https://io.ionic.demo.ac",
_111
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_111
discoveryUrl:
_111
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_111
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_111
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_111
scope: "openid offline_access email picture profile",
_111
};
_111
_111
const setupAuthConnect = async (): Promise<void> => {
_111
return AuthConnect.setup({
_111
platform: isNative ? "capacitor" : "web",
_111
logLevel: "DEBUG",
_111
ios: { webView: "private" },
_111
web: { uiMode: "popup", authFlow: "implicit" },
_111
});
_111
};
_111
_111
export const provider = new Auth0Provider();
_111
_111
export const AuthContext = createContext<{
_111
isAuthenticated: boolean;
_111
getAccessToken: () => Promise<string | undefined>;
_111
getUserName: () => Promise<string | undefined>;
_111
login: () => Promise<void>;
_111
logout: () => Promise<void>;
_111
}>({
_111
isAuthenticated: false,
_111
getAccessToken: () => { throw new Error("Method not implemented."); },
_111
getUserName: () => { throw new Error("Method not implemented."); },
_111
login: () => { throw new Error("Method not implemented."); },
_111
logout: () => { throw new Error("Method not implemented."); }
_111
});
_111
_111
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_111
const [isSetup, setIsSetup] = useState<boolean>(false);
_111
const [authResult, setAuthResult] = useState<AuthResult | null>(null);
_111
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
_111
_111
const refreshAuth = async (authResult: AuthResult): Promise<AuthResult | null> => {
_111
let newAuthResult: AuthResult | null = null;
_111
_111
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_111
try {
_111
newAuthResult = await AuthConnect.refreshSession(provider, authResult);
_111
} catch (err) {
_111
console.log("Error refreshing session.", err);
_111
}
_111
}
_111
_111
return newAuthResult;
_111
};
_111
_111
const getAuthResult = async (): Promise<AuthResult | null> => {
_111
if (authResult && (await AuthConnect.isAccessTokenExpired(authResult))) {
_111
const newAuthResult = await refreshAuth(authResult);
_111
setAuthResult(newAuthResult);
_111
}
_111
setIsAuthenticated(!!authResult);
_111
return authResult;
_111
};
_111
_111
useEffect(() => {
_111
setupAuthConnect().then(() => setIsSetup(true));
_111
}, []);
_111
_111
const login = async (): Promise<void> => {
_111
const authResult = await AuthConnect.login(provider, options);
_111
setAuthResult(authResult);
_111
setIsAuthenticated(true);
_111
};
_111
_111
const logout = async (): Promise<void> => {
_111
if (authResult) {
_111
await AuthConnect.logout(provider, authResult);
_111
setAuthResult(null);
_111
setIsAuthenticated(false);
_111
}
_111
};
_111
_111
const getAccessToken = async (): Promise<string | undefined> => {
_111
const res = await getAuthResult();
_111
return res?.accessToken;
_111
};
_111
_111
const getUserName = async (): Promise<string | undefined> => {
_111
const res = await getAuthResult();
_111
if (res) {
_111
const data = await AuthConnect.decodeToken<{ name: string }>(
_111
TokenType.id,
_111
res
_111
);
_111
return data?.name;
_111
}
_111
};
_111
_111
return (
_111
<AuthContext.Provider value={{
_111
isAuthenticated, getAccessToken, getUserName, login, logout
_111
}}>
_111
{isSetup && children}
_111
</AuthContext.Provider>
_111
);
_111
};

In a typical OIDC implementation, access tokens are very short lived. It is common to use a longer lived refresh token to obtain a new AuthResult.

Add a function that performs a refresh, then modify getAuthResult() to refresh the session when needed. Now anything using getAuthResult() to get the current auth result will automatically trigger a refresh if needed.

Store the Auth Result

So far the AuthResult has been stored in a local state variable, which has a couple of disadvantages:

  • Tokens could show up in a stack trace.
  • Tokens do not survive a browser refresh or application restart.

There are several options we could use to store the AuthResult, but one that handles persistence as well as storing the data in a secure location on native devices is Ionic Identity Vault.

For our application, we will install Identity Vault and use it in "Secure Storage" mode to store the tokens.

Terminal

npm install @ionic-enterprise/identity-vault

Create a Vault factory

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_10
import { BrowserVault, IdentityVaultConfig, Vault } from "@ionic-enterprise/identity-vault";
_10
import { isPlatform } from "@ionic/react";
_10
_10
const createVault = (config: IdentityVaultConfig): Vault | BrowserVault => {
_10
return isPlatform("hybrid") ? new Vault(config) : new BrowserVault(config);
_10
};

Create SessionVaultProvider.tsx within src/providers. In this file setup a factory that builds either the actual Vault - if we are on a device - or a browser-based "Vault" suitable for development in the browser.

This provides us with a secure Vault on device, or a fallback Vault that allows us to keep using our browser-based development flow.

Set up a React context

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_53
import { BrowserVault, IdentityVaultConfig, Vault } from "@ionic-enterprise/identity-vault";
_53
import { isPlatform } from "@ionic/react";
_53
_53
const createVault = (config: IdentityVaultConfig): Vault | BrowserVault => {
_53
return isPlatform("hybrid") ? new Vault(config) : new BrowserVault(config);
_53
};
_53
_53
const key = 'auth-result';
_53
const vault = createVault({
_53
key: 'io.ionic.gettingstartedacreact',
_53
type: VaultType.SecureStorage,
_53
deviceSecurityType: DeviceSecurityType.None,
_53
lockAfterBackgrounded: 5000,
_53
shouldClearVaultAfterTooManyFailedAttempts: true,
_53
customPasscodeInvalidUnlockAttempts: 2,
_53
unlockVaultOnLoad: false,
_53
});
_53
_53
export const SessionVaultContext = createContext<{
_53
clearSession: () => Promise<void>;
_53
getSession: () => Promise<AuthResult | null>;
_53
setSession: (value?: AuthResult) => Promise<void>;
_53
}>({
_53
clearSession: () => {
_53
throw new Error('Method not implemented.');
_53
},
_53
getSession: () => {
_53
throw new Error('Method not implemented.');
_53
},
_53
setSession: () => {
_53
throw new Error('Method not implemented.');
_53
},
_53
});
_53
_53
export const SessionVaultProvider: React.FC<PropsWithChildren> = ({ children }) => {
_53
const clearSession = (): Promise<void> => {
_53
return vault.clear();
_53
};
_53
_53
const getSession = (): Promise<AuthResult | null> => {
_53
return vault.getValue<AuthResult>(key);
_53
};
_53
_53
const setSession = (value?: AuthResult): Promise<void> => {
_53
return vault.setValue(key, value);
_53
};
_53
_53
return (
_53
<SessionVaultContext.Provider value={{ clearSession, getSession, setSession }}>
_53
{children}
_53
</SessionVaultContext.Provider>
_53
);
_53
};

Like AuthProvider, a React Context will be used to expose functionality related to managing the authentication result.

Don't forget to wrap IonReactRouter with the new provider so the Context can be shared with the Tabs.

src/App.tsx

_80
import { Redirect, Route } from "react-router-dom";
_80
import {
_80
</SessionVaultProvider>

Modify the Auth context

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx

_122
import { AuthConnect, ProviderOptions, Auth0Provider, TokenType } from "@ionic-enterprise/auth";
_122
import { isPlatform } from "@ionic/react";
_122
import { PropsWithChildren, createContext } from "react";
_122
import { SessionVaultContext } from './SessionVaultProvider';
_122
_122
const isNative = isPlatform("hybrid");
_122
_122
const options: ProviderOptions = {
_122
audience: "https://io.ionic.demo.ac",
_122
clientId: "yLasZNUGkZ19DGEjTmAITBfGXzqbvd00",
_122
discoveryUrl:
_122
"https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration",
_122
logoutUrl: isNative ? "msauth://login" : "http://localhost:8100/login",
_122
redirectUri: isNative ? "msauth://login" : "http://localhost:8100/login",
_122
scope: "openid offline_access email picture profile",
_122
};
_122
_122
const setupAuthConnect = async (): Promise<void> => {
_122
return AuthConnect.setup({
_122
platform: isNative ? "capacitor" : "web",
_122
logLevel: "DEBUG",
_122
ios: { webView: "private" },
_122
web: { uiMode: "popup", authFlow: "implicit" },
_122
});
_122
};
_122
_122
export const provider = new Auth0Provider();
_122
_122
export const AuthContext = createContext<{
_122
isAuthenticated: boolean;
_122
getAccessToken: () => Promise<string | undefined>;
_122
getUserName: () => Promise<string | undefined>;
_122
login: () => Promise<void>;
_122
logout: () => Promise<void>;
_122
}>({
_122
isAuthenticated: false,
_122
getAccessToken: () => { throw new Error("Method not implemented."); },
_122
getUserName: () => { throw new Error("Method not implemented."); },
_122
login: () => { throw new Error("Method not implemented."); },
_122
logout: () => { throw new Error("Method not implemented."); }
_122
});
_122
_122
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_122
const [isSetup, setIsSetup] = useState<boolean>(false);
_122
const { clearSession, getSession, setSession } = useContext(SessionVaultContext);
_122
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
_122
_122
const saveAuthResult = async (authResult: AuthResult | null): Promise<void> => {
_122
if (authResult) {
_122
await setSession(authResult);
_122
setIsAuthenticated(true);
_122
} else {
_122
await clearSession();
_122
setIsAuthenticated(false);
_122
}
_122
};
_122
_122
const refreshAuth = async (authResult: AuthResult): Promise<AuthResult | null> => {
_122
let newAuthResult: AuthResult | null = null;
_122
_122
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_122
try {
_122
newAuthResult = await AuthConnect.refreshSession(provider, authResult);
_122
} catch (err) {
_122
console.log("Error refreshing session.", err);
_122
}
_122
}
_122
_122
return newAuthResult;
_122
};
_122
_122
const getAuthResult = async (): Promise<AuthResult | null> => {
_122
let authResult = await getSession();
_122
_122
if (authResult && (await AuthConnect.isAccessTokenExpired(authResult))) {
_122
const newAuthResult = await refreshAuth(authResult);
_122
await saveAuthResult(newAuthResult);
_122
}
_122
setIsAuthenticated(!!authResult);
_122
return authResult;
_122
};
_122
_122
useEffect(() => {
_122
setupAuthConnect().then(() => setIsSetup(true));
_122
}, []);
_122
_122
const login = async (): Promise<void> => {
_122
const authResult = await AuthConnect.login(provider, options);
_122
await saveAuthResult(authResult);
_122
};
_122
_122
const logout = async (): Promise<void> => {
_122
if (authResult) {
_122
await AuthConnect.logout(provider, authResult);
_122
await saveAuthResult(authResult);
_122
}
_122
};
_122
_122
const getAccessToken = async (): Promise<string | undefined> => {
_122
const res = await getAuthResult();
_122
return res?.accessToken;
_122
};
_122
_122
const getUserName = async (): Promise<string | undefined> => {
_122
const res = await getAuthResult();
_122
if (res) {
_122
const data = await AuthConnect.decodeToken<{ name: string }>(
_122
TokenType.id,
_122
res
_122
);
_122
return data?.name;
_122
}
_122
};
_122
_122
return (
_122
<AuthContext.Provider value={{
_122
isAuthenticated, getAccessToken, getUserName, login, logout
_122
}}>
_122
{isSetup && children}
_122
</AuthContext.Provider>
_122
);
_122
};

The goal is to no longer store the auth result in a session variable. Instead, we will use the session Vault to store the result and retrieve it as needed.

You should now be able to refresh the app and have a persistent session. The tutorial is now complete!

Learn how to get started with Auth Connect by creating a sample project that uses @ionic/react.

This tutorial starts with installing and configuring Auth Connect and moves onto achieving common use-cases: performing login/logout, checking the user's authentication status, obtaining tokens from Auth Connect, and storing tokens using Identity Vault.

Generate the Application

Start by creating an Ionic React Tabbed Layout starter.

Terminal

ionic start getting-started-ac-react-tabs --type=react

Change the app identifier

Ionic recommends changing the app identifier before adding any deployment platforms. If you want to change the app identifier after adding iOS or Android, you will have to update native artifacts.

capacitor.config.ts

_10
import { CapacitorConfig } from "@capacitor/cli";
_10
appId: "io.ionic.gettingstartedacreact",

Remove React Strict Mode

Strict mode triggers useEffect twice in development, remove it for this tutorial.

index.tsx

_19
import React from "react";
_19
root.render(<App />);

Add the native platforms

Build the application then install and create the iOS and Android platforms.

Terminal

npm run build
ionic cap add android
ionic cap add ios

Modify the build script

Modify the build npm script to sync Capacitor with each build.

package.json

_24
{
_24
"build": "react-scripts build && cap sync",

Install Auth Connect

In order to install Auth Connect, you will need to use ionic enterprise register to register your product key. This will create a .npmrc file containing the product key.

If you have already performed that step for your production application, you can just copy the .npmrc file from your production project. Since this application is for learning purposes only, you don't need to obtain another key.

Terminal

npm install @ionic-enterprise/auth

Configure Auth Connect

Auth Connect functionality will be exposed through a React Context.

Create AuthProvider.tsx within a folder named src/providers.

Don't forget to wrap IonReactRouter with the new provider so the Context can be shared with the Tabs.

src/App.tsx

_78
import { Redirect, Route } from "react-router-dom";
_78
import {
_78
</IonReactRouter>

Auth Connect options

The options object gets passed to the login() function to help establish the authentication session.

Obtaining this information likely takes coordination with whomever administers the backend services.

You can use your own configuration for this step; however, we suggest starting with our configuration, get the application working, and then try your own configuration after that.

Initialization

Auth Connect needs to be initialized before any functions can be used.

A flag in AuthProvider will prevent components within IonReactRouter from rendering until Auth Connect has initialized.

The Provider

A provider object specifying details on communicating with the OIDC service is required.

Auth Connect offers several common providers out of the box: Auth0Provider, AzureProvider, CognitoProvider, OktaProvider, and OneLoginProvider. You can also create your own provider, though doing so is beyond the scope of this tutorial.

Login and Logout

Login and logout are the two most fundamental operations in the authentication flow.

Login requires both the provider and options established above. Login resolves an AuthResult if the operation succeeds. The AuthResult contains auth tokens as well as some other information. This object needs to be passed to almost all other AuthConnect functions; as such, it needs to be saved. The login() call rejects with an error if the user cancels the login or if something else prevents the login to complete.

Logout requires the provider as well as the AuthResult object returned by the login() call.

To test these new functions, replace ExploreContainer with "Login" and "Logout" buttons in src/pages/Tab1.tsx.

Using Ionic's Auth0 provider, functionality can be tested with the following credentials:

  • Email Address: test@ionic.io
  • Password: Ion54321

Configure the native projects

Login will fail when testing on a native device.

This occurs because the native device doesn't know which application(s) handle navigation to the msauth:// scheme (using Ionic's Auth0 provider). To register the application to handle the scheme, modify the build.gradle and Info.plist files as noted here.

Replace $AUTH_URL_SCHEME with msauth when using Ionic's Auth0 provider.

Determining the current auth status

Users are shown both the login and logout buttons but you don't really know if the user is logged in or not. Let's change that.

A simple strategy to use is tracking the status via state, updating the value after calling certain AuthConnect methods.

Ignore the extra complexity with the getAuthResult() function -- we will expand on that as we go.

Use isAuthenticated to determine which button to display, depending on the current auth status.

Notice the try...catch in the handleLogin() method. Production apps should have some kind of handling here, but this tutorial will only log the fact.

At this point, you should see the "Login" button if you are not logged in and the "Logout" button if you are.

Get the tokens

We can now log in and out, but what about getting the tokens that the OIDC provider gave us? That information is stored as part of the AuthResult. Auth Connect provides methods to allow us to easily look at the contents of the tokens.

Note: the format and data stored in the ID token may change based on your provider and configuration. Check the documentation and configuration of your own provider for details.

These methods can be used wherever a specific token is required. For example, if you are accessing a backend API that requires you to include a bearer token, you can use getAccessToken() to create an HTTP interceptor that adds the token to HTTP requests.

An interceptor isn't needed for this app, but as a challenge to you, update Tab1.tsx to show the user's name when they are logged in. You could also display the access token if you want (though you'd never do that in a real app).

Refreshing the authentication session

In a typical OIDC implementation, access tokens are very short lived. It is common to use a longer lived refresh token to obtain a new AuthResult.

Add a function that performs a refresh, then modify getAuthResult() to refresh the session when needed. Now anything using getAuthResult() to get the current auth result will automatically trigger a refresh if needed.

Store the Auth Result

So far the AuthResult has been stored in a local state variable, which has a couple of disadvantages:

  • Tokens could show up in a stack trace.
  • Tokens do not survive a browser refresh or application restart.

There are several options we could use to store the AuthResult, but one that handles persistence as well as storing the data in a secure location on native devices is Ionic Identity Vault.

For our application, we will install Identity Vault and use it in "Secure Storage" mode to store the tokens.

Terminal

npm install @ionic-enterprise/identity-vault

Create a Vault factory

Create SessionVaultProvider.tsx within src/providers. In this file setup a factory that builds either the actual Vault - if we are on a device - or a browser-based "Vault" suitable for development in the browser.

This provides us with a secure Vault on device, or a fallback Vault that allows us to keep using our browser-based development flow.

Set up a React context

Like AuthProvider, a React Context will be used to expose functionality related to managing the authentication result.

Don't forget to wrap IonReactRouter with the new provider so the Context can be shared with the Tabs.

src/App.tsx

_80
import { Redirect, Route } from "react-router-dom";
_80
import {
_80
</SessionVaultProvider>

Modify the Auth context

The goal is to no longer store the auth result in a session variable. Instead, we will use the session Vault to store the result and retrieve it as needed.

You should now be able to refresh the app and have a persistent session. The tutorial is now complete!

AuthProvider.tsx
Tab1.tsx
SessionVaultProvider.tsx
ExpandClose

_117
import { AuthConnect, ProviderOptions, Auth0Provider, TokenType } from "@ionic-enterprise/auth";
_117
import { isPlatform } from '@ionic/react';
_117
import { PropsWithChildren, createContext, useState, useEffect, useContext } from 'react';
_117
import { SessionVaultContext } from './SessionVaultProvider';
_117
_117
const isNative = isPlatform('hybrid');
_117
_117
const options: ProviderOptions = {
_117
audience: 'https://io.ionic.demo.ac',
_117
clientId: 'yLasZNUGkZ19DGEjTmAITBfGXzqbvd00',
_117
discoveryUrl: 'https://dev-2uspt-sz.us.auth0.com/.well-known/openid-configuration',
_117
logoutUrl: isNative ? 'msauth://login' : 'http://localhost:8100/login',
_117
redirectUri: isNative ? 'msauth://login' : 'http://localhost:8100/login',
_117
scope: 'openid offline_access email picture profile',
_117
};
_117
_117
const setupAuthConnect = async (): Promise<void> => {
_117
return AuthConnect.setup({
_117
platform: isNative ? 'capacitor' : 'web',
_117
logLevel: 'DEBUG',
_117
ios: { webView: 'private' },
_117
web: { uiMode: 'popup', authFlow: 'implicit' },
_117
});
_117
};
_117
_117
const provider = new Auth0Provider();
_117
_117
export const AuthContext = createContext<{
_117
isAuthenticated: boolean;
_117
getAccessToken: () => Promise<string | undefined>;
_117
getUserName: () => Promise<string | undefined>;
_117
login: () => Promise<void>;
_117
logout: () => Promise<void>;
_117
}>({
_117
isAuthenticated: false,
_117
getAccessToken: () => { throw new Error('Method not implemented.'); },
_117
getUserName: () => { throw new Error('Method not implemented.'); },
_117
login: () => { throw new Error('Method not implemented.'); },
_117
logout: () => { throw new Error('Method not implemented.'); },
_117
});
_117
_117
export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
_117
const [isSetup, setIsSetup] = useState<boolean>(false);
_117
const { clearSession, getSession, setSession } = useContext(SessionVaultContext);
_117
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
_117
_117
const saveAuthResult = async (authResult: AuthResult | null): Promise<void> => {
_117
if (authResult) {
_117
await setSession(authResult);
_117
setIsAuthenticated(true);
_117
} else {
_117
await clearSession();
_117
setIsAuthenticated(false);
_117
}
_117
};
_117
_117
const refreshAuth = async (authResult: AuthResult): Promise<AuthResult | null> => {
_117
let newAuthResult: AuthResult | null = null;
_117
_117
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_117
try {
_117
newAuthResult = await AuthConnect.refreshSession(provider, authResult);
_117
} catch (err) {
_117
console.log('Error refreshing session.', err);
_117
}
_117
}
_117
_117
return newAuthResult;
_117
};
_117
_117
const getAuthResult = async (): Promise<AuthResult | null> => {
_117
let authResult = await getSession();
_117
_117
if (authResult && (await AuthConnect.isAccessTokenExpired(authResult))) {
_117
const newAuthResult = await refreshAuth(authResult);
_117
await saveAuthResult(newAuthResult);
_117
}
_117
setIsAuthenticated(!!authResult);
_117
return authResult;
_117
};
_117
_117
useEffect(() => {
_117
setupAuthConnect().then(() => setIsSetup(true));
_117
}, []);
_117
_117
const getAccessToken = async (): Promise<string | undefined> => {
_117
const res = await getAuthResult();
_117
return res?.accessToken;
_117
};
_117
_117
const getUserName = async (): Promise<string | undefined> => {
_117
const res = await getAuthResult();
_117
if (res) {
_117
const data = await AuthConnect.decodeToken<{ name: string }>(TokenType.id, res);
_117
return data?.name;
_117
}
_117
};
_117
_117
const login = async (): Promise<void> => {
_117
const authResult = await AuthConnect.login(provider, options);
_117
await saveAuthResult(authResult);
_117
};
_117
_117
const logout = async (): Promise<void> => {
_117
const authResult = await getAuthResult();
_117
if (authResult) {
_117
await AuthConnect.logout(provider, authResult);
_117
await saveAuthResult(null);
_117
}
_117
};
_117
_117
return (
_117
<AuthContext.Provider value={{ isAuthenticated, getAccessToken, getUserName, login, logout }}>
_117
{isSetup && children}
_117
</AuthContext.Provider>
_117
);
_117
};