-
Notifications
You must be signed in to change notification settings - Fork 306
feat(pnv): Add support for Firebase Phone Number Verification #1203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9d00629
0fdfa4a
c8b1026
e504376
025e377
e935445
0ba0495
04dab1e
8c9f1d9
91bb749
929335b
969fca5
6e50688
99c58c7
45d46bf
90ce6f4
330aa89
2a6ce31
7ab5389
3e633a1
5ed182c
4ea1d95
ba52bc7
9e36fcc
7f8d585
dd9a434
8feb98d
12a03e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # AI coding assistant guidelines: Firebase Admin Java SDK | ||
|
|
||
| This document defines repository-wide expectations, code styles, and Checkstyle compliance metrics that every autonomous AI coding agent or pair programming assistant must strictly follow when introducing or modifying code in this repository. | ||
|
|
||
| --- | ||
|
|
||
| ## 🎨 Code Style & Checkstyle Compliance | ||
|
|
||
| This repository enforces standard **Google Java Style** guidelines validated through automated Maven checkstyle executions (`checkstyle.xml`). Any compilation or pull request build will fail if these constraints are violated. | ||
|
|
||
| ### 1. Strict 100-Character Line Limit | ||
| * **Rule:** No line of code, comments, Javadoc documentation entries, or inline string literal concatenations may exceed **100 characters** under any circumstances. | ||
| * **Remediation:** | ||
| * Break long method parameters, array initializations, and logic comparisons onto separate lines. | ||
| * Wrap long `assertThrows` statements or lambda chains across multiple contiguous lines. | ||
| * Format long log messages, exception messages, or assertion string explanations using standard string block concatenation broken across separate lines. | ||
|
|
||
| ### 2. Import Ordering & Grouping | ||
| * **Rule:** Imports must be grouped and arranged alphabetically to avoid validation noise. | ||
| * **Remediation:** | ||
| 1. Static imports placed first, grouped, and alphabetically arranged. | ||
| 2. Non-static imports grouped alphabetically by package tier. | ||
| 3. Avoid utilizing wildcard (`*`) imports. Every import declaration must be explicit. | ||
|
|
||
| ### 3. Javadoc Completeness & Formatting | ||
| * **Rule:** Every public class, package-private component interface, constructor, and public method signature must include fully formed Javadoc documentation blocks. | ||
| * **Remediation:** | ||
| * Document all arguments via `@param`, explain error flows via `@throws`, and clear return constraints via `@return`. | ||
| * Wrap documentation description text explicitly so that no single javadoc documentation line passes the 100 characters limit. | ||
|
|
||
| ### 4. Indentation & Spacing | ||
| * **Rule:** Standard indentation uses exactly **2 spaces** per block indentation level. **4 spaces** are used explicitly for wrapped line continuation indentation. | ||
| * **Remediation:** Never utilize tab characters. Ensure block braces follow the Google K&R opening line placement convention. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.firebase.phonenumberverification; | ||
|
|
||
| import com.google.firebase.FirebaseApp; | ||
| import com.google.firebase.ImplFirebaseTrampolines; | ||
| import com.google.firebase.internal.FirebaseService; | ||
| import com.google.firebase.phonenumberverification.internal.FirebasePhoneNumberVerificationTokenVerifier; | ||
|
|
||
| /** | ||
| * This class is the entry point for the Firebase Phone Number Verification service. | ||
| * | ||
| * <p>You can get an instance of {@link FirebasePhoneNumberVerification} via {@link #getInstance()}, | ||
| * or {@link #getInstance(FirebaseApp)} and then use it. | ||
| */ | ||
| public final class FirebasePhoneNumberVerification { | ||
| private static final String SERVICE_ID = FirebasePhoneNumberVerification.class.getName(); | ||
| private final FirebasePhoneNumberVerificationTokenVerifier tokenVerifier; | ||
|
|
||
| private FirebasePhoneNumberVerification(FirebaseApp app) { | ||
| this.tokenVerifier = new FirebasePhoneNumberVerificationTokenVerifier(app); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the {@link FirebasePhoneNumberVerification} instance for the default {@link FirebaseApp}. | ||
| * | ||
| * @return The {@link FirebasePhoneNumberVerification} instance for the default | ||
| * {@link FirebaseApp}. | ||
| */ | ||
| public static FirebasePhoneNumberVerification getInstance() { | ||
| return getInstance(FirebaseApp.getInstance()); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the {@link FirebasePhoneNumberVerification} instance for the specified | ||
| * {@link FirebaseApp}. | ||
| * | ||
| * @return The {@link FirebasePhoneNumberVerification} instance for the specified | ||
| * {@link FirebaseApp}. | ||
| */ | ||
| public static synchronized FirebasePhoneNumberVerification getInstance(FirebaseApp app) { | ||
| FirebasePhoneNumberVerificationService service = | ||
| ImplFirebaseTrampolines.getService(app, SERVICE_ID, | ||
| FirebasePhoneNumberVerificationService.class); | ||
| if (service == null) { | ||
| service = ImplFirebaseTrampolines.addService( | ||
| app, new FirebasePhoneNumberVerificationService(app)); | ||
| } | ||
| return service.getInstance(); | ||
| } | ||
|
|
||
| /** | ||
| * Verifies a Firebase Phone Number Verification token (JWT). | ||
| * | ||
| * @param phoneNumberVerificationJwt The JWT string to verify. | ||
| * @return A verified {@link FirebasePhoneNumberVerificationToken}. | ||
| * @throws FirebasePhoneNumberVerificationException If verification fails. | ||
| */ | ||
| public FirebasePhoneNumberVerificationToken verifyToken(String phoneNumberVerificationJwt) | ||
| throws FirebasePhoneNumberVerificationException { | ||
| return this.tokenVerifier.verifyToken(phoneNumberVerificationJwt); | ||
| } | ||
|
|
||
| private static class FirebasePhoneNumberVerificationService | ||
| extends FirebaseService<FirebasePhoneNumberVerification> { | ||
| FirebasePhoneNumberVerificationService(FirebaseApp app) { | ||
| super(SERVICE_ID, new FirebasePhoneNumberVerification(app)); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,28 @@ | ||||||
| /* | ||||||
| * Copyright 2026 Google LLC | ||||||
| * | ||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| * you may not use this file except in compliance with the License. | ||||||
| * You may obtain a copy of the License at | ||||||
| * | ||||||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
| * | ||||||
| * Unless required by applicable law or agreed to in writing, software | ||||||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
| * See the License for the specific language governing permissions and | ||||||
| * limitations under the License. | ||||||
| */ | ||||||
|
|
||||||
| package com.google.firebase.phonenumberverification; | ||||||
|
|
||||||
| /** | ||||||
| * Error codes that are used in {@link FirebasePhoneNumberVerification}. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should match the error doc sting we use to the other error codes. Something along the lines of:
Suggested change
|
||||||
| */ | ||||||
| public enum FirebasePhoneNumberVerificationErrorCode { | ||||||
| INVALID_ARGUMENT, | ||||||
| INVALID_TOKEN, | ||||||
| TOKEN_EXPIRED, | ||||||
| INTERNAL_ERROR, | ||||||
| SERVICE_ERROR, | ||||||
| } | ||||||
|
Comment on lines
+22
to
+28
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add doc strings to these? |
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.firebase.phonenumberverification; | ||
|
|
||
| import com.google.firebase.ErrorCode; | ||
| import com.google.firebase.FirebaseException; | ||
|
|
||
| /** | ||
| * Generic exception related to Firebase Phone Number Verification. | ||
| * Check the error code and message for more details. | ||
| */ | ||
| public class FirebasePhoneNumberVerificationException extends FirebaseException { | ||
| private final FirebasePhoneNumberVerificationErrorCode errorCode; | ||
|
|
||
| /** | ||
| * Exception that created from {@link FirebasePhoneNumberVerificationErrorCode}, | ||
| * {@link String} message and {@link Throwable} cause. | ||
| * | ||
| * @param errorCode {@link FirebasePhoneNumberVerificationErrorCode} | ||
| * @param message {@link String} | ||
| * @param cause {@link Throwable} | ||
| */ | ||
| public FirebasePhoneNumberVerificationException( | ||
| FirebasePhoneNumberVerificationErrorCode errorCode, | ||
| String message, | ||
| Throwable cause | ||
| ) { | ||
| super(mapToFirebaseError(errorCode), message, cause); | ||
| this.errorCode = errorCode; | ||
| } | ||
|
|
||
| /** | ||
| * Exception that created from {@link FirebasePhoneNumberVerificationErrorCode} | ||
| * and {@link String} message. | ||
| * | ||
| * @param errorCode {@link FirebasePhoneNumberVerificationErrorCode} | ||
| * @param message {@link String} | ||
| */ | ||
| public FirebasePhoneNumberVerificationException( | ||
| FirebasePhoneNumberVerificationErrorCode errorCode, | ||
| String message | ||
| ) { | ||
| this(errorCode, message, null); | ||
| } | ||
|
|
||
| public FirebasePhoneNumberVerificationErrorCode getPhoneNumberVerificationErrorCode() { | ||
| return errorCode; | ||
| } | ||
|
|
||
| private static ErrorCode mapToFirebaseError(FirebasePhoneNumberVerificationErrorCode code) { | ||
| if (code == null) { | ||
| return ErrorCode.INTERNAL; | ||
| } | ||
| switch (code) { | ||
| case INVALID_ARGUMENT: | ||
| return ErrorCode.INVALID_ARGUMENT; | ||
| case TOKEN_EXPIRED: | ||
| case INVALID_TOKEN: | ||
| return ErrorCode.UNAUTHENTICATED; | ||
| case SERVICE_ERROR: | ||
| return ErrorCode.UNAVAILABLE; | ||
| case INTERNAL_ERROR: | ||
| default: | ||
| return ErrorCode.INTERNAL; | ||
| } | ||
| } | ||
|
Comment on lines
+64
to
+80
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we are using a different pattern for determining the base Firebase Error code (Using a map rather than defining it manually) than what we use for the other services. I don't have a strong opinion against it since it think this it makes modifying these easier. But wanted to get your thoughts on it. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.firebase.phonenumberverification; | ||
|
|
||
| import static com.google.common.base.Preconditions.checkArgument; | ||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
|
|
||
| import com.google.common.collect.ImmutableList; | ||
| import com.google.common.collect.ImmutableMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * Represents a verified Firebase Phone Number Verification token. | ||
| */ | ||
| public class FirebasePhoneNumberVerificationToken { | ||
| private final Map<String, Object> claims; | ||
|
|
||
| /** | ||
| * Create an instance of {@link FirebasePhoneNumberVerificationToken} from a map of JWT claims. | ||
| * | ||
| * @param claims A map of JWT claims. | ||
| */ | ||
| public FirebasePhoneNumberVerificationToken(Map<String, Object> claims) { | ||
| checkNotNull(claims, "Claims map must not be null"); | ||
| checkArgument(claims.containsKey("sub"), "Claims map must contain sub"); | ||
| this.claims = ImmutableMap.copyOf(claims); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the issuer identifier for the issuer of the response. | ||
| */ | ||
| public String getIssuer() { | ||
| return (String) claims.get("iss"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the phone number of the user. | ||
| * This corresponds to the 'sub' claim in the JWT. | ||
| */ | ||
| public String getPhoneNumber() { | ||
| return (String) claims.get("sub"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the audience for which this token is intended. | ||
| */ | ||
| public List<String> getAudience() { | ||
| Object audience = claims.get("aud"); | ||
| if (audience instanceof String) { | ||
| return ImmutableList.of((String) audience); | ||
| } else if (audience instanceof List) { | ||
| @SuppressWarnings("unchecked") | ||
| List<String> audienceList = (List<String>) audience; | ||
| return ImmutableList.copyOf(audienceList); | ||
| } | ||
| return ImmutableList.of(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the expiration time in seconds since the Unix epoch. | ||
| */ | ||
| public long getExpirationTime() { | ||
| 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() { | ||
| 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; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the entire map of claims. | ||
| */ | ||
| public Map<String, Object> getClaims() { | ||
| return claims; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we bump this to latest 10.9?