firebase / firebase/firebase-admin-dart
Refactor: Consolidate JWKS fetching and expose a unified JWT verifier for downstream SDKs
- Dominant language
- Dart
- Stars
- 177
- Forks
- 70
- Avg merge
- 5d 14h
- Merged PRs (30d)
- 6
Description
### Background
Across `firebase_admin_sdk` and downstream consumers like `firebase_functions` (which depends on `firebase_admin_sdk`), there are multiple overlapping implementations of JWT signature verification, JWKS / X.509 certificate fetching, and public key caching.
Currently, `firebase_admin_sdk` maintains both [`UrlKeyFetcher`](https://github.com/firebase/firebase-admin-dart/blob/main/packages/firebase_admin_sdk/lib/src/utils/jwt.dart#L60-L120) (for X.509 PEM certs) and [`JwksFetcher`](https://github.com/firebase/firebase-admin-dart/blob/main/packages/firebase_admin_sdk/lib/src/utils/jwt.dart#L122-L171) (for JSON Web Key Sets) in `lib/src/utils/jwt.dart`.
### Current Inconsistencies & Duplication
1. **Inconsistent Key Caching & TTLs**:
* `UrlKeyFetcher` parses HTTP `Cache-Control: max-age` headers via string splitting and tracks expiration via `DateTime`.
* `JwksFetcher` ignores HTTP `Cache-Control` headers entirely, hardcoding a static 6-hour TTL (`6 * 60 * 60 * 1000`) and tracking expiration via epoch integer comparison.
* Meanwhile, downstream in `firebase_functions`, [`AuthBlockingTokenVerifier`](https://github.com/firebase/firebase-functions-dart/blob/main/lib/src/identity/token_verifier.dart#L149-L202) hand-rolled a third key fetcher (`_getGoogleKeys()`) that parses `Cache-Control: max-age` via regex and defaults to a 1-hour TTL.
2. **Inconsistent Clock Skew and Claim Validation**:
* Neither [`FirebaseTokenVerifier`](https://github.com/firebase/firebase-admin-dart/blob/main/packages/firebase_admin_sdk/lib/src/auth/token_verifier.dart) nor [`AppCheckTokenVerifier`](https://github.com/firebase/firebase-admin-dart/blob/main/packages/firebase_admin_sdk/lib/src/app_check/token_verifier.dart) supports clock-skew tolerance on timestamps (`iat`, `exp`). However, Auth Blocking events in `firebase_functions` require a 5-minute clock skew on `iat`.
* Audience (`aud`) claim handling varies: ID token verification assumes `String`, App Check assumes `List`, and downstream functions must handle both `String` and `List` polymorphically.
3. **Lack of Reusability for Downstream Packages**:
* Because `PublicKeySignatureVerifier` and `JwksFetcher` are either marked `@internal` or lack configuration ergonomics (v3 JWKS endpoints, clock skew, polymorphic audience matching), `firebase_functions` was forced to duplicate the entire JWT verification stack from scratch.
### Proposed Action Items
* [ ] Unify `JwksFetcher` and `UrlKeyFetcher` to share standard HTTP `Cache-Control: max-age` parsing (with a sensible default fallback like 1 hour).
* [ ] Support configurable clock-skew tolerance (e.g., an optional `Duration clockSkew` parameter) in `PublicKeySignatureVerifier` and claim validation.
* [ ] Support polymorphic audience (`aud`) matching (`String` vs. `List`).
* [ ] Expose a clean, reusable JWT verification primitive (or align with an upstream foundation like `google_cloud_server_auth`) so downstream SDKs like `firebase_functions` can delete their custom verifiers and rely directly on `firebase_admin_sdk`.
Contributor guide
Research direction
Start by reading packages/firebase_admin_sdk/lib/src/utils/jwt.dart, especially UrlKeyFetcher, JwksFetcher, and PublicKeySignatureVerifier, then compare the token verifiers and firebase_functions/lib/src/identity/token_verifier.dart. Map the cache, clock-skew, and audience-validation differences before choosing a shared API. Done means downstream SDKs can use one reusable verifier without duplicating key fetching or claim validation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- authentication, backend, security
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100