diff --git a/__tests__/auth-provider.test.tsx b/__tests__/auth-provider.test.tsx index 9dbd6d85..93702b5d 100644 --- a/__tests__/auth-provider.test.tsx +++ b/__tests__/auth-provider.test.tsx @@ -457,4 +457,16 @@ describe('Auth0Provider', () => { __raw: '__test_raw_token__', }); }); + + it('should memoize the getIdTokenClaims method', async () => { + const wrapper = createWrapper(); + const { waitForNextUpdate, result, rerender } = renderHook( + () => useContext(Auth0Context), + { wrapper } + ); + await waitForNextUpdate(); + const memoized = result.current.getIdTokenClaims; + rerender(); + expect(result.current.getIdTokenClaims).toBe(memoized); + }); }); diff --git a/src/auth0-provider.tsx b/src/auth0-provider.tsx index dbaacdf9..2e10ef41 100644 --- a/src/auth0-provider.tsx +++ b/src/auth0-provider.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useReducer, useState } from 'react'; +import React, { useCallback, useEffect, useReducer, useState } from 'react'; import { Auth0Client, Auth0ClientOptions, @@ -10,6 +10,7 @@ import { RedirectLoginOptions as Auth0RedirectLoginOptions, GetTokenWithPopupOptions, GetTokenSilentlyOptions, + GetIdTokenClaimsOptions, } from '@auth0/auth0-spa-js'; import Auth0Context, { RedirectLoginOptions } from './auth0-context'; import { hasAuthParams, loginError, tokenError } from './utils'; @@ -225,56 +226,78 @@ const Auth0Provider = (opts: Auth0ProviderOptions): JSX.Element => { })(); }, [client, onRedirectCallback, skipRedirectCallback]); - const loginWithPopup = async ( - options?: PopupLoginOptions, - config?: PopupConfigOptions - ): Promise => { - dispatch({ type: 'LOGIN_POPUP_STARTED' }); - try { - await client.loginWithPopup(options, config); - } catch (error) { - dispatch({ type: 'ERROR', error: loginError(error) }); - return; - } - const user = await client.getUser(); - dispatch({ type: 'LOGIN_POPUP_COMPLETE', user }); - }; + const loginWithRedirect = useCallback( + (opts?: Auth0RedirectLoginOptions): Promise => + client.loginWithRedirect(toAuth0LoginRedirectOptions(opts)), + [client] + ); - const logout = (opts: LogoutOptions = {}): void => { - client.logout(opts); - if (opts.localOnly) { - dispatch({ type: 'LOGOUT' }); - } - }; + const loginWithPopup = useCallback( + async ( + options?: PopupLoginOptions, + config?: PopupConfigOptions + ): Promise => { + dispatch({ type: 'LOGIN_POPUP_STARTED' }); + try { + await client.loginWithPopup(options, config); + } catch (error) { + dispatch({ type: 'ERROR', error: loginError(error) }); + return; + } + const user = await client.getUser(); + dispatch({ type: 'LOGIN_POPUP_COMPLETE', user }); + }, + [client] + ); - const getAccessTokenSilently = async ( - opts?: GetTokenSilentlyOptions - ): Promise => { - let token; - try { - token = await client.getTokenSilently(opts); - } catch (error) { - throw tokenError(error); - } - const user = await client.getUser(); - dispatch({ type: 'GET_TOKEN_COMPLETE', user }); - return token; - }; + const logout = useCallback( + (opts: LogoutOptions = {}): void => { + client.logout(opts); + if (opts.localOnly) { + dispatch({ type: 'LOGOUT' }); + } + }, + [client] + ); - const getAccessTokenWithPopup = async ( - opts?: GetTokenWithPopupOptions, - config?: PopupConfigOptions - ): Promise => { - let token; - try { - token = await client.getTokenWithPopup(opts, config); - } catch (error) { - throw tokenError(error); - } - const user = await client.getUser(); - dispatch({ type: 'GET_TOKEN_COMPLETE', user }); - return token; - }; + const getAccessTokenSilently = useCallback( + async (opts?: GetTokenSilentlyOptions): Promise => { + let token; + try { + token = await client.getTokenSilently(opts); + } catch (error) { + throw tokenError(error); + } + const user = await client.getUser(); + dispatch({ type: 'GET_TOKEN_COMPLETE', user }); + return token; + }, + [client] + ); + + const getAccessTokenWithPopup = useCallback( + async ( + opts?: GetTokenWithPopupOptions, + config?: PopupConfigOptions + ): Promise => { + let token; + try { + token = await client.getTokenWithPopup(opts, config); + } catch (error) { + throw tokenError(error); + } + const user = await client.getUser(); + dispatch({ type: 'GET_TOKEN_COMPLETE', user }); + return token; + }, + [client] + ); + + const getIdTokenClaims = useCallback( + (opts?: GetIdTokenClaimsOptions): Promise => + client.getIdTokenClaims(opts), + [client] + ); return ( { ...state, getAccessTokenSilently, getAccessTokenWithPopup, - getIdTokenClaims: (opts): Promise => - client.getIdTokenClaims(opts), - loginWithRedirect: (opts): Promise => - client.loginWithRedirect(toAuth0LoginRedirectOptions(opts)), + getIdTokenClaims, + loginWithRedirect, loginWithPopup, logout, }}