Skip to content

Commit 37201b7

Browse files
committed
fix(auth): correct JWT claims types to match official documentation
1 parent 41cabeb commit 37201b7

File tree

2 files changed

+82
-20
lines changed

2 files changed

+82
-20
lines changed

packages/core/auth-js/src/lib/types.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,21 +1439,31 @@ export type RequiredClaims = {
14391439
session_id: string
14401440
}
14411441

1442+
/**
1443+
* JWT Payload containing claims for Supabase authentication tokens.
1444+
*
1445+
* Required claims (iss, aud, exp, iat, sub, role, aal, session_id) are inherited from RequiredClaims.
1446+
* All other claims are optional as they can be customized via Custom Access Token Hooks.
1447+
*
1448+
* @see https://supabase.com/docs/guides/auth/jwt-fields
1449+
*/
14421450
export interface JwtPayload extends RequiredClaims {
1451+
// Standard optional claims (can be customized via custom access token hooks)
14431452
email?: string
14441453
phone?: string
1445-
user_metadata?: UserMetadata
1446-
app_metadata?: UserAppMetadata
14471454
is_anonymous?: boolean
1448-
is_sso_user?: boolean
1449-
id?: string
1450-
created_at?: string
1451-
updated_at?: string
1452-
confirmed_at?: string
1453-
email_confirmed_at?: string
1454-
phone_confirmed_at?: string
1455-
last_sign_in_at?: string
1456-
identities?: UserIdentity[]
1455+
1456+
// Optional claims
1457+
jti?: string
1458+
nbf?: number
1459+
app_metadata?: UserAppMetadata
1460+
user_metadata?: UserMetadata
1461+
amr?: AMREntry[]
1462+
1463+
// Special claims (only in anon/service role tokens)
1464+
ref?: string
1465+
1466+
// Allow custom claims via custom access token hooks
14571467
[key: string]: any
14581468
}
14591469

packages/core/auth-js/test/GoTrueClient.test.ts

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,18 +1628,70 @@ describe('getClaims', () => {
16281628
const { data, error } = await authWithSession.getClaims()
16291629
expect(error).toBeNull()
16301630
expect(data).not.toBeNull()
1631-
1631+
16321632
const claims = data?.claims
16331633
expect(claims).toBeDefined()
1634-
1635-
expect(typeof claims?.email).toBe('string')
1636-
expect(typeof claims?.user_metadata).toBe('object')
1637-
expect(typeof claims?.app_metadata).toBe('object')
1638-
expect(typeof claims?.is_anonymous).toBe('boolean')
1639-
expect(typeof claims?.is_sso_user).toBe('boolean')
1640-
expect(typeof claims?.id).toBe('string')
1641-
expect(typeof claims?.created_at).toBe('string')
1634+
1635+
// Test core required claims that are always present
1636+
expect(typeof claims?.sub).toBe('string')
16421637
expect(typeof claims?.role).toBe('string')
1638+
1639+
// Test standard optional claims
1640+
if (claims?.email !== undefined) {
1641+
expect(typeof claims.email).toBe('string')
1642+
}
1643+
if (claims?.phone !== undefined) {
1644+
expect(typeof claims.phone).toBe('string')
1645+
}
1646+
if (claims?.user_metadata !== undefined) {
1647+
expect(typeof claims.user_metadata).toBe('object')
1648+
}
1649+
if (claims?.app_metadata !== undefined) {
1650+
expect(typeof claims.app_metadata).toBe('object')
1651+
}
1652+
if (claims?.is_anonymous !== undefined) {
1653+
expect(typeof claims.is_anonymous).toBe('boolean')
1654+
}
1655+
1656+
// Test optional JWT standard claims if present
1657+
if (claims?.iss !== undefined) {
1658+
expect(typeof claims.iss).toBe('string')
1659+
}
1660+
if (claims?.aud !== undefined) {
1661+
expect(['string', 'object']).toContain(typeof claims.aud)
1662+
}
1663+
if (claims?.exp !== undefined) {
1664+
expect(typeof claims.exp).toBe('number')
1665+
}
1666+
if (claims?.iat !== undefined) {
1667+
expect(typeof claims.iat).toBe('number')
1668+
}
1669+
if (claims?.aal !== undefined) {
1670+
expect(typeof claims.aal).toBe('string')
1671+
}
1672+
if (claims?.session_id !== undefined) {
1673+
expect(typeof claims.session_id).toBe('string')
1674+
}
1675+
if (claims?.jti !== undefined) {
1676+
expect(typeof claims.jti).toBe('string')
1677+
}
1678+
if (claims?.nbf !== undefined) {
1679+
expect(typeof claims.nbf).toBe('number')
1680+
}
1681+
1682+
// Verify amr array structure if present
1683+
if (claims?.amr) {
1684+
expect(Array.isArray(claims.amr)).toBe(true)
1685+
if (claims.amr.length > 0) {
1686+
expect(typeof claims.amr[0].method).toBe('string')
1687+
expect(typeof claims.amr[0].timestamp).toBe('number')
1688+
}
1689+
}
1690+
1691+
// Verify ref claim if present (anon/service role tokens)
1692+
if (claims?.ref !== undefined) {
1693+
expect(typeof claims.ref).toBe('string')
1694+
}
16431695
})
16441696

16451697
test('getClaims fetches JWKS to verify asymmetric jwt', async () => {

0 commit comments

Comments
 (0)