feat(pnv): Add support for Firebase Phone Number Verification#1203
feat(pnv): Add support for Firebase Phone Number Verification#1203lahirumaramba wants to merge 19 commits intomainfrom
Conversation
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request introduces the Firebase Phone Number Verification service, implementing JWT token verification using the Nimbus JOSE + JWT library. It includes the core service entry point, model classes for verified tokens, custom exception handling, and a verifier that checks JWT headers and claims against a remote JWKS. Feedback highlights a security vulnerability where the issuer claim was not validated against the specific project ID, potentially allowing tokens from other projects. Additionally, improvements were suggested for the token model to handle claim types more robustly and prevent potential class cast exceptions.
| if (claims.getAudience().isEmpty() || !claims.getAudience().contains(issuer)) { | ||
| throw new FirebasePhoneNumberVerificationException(FirebasePhoneNumberVerificationErrorCode.INVALID_TOKEN, | ||
| "Invalid audience. Expected to contain: " + issuer + " but found: " + claims.getAudience() | ||
| ); | ||
| } |
There was a problem hiding this comment.
The token verification logic should validate that the iss (issuer) claim matches the expected value for the current project (https://fpnv.googleapis.com/projects/{projectId}). Since the JWKS is shared across all Firebase projects, failing to verify the issuer allows tokens from other projects to be accepted, which is a security vulnerability.
String expectedIssuer = "https://fpnv.googleapis.com/projects/" + projectId;
if (!expectedIssuer.equals(issuer)) {
throw new FirebasePhoneNumberVerificationException(FirebasePhoneNumberVerificationErrorCode.INVALID_TOKEN,
"Invalid issuer. Expected: " + expectedIssuer + " but found: " + issuer);
}
if (claims.getAudience().isEmpty() || !claims.getAudience().contains(issuer)) {
throw new FirebasePhoneNumberVerificationException(FirebasePhoneNumberVerificationErrorCode.INVALID_TOKEN,
"Invalid audience. Expected to contain: " + issuer + " but found: " + claims.getAudience()
);
}| * Returns the expiration time in seconds since the Unix epoch. | ||
| */ | ||
| public long getExpirationTime() { | ||
| return ((java.util.Date) claims.get("exp")).getTime() / 1000L; |
There was a problem hiding this comment.
Casting the exp claim directly to java.util.Date is fragile. While the Nimbus library typically returns Date objects, this class is public and could be instantiated with a map where these values are Long or Integer (e.g., if parsed from raw JSON). It's safer to handle both Date and Number types to avoid potential ClassCastException or NullPointerException.
Object exp = claims.get("exp");
if (exp instanceof java.util.Date) {
return ((java.util.Date) exp).getTime() / 1000L;
}
return exp instanceof Number ? ((Number) exp).longValue() : 0L;| * Returns the issued-at time in seconds since the Unix epoch. | ||
| */ | ||
| public long getIssuedAt() { | ||
| return ((java.util.Date) claims.get("iat")).getTime() / 1000L; |
There was a problem hiding this comment.
Similar to the exp claim, the iat claim should be handled robustly to avoid potential ClassCastException or NullPointerException if the claim is missing or represented as a numeric type.
Object iat = claims.get("iat");
if (iat instanceof java.util.Date) {
return ((java.util.Date) iat).getTime() / 1000L;
}
return iat instanceof Number ? ((Number) iat).longValue() : 0L;…abilize headless pipelines
Add support for Firebase Phone Number Verification