Skip to content

Commit 6fa458d

Browse files
authored
feat: Add parseIdToken utility to public API (#1537)
1 parent e0d4a6a commit 6fa458d

5 files changed

Lines changed: 105 additions & 0 deletions

File tree

EXAMPLES.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ auth0.auth
137137

138138
This endpoint requires an access token that was granted the `/userinfo` audience. Check that the authentication request that returned the access token included an audience value of `https://{YOUR_AUTH0_DOMAIN}.auth0.com/userinfo`.
139139

140+
### Parse user profile from an ID token locally
141+
142+
If you already have credentials (e.g. from `webAuth.authorize()` or `credentialsManager.getCredentials()`), you can extract the user profile from the ID token without a network request:
143+
144+
```js
145+
import Auth0, { parseIdToken } from 'react-native-auth0';
146+
147+
const auth0 = new Auth0({ domain, clientId });
148+
const credentials = await auth0.webAuth.authorize({ scope: 'openid profile email' });
149+
const user = parseIdToken(credentials.idToken);
150+
// user.sub, user.name, user.email, etc.
151+
```
152+
153+
This is the same parsing that `Auth0Provider` performs internally. It's useful when you manage auth state yourself via the `Auth0` class and want to avoid the network round-trip of `auth.userInfo()`.
154+
140155
### Getting new access token with refresh token
141156

142157
```js
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { parseIdToken } from '../parseIdToken';
2+
import { jwtDecode } from 'jwt-decode';
3+
4+
jest.mock('jwt-decode');
5+
6+
describe('parseIdToken', () => {
7+
const mockJwtDecode = jwtDecode as jest.Mock;
8+
9+
beforeEach(() => {
10+
jest.clearAllMocks();
11+
});
12+
13+
it('should return a User with decoded profile claims', () => {
14+
mockJwtDecode.mockReturnValue({
15+
sub: 'auth0|123',
16+
name: 'Jane Doe',
17+
email: 'jane@example.com',
18+
email_verified: true,
19+
given_name: 'Jane',
20+
family_name: 'Doe',
21+
});
22+
23+
const user = parseIdToken('mock-id-token');
24+
25+
expect(user.sub).toBe('auth0|123');
26+
expect(user.name).toBe('Jane Doe');
27+
expect(user.email).toBe('jane@example.com');
28+
expect(user.emailVerified).toBe(true);
29+
expect(user.givenName).toBe('Jane');
30+
expect(user.familyName).toBe('Doe');
31+
});
32+
33+
it('should exclude protocol claims', () => {
34+
mockJwtDecode.mockReturnValue({
35+
sub: 'auth0|123',
36+
iss: 'https://tenant.auth0.com/',
37+
aud: 'client-id',
38+
exp: 9999999999,
39+
iat: 1000000000,
40+
});
41+
42+
const user = parseIdToken('mock-id-token');
43+
44+
expect(user.sub).toBe('auth0|123');
45+
expect((user as any).iss).toBeUndefined();
46+
expect((user as any).aud).toBeUndefined();
47+
expect((user as any).exp).toBeUndefined();
48+
expect((user as any).iat).toBeUndefined();
49+
});
50+
51+
it('should throw if the token is missing the sub claim', () => {
52+
mockJwtDecode.mockReturnValue({
53+
name: 'No Sub',
54+
email: 'nosub@example.com',
55+
});
56+
57+
expect(() => parseIdToken('bad-token')).toThrow(
58+
'ID token is missing the required "sub" claim.'
59+
);
60+
});
61+
});

src/core/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './validation';
22
export * from './conversion';
33
export * from './fetchWithTimeout';
4+
export * from './parseIdToken';
45
export * from './scope';

src/core/utils/parseIdToken.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { User } from '../../types';
2+
import { Auth0User } from '../models';
3+
4+
/**
5+
* Decodes a JWT ID token and returns a {@link User} object with standard OIDC
6+
* profile claims (camelCased) and any custom claims present in the token.
7+
*
8+
* This provides the same user-parsing behavior that {@link Auth0Provider} uses
9+
* internally, for consumers managing auth state directly via the {@link Auth0} class.
10+
*
11+
* @param idToken - A JWT ID token string (e.g. from `credentials.idToken`).
12+
* @returns A parsed {@link User} containing profile and custom claims.
13+
* @throws If the token is missing the required `sub` claim.
14+
*
15+
* @example
16+
* ```typescript
17+
* import Auth0, { parseIdToken } from 'react-native-auth0';
18+
*
19+
* const auth0 = new Auth0({ domain, clientId });
20+
* const credentials = await auth0.webAuth.authorize({ scope: 'openid profile email' });
21+
* const user = parseIdToken(credentials.idToken);
22+
* // user.sub, user.name, user.email, etc.
23+
* ```
24+
*/
25+
export function parseIdToken(idToken: string): User {
26+
return Auth0User.fromIdToken(idToken);
27+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export {
1111
MyAccountError,
1212
} from './core/models';
1313
export { TimeoutError } from './core/utils/fetchWithTimeout';
14+
export { parseIdToken } from './core/utils';
1415
export { TokenType } from './types/common';
1516
export { Auth0Provider } from './hooks/Auth0Provider';
1617
export { useAuth0 } from './hooks/useAuth0';

0 commit comments

Comments
 (0)