diff --git a/__tests__/auth-provider.test.tsx b/__tests__/auth-provider.test.tsx index 0ec66d3c..3aa61116 100644 --- a/__tests__/auth-provider.test.tsx +++ b/__tests__/auth-provider.test.tsx @@ -975,6 +975,30 @@ describe('Auth0Provider', () => { }); }); + it('should return the user on getUser', async () => { + const userObject = { name: 'foo' }; + clientMock.getUser.mockResolvedValue(userObject); + const wrapper = createWrapper(); + const { result } = renderHook(() => useContext(Auth0Context), { wrapper }); + let user; + await act(async () => { + user = await result.current.getUser(); + }); + expect(user).toBe(userObject); + }); + + it('should handle getUser errors', async () => { + const wrapper = createWrapper(); + const { result } = renderHook(() => useContext(Auth0Context), { wrapper }); + + clientMock.getUser.mockRejectedValue(new Error('__test_error__')); + await act(async () => { + await expect(() => result.current.getUser()).rejects.toThrowError( + '__test_error__' + ); + }); + }); + it('should allow passing a custom context', async () => { const context = React.createContext(initialContext); clientMock.getIdTokenClaims.mockResolvedValue({ diff --git a/src/auth0-context.tsx b/src/auth0-context.tsx index d2960ab9..7e895bcc 100644 --- a/src/auth0-context.tsx +++ b/src/auth0-context.tsx @@ -140,6 +140,14 @@ export interface Auth0ContextInterface * @param url The URL to that should be used to retrieve the `state` and `code` values. Defaults to `window.location.href` if not given. */ handleRedirectCallback: (url?: string) => Promise; + + /** + * Returns the user information if available (decoded + * from the `id_token`). + * + * @typeparam TUser The type to return, has to extend {@link User}. + */ + getUser: () => Promise; } /** @@ -163,6 +171,7 @@ export const initialContext = { loginWithPopup: stub, logout: stub, handleRedirectCallback: stub, + getUser: stub, }; /** diff --git a/src/auth0-provider.tsx b/src/auth0-provider.tsx index 9e4e09a2..b16cfbd1 100644 --- a/src/auth0-provider.tsx +++ b/src/auth0-provider.tsx @@ -25,6 +25,7 @@ import { hasAuthParams, loginError, tokenError, + userError, deprecateRedirectUri, } from './utils'; import { reducer } from './reducer'; @@ -272,6 +273,18 @@ const Auth0Provider = (opts: Auth0ProviderOptions) => { [client] ); + const getUser = useCallback( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async (): Promise => { + try { + return await client.getUser(); + } catch (error) { + throw userError(error); + } + }, + [client] + ); + const contextValue = useMemo>(() => { return { ...state, @@ -282,6 +295,7 @@ const Auth0Provider = (opts: Auth0ProviderOptions) => { loginWithPopup, logout, handleRedirectCallback, + getUser, }; }, [ state, @@ -292,6 +306,7 @@ const Auth0Provider = (opts: Auth0ProviderOptions) => { loginWithPopup, logout, handleRedirectCallback, + getUser, ]); return {children}; diff --git a/src/utils.tsx b/src/utils.tsx index d26a256c..8f0bc963 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -36,6 +36,8 @@ export const loginError = normalizeErrorFn('Login failed'); export const tokenError = normalizeErrorFn('Get access token failed'); +export const userError = normalizeErrorFn('Get user failed'); + /** * @ignore * Helper function to map the v1 `redirectUri` option to the v2 `authorizationParams.redirect_uri`