import auth0 from 'auth0-js'; import { storeStateAndNonce, clearStateAndNonce, getStateAndNonce, storeAuth, getIdToken, getUserInfo, clear, getExpiresAt, getAccessToken, } from './storage'; export default class AuthenticationClient { /** * Instantiates an authentication client. * You should only need one per app and authentication method. * @param {Object} auth0Config An auth0 configuration object, as per their API. */ constructor(auth0Config) { this.config = auth0Config; this.webAuth = new auth0.WebAuth(auth0Config); } logout() { clear(); } getUserInfo() { return getUserInfo(); } isAuthenticated() { return !!getIdToken(); } isExpired() { const expiresAt = getExpiresAt(); return new Date().getTime() > expiresAt && this.isAuthenticated(); } /** * Triggers a login by redirecting to auth0. */ triggerLogin(state) { // clear the state and nonce when triggering a login - otherwise hash parsing wont work. clearStateAndNonce(); this.webAuth.authorize({ state: JSON.stringify(state) }); } triggerSilentLogin(state) { if (!state) { throw new Error('You must specify a state.'); } const nonce = new Date().getTime().toString(); // before we trigger the silent login - store the state and nonce in localstorage. storeStateAndNonce(state, nonce); let url = this.webAuth.client.buildAuthorizeUrl({ ...this.config, nonce, state: JSON.stringify(state), }); url += '&prompt=none'; window.location.href = url; } checkSession(resolve, reject) { this.webAuth.checkSession({state: {}}, (err, result) => { if (err) { return reject(err || 'Re-authentication failed'); } else { return this.storeSession(result, resolve, reject); } }); } storeSession(authResult, resolve, reject) { this.webAuth.client.userInfo(authResult.accessToken, (err, user) => { if (err) { // if any error happens when fetching user info - nuke all session info this.logout(); return reject('Authentication failed'); } const expiresAt = JSON.stringify(authResult.expiresIn * 1000 + new Date().getTime()); storeAuth({ ...authResult, user, expiresAt }); return resolve(JSON.parse(authResult.state)); }); } handleAuthentication() { return new Promise((resolve, reject) => { // retrieve stored state and nonce from localstorage const { state, nonce } = getStateAndNonce(); // however, if there is no state and nonce stored - do not provide any param to parseHash. // Otherwise, the non-silent logins will fail due to invalid state. const parseHashParam = state && nonce ? { state, nonce } : undefined; this.webAuth.parseHash(parseHashParam, (err, authResult) => { if (authResult && authResult.accessToken && authResult.idToken) { // If we fail to either set the session or store user info - reject and clear the session. try { return this.storeSession(authResult, resolve, reject); } catch (error) { return reject(error || 'Authentication failed'); } } else { return reject(err || 'Authentication failed'); } }); }); } idToken() { return getIdToken(); } accessToken() { return getAccessToken(); } }