Skip to content

Commit b3ea208

Browse files
nohe427gemini-code-assist[bot]natebosch
authored
feat: add custom claims support to DecodedIdToken model and update tests (#253)
* feat: add custom claims support to DecodedIdToken model and update tests * docs: add custom claims support to changelog * docs: add example for accessing custom claims in README * fix: exclude standard OIDC and reserved claims from DecodedIdToken custom claims map * Update packages/firebase_admin_sdk/lib/src/auth/token_verifier.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Fix the github suggested change * Update packages/firebase_admin_sdk/lib/src/auth/token_verifier.dart Co-authored-by: Nate Bosch <nbosch1@gmail.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Nate Bosch <nbosch1@gmail.com>
1 parent 4873d0e commit b3ea208

4 files changed

Lines changed: 42 additions & 5 deletions

File tree

packages/firebase_admin_sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- AppOptions.additionalScopes` — append extra OAuth2 scopes to the SDK-managed HTTP client
88
without providing your own `AuthClient`.
99
- `FirebaseApp.client` is now part of the public API.
10+
- Add support for custom claims in ID tokens.
1011

1112
## 0.5.1
1213

packages/firebase_admin_sdk/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,11 @@ final nextPageToken = result.pageToken;
385385
final idToken = req.headers['Authorization'].split(' ')[1];
386386
final decodedToken = await auth.verifyIdToken(idToken, checkRevoked: true);
387387
print(decodedToken.uid);
388+
389+
// Access custom claims
390+
if (decodedToken.claims['isAdmin'] == true) {
391+
print('User is an admin');
392+
}
388393
```
389394

390395
#### createCustomToken

packages/firebase_admin_sdk/lib/src/auth/token_verifier.dart

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ class DecodedIdToken {
349349
DecodedIdToken({
350350
required this.aud,
351351
required this.authTime,
352+
required this.claims,
352353
required this.email,
353354
required this.emailVerified,
354355
required this.exp,
@@ -363,11 +364,14 @@ class DecodedIdToken {
363364

364365
@internal
365366
factory DecodedIdToken.fromMap(Map<String, Object?> map) {
367+
final claims = _extractCustomClaims(map);
368+
366369
return DecodedIdToken(
367370
aud: map['aud']! as String,
368371
authTime: DateTime.fromMillisecondsSinceEpoch(
369372
(map['auth_time']! as int) * 1000,
370373
),
374+
claims: Map.unmodifiable(claims),
371375
email: map['email'] as String?,
372376
emailVerified: map['email_verified'] as bool?,
373377
exp: map['exp']! as int,
@@ -452,11 +456,30 @@ class DecodedIdToken {
452456
/// convenience, and is set as the value of the [`sub`](#sub) property.
453457
String uid;
454458

455-
/**
456-
* Other arbitrary claims included in the ID token.
457-
*/
458-
// TODO allow any key
459-
// [key: string]: any;
459+
/// Other arbitrary claims included in the ID token.
460+
final Map<String, Object?> claims;
461+
}
462+
463+
/// Extracts custom claims from the raw ID token payload map.
464+
///
465+
/// Custom claims are all claims that are not standard OpenID Connect claims
466+
/// or reserved Firebase claims.
467+
Map<String, Object?> _extractCustomClaims(Map<String, Object?> map) {
468+
return {...map}..removeWhere(
469+
(key, _) => const {
470+
'aud',
471+
'auth_time',
472+
'email',
473+
'email_verified',
474+
'exp',
475+
'firebase',
476+
'iat',
477+
'iss',
478+
'phone_number',
479+
'picture',
480+
'sub',
481+
}.contains(key),
482+
);
460483
}
461484

462485
/// User facing token information related to the Firebase ID token.

packages/firebase_admin_sdk/test/unit/auth/token_verifier_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ void main() {
3636
'phone_number': 'mock-phone-number',
3737
'picture': 'mock-picture',
3838
'sub': 'mock-sub',
39+
'custom_claim': 'mock-custom-claim',
40+
'isAdmin': true,
41+
'numberOfTests': 3,
3942
});
4043
expect(idToken.aud, 'mock-aud');
4144
expect(idToken.authTime, DateTime.fromMillisecondsSinceEpoch(1000));
@@ -56,6 +59,11 @@ void main() {
5659
expect(idToken.picture, 'mock-picture');
5760
expect(idToken.sub, 'mock-sub');
5861
expect(idToken.uid, 'mock-sub');
62+
expect(idToken.claims['custom_claim'], 'mock-custom-claim');
63+
expect(idToken.claims['isAdmin'], true);
64+
expect(idToken.claims['numberOfTests'], 3);
65+
expect(idToken.claims.containsKey('aud'), false);
66+
expect(idToken.claims.containsKey('sub'), false);
5967
});
6068
});
6169
}

0 commit comments

Comments
 (0)