feat(auth): Add Firebase Phone Number Verification support with verify_token()#953
feat(auth): Add Firebase Phone Number Verification support with verify_token()#953codeprakhar25 wants to merge 1 commit into
Conversation
Added firebase_admin/phone_number_verification.py with a module-level verify_token() function and supporting PhoneNumberVerificationService. The service fetches public keys from the FPNV JWKS endpoint and verifies ES256-signed JWTs, validating the kid/typ/alg headers, issuer, audience, expiry, and phone-number subject claim. Also added tests/test_phone_number_verification.py with 32 unit tests covering all validation paths, including an end-to-end test that signs a real JWT with a generated EC key pair and mocks only the JWKS fetch.
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
There was a problem hiding this comment.
Code Review
This pull request introduces the Firebase Phone Number Verification module (firebase_admin/phone_number_verification.py) along with its corresponding test suite (tests/test_phone_number_verification.py). The new module provides functionality to verify JWTs issued by the Firebase Phone Number Verification service. Feedback on the changes highlights two main improvements: explicitly enabling cache_jwk_set=True in PyJWKClient to ensure token caching works as intended and avoids excessive network requests, and refactoring the check_string validator method from a @classmethod to a @staticmethod since it does not access class-level state.
| 'or set the GOOGLE_CLOUD_PROJECT environment variable.') | ||
| self._expected_issuer = _ISSUER_PREFIX + self._project_id | ||
| # Cache JWKS for up to 6 hours (21600 seconds) to reduce network overhead. | ||
| self._jwks_client = PyJWKClient(_JWKS_URL, lifespan=21600) |
There was a problem hiding this comment.
In PyJWT, the PyJWKClient constructor defaults cache_jwk_set to False. When cache_jwk_set is False, the client will fetch the JWK set from the network on every single call to get_signing_key_from_jwt(), completely ignoring the lifespan parameter.\n\nTo actually enable caching and prevent excessive network requests to the Google JWKS endpoint, you must explicitly pass cache_jwk_set=True.
| self._jwks_client = PyJWKClient(_JWKS_URL, lifespan=21600) | |
| self._jwks_client = PyJWKClient(_JWKS_URL, cache_jwk_set=True, lifespan=21600) |
| @classmethod | ||
| def check_string(cls, label: str, value: Any): |
There was a problem hiding this comment.
|
Closing — this PR was opened by mistake by an automated experiment harness and is not a genuine contribution. Apologies for the noise. |
Summary
This PR adds the
firebase_admin.phone_number_verificationmodule, implementing JWT verification for the Firebase Phone Number Verification (FPNV) service. It continues the work from PR #934.Problem
Server-side SDKs had no way to verify FPNV JWTs issued to clients after phone number verification, making it impossible to trust client-supplied phone numbers without a round-trip to a Firebase REST endpoint.
Solution
Added a new
phone_number_verificationmodule following the exact patterns established byapp_check.py:verify_token(token, app=None)— module-level function that validates and decodes an FPNV JWT.PhoneNumberVerificationToken— adictsubclass wrapping the decoded claims with convenience properties (phone_number,issuer,audience,exp,iat)._PhoneNumberVerificationService— internal service class that manages a cachedPyJWKClient(6-hour TTL) and performs header/claim validation._Validators— reusable string-validation utilities.Key technical details
firebaseappcheck.googleapis.com/v1/jwksfpnv.googleapis.com/v1beta/jwkshttps://firebaseappcheck.googleapis.com/)https://fpnv.googleapis.com/projects/{id})projects/{id}kidmust be presentThe
issueris also passed tojwt.decode()so PyJWT validates theissclaim automatically, giving a preciseInvalidIssuerErrorrather than a generic one.Testing
Added
tests/test_phone_number_verification.pywith 32 unit tests:PhoneNumberVerificationToken— property accessors, dict behaviour, missing-claim defaults._PhoneNumberVerificationService— missing project ID, all non-string/empty-string token inputs, every header validation branch (kid,typ,alg), every_decode_and_verifyerror path (expired, bad signature, wrong audience, wrong issuer, malformed token, None/non-string/emptysub).verify_token()— round-trip success, malformed JWT, header rejection.fetch_datacall, and verifies the full decoding path.All 32 tests pass and both files score 10.00/10 with pylint.
Context Sources Used