2020import static com .google .common .base .Preconditions .checkNotNull ;
2121import static com .google .common .base .Preconditions .checkState ;
2222
23- import com .google .api .client .googleapis .auth .oauth2 .GooglePublicKeysManager ;
2423import com .google .api .client .json .JsonFactory ;
2524import com .google .api .client .util .Clock ;
2625import com .google .api .core .ApiFuture ;
3635import com .google .firebase .auth .UserRecord .UpdateRequest ;
3736import com .google .firebase .auth .internal .FirebaseTokenFactory ;
3837import com .google .firebase .auth .internal .FirebaseTokenVerifier ;
38+ import com .google .firebase .auth .internal .KeyManagers ;
3939import com .google .firebase .internal .FirebaseService ;
40+ import com .google .firebase .internal .NonNull ;
4041import com .google .firebase .internal .Nullable ;
4142import com .google .firebase .internal .TaskToApiFuture ;
4243import com .google .firebase .tasks .Task ;
5455 */
5556public class FirebaseAuth {
5657
57- private final GooglePublicKeysManager googlePublicKeysManager ;
5858 private final Clock clock ;
5959
6060 private final FirebaseApp firebaseApp ;
61+ private final KeyManagers keyManagers ;
6162 private final GoogleCredentials credentials ;
6263 private final String projectId ;
6364 private final JsonFactory jsonFactory ;
@@ -66,21 +67,17 @@ public class FirebaseAuth {
6667 private final Object lock ;
6768
6869 private FirebaseAuth (FirebaseApp firebaseApp ) {
69- this (firebaseApp ,
70- FirebaseTokenVerifier .buildGooglePublicKeysManager (
71- firebaseApp .getOptions ().getHttpTransport ()),
72- Clock .SYSTEM );
70+ this (firebaseApp , KeyManagers .getDefault (firebaseApp , Clock .SYSTEM ), Clock .SYSTEM );
7371 }
7472
7573 /**
7674 * Constructor for injecting a GooglePublicKeysManager, which is used to verify tokens are
7775 * correctly signed. This should only be used for testing to override the default key manager.
7876 */
7977 @ VisibleForTesting
80- FirebaseAuth (
81- FirebaseApp firebaseApp , GooglePublicKeysManager googlePublicKeysManager , Clock clock ) {
78+ FirebaseAuth (FirebaseApp firebaseApp , KeyManagers keyManagers , Clock clock ) {
8279 this .firebaseApp = checkNotNull (firebaseApp );
83- this .googlePublicKeysManager = checkNotNull (googlePublicKeysManager );
80+ this .keyManagers = checkNotNull (keyManagers );
8481 this .clock = checkNotNull (clock );
8582 this .credentials = ImplFirebaseTrampolines .getCredentials (firebaseApp );
8683 this .projectId = ImplFirebaseTrampolines .getProjectId (firebaseApp );
@@ -114,6 +111,106 @@ public static synchronized FirebaseAuth getInstance(FirebaseApp app) {
114111 return service .getInstance ();
115112 }
116113
114+ private Task <String > createSessionCookie (
115+ final String idToken , final SessionCookieOptions options ) {
116+ checkNotDestroyed ();
117+ checkArgument (!Strings .isNullOrEmpty (idToken ), "idToken must not be null or empty" );
118+ checkNotNull (options , "options must not be null" );
119+ return call (new Callable <String >() {
120+ @ Override
121+ public String call () throws Exception {
122+ return userManager .createSessionCookie (idToken , options );
123+ }
124+ });
125+ }
126+
127+ /**
128+ * Creates a new Firebase session cookie from the given ID token and options. The returned JWT
129+ * can be set as a server-side session cookie with a custom cookie policy.
130+ *
131+ * @param idToken The Firebase ID token to exchange for a session cookie.
132+ * @param options Additional options required to create the cookie.
133+ * @return An {@code ApiFuture} which will complete successfully with a session cookie string.
134+ * If an error occurs while generating the cookie or if the specified ID token is invalid,
135+ * the future throws a {@link FirebaseAuthException}.
136+ * @throws IllegalArgumentException If the ID token is null or empty, or if options is null.
137+ */
138+ public ApiFuture <String > createSessionCookieAsync (
139+ @ NonNull String idToken , @ NonNull SessionCookieOptions options ) {
140+ return new TaskToApiFuture <>(createSessionCookie (idToken , options ));
141+ }
142+
143+ /**
144+ * Parses and verifies a Firebase session cookie.
145+ *
146+ * <p>If verified successfully, the returned {@code ApiFuture} completes with a parsed version of
147+ * the cookie from which the UID and the other claims can be read. If the cookie is invalid,
148+ * the future throws an exception indicating the failure.
149+ *
150+ * <p>This method does not check whether the cookie has been revoked. See
151+ * {@link #verifySessionCookieAsync(String, boolean)}.
152+ *
153+ * @param cookie A Firebase session cookie string to verify and parse.
154+ * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or
155+ * unsuccessfully with the failure Exception.
156+ */
157+ public ApiFuture <FirebaseToken > verifySessionCookieAsync (String cookie ) {
158+ return new TaskToApiFuture <>(verifySessionCookie (cookie , false ));
159+ }
160+
161+ /**
162+ * Parses and verifies a Firebase session cookie.
163+ *
164+ * <p>If {@code checkRevoked} is true, additionally verifies that the cookie has not been
165+ * revoked.
166+ *
167+ * <p>If verified successfully, the returned {@code ApiFuture} completes with a parsed version of
168+ * the cookie from which the UID and the other claims can be read. If the cookie is invalid or
169+ * has been revoked while {@code checkRevoked} is true, the future throws an exception indicating
170+ * the failure.
171+ *
172+ * @param cookie A Firebase session cookie string to verify and parse.
173+ * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly
174+ * revoked.
175+ * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or
176+ * unsuccessfully with the failure Exception.
177+ */
178+ public ApiFuture <FirebaseToken > verifySessionCookieAsync (String cookie , boolean checkRevoked ) {
179+ return new TaskToApiFuture <>(verifySessionCookie (cookie , checkRevoked ));
180+ }
181+
182+ private Task <FirebaseToken > verifySessionCookie (final String cookie , final boolean checkRevoked ) {
183+ checkNotDestroyed ();
184+ checkState (!Strings .isNullOrEmpty (projectId ),
185+ "Must initialize FirebaseApp with a project ID to call verifySessionCookie()" );
186+ return call (new Callable <FirebaseToken >() {
187+ @ Override
188+ public FirebaseToken call () throws Exception {
189+ FirebaseTokenVerifier firebaseTokenVerifier =
190+ FirebaseTokenVerifier .createSessionCookieVerifier (projectId , keyManagers , clock );
191+ FirebaseToken firebaseToken = FirebaseToken .parse (jsonFactory , cookie );
192+ // This will throw a FirebaseAuthException with details on how the token is invalid.
193+ firebaseTokenVerifier .verifyTokenAndSignature (firebaseToken .getToken ());
194+
195+ if (checkRevoked ) {
196+ checkRevoked (firebaseToken , "session cookie" ,
197+ FirebaseUserManager .SESSION_COOKIE_REVOKED_ERROR );
198+ }
199+ return firebaseToken ;
200+ }
201+ });
202+ }
203+
204+ private void checkRevoked (
205+ FirebaseToken firebaseToken , String label , String errorCode ) throws FirebaseAuthException {
206+ String uid = firebaseToken .getUid ();
207+ UserRecord user = userManager .getUserById (uid );
208+ long issuedAt = (long ) firebaseToken .getClaims ().get ("iat" );
209+ if (user .getTokensValidAfterTimestamp () > issuedAt * 1000 ) {
210+ throw new FirebaseAuthException (errorCode , "Firebase " + label + " revoked" );
211+ }
212+ }
213+
117214 /**
118215 * Similar to {@link #createCustomTokenAsync(String)}, but returns a {@link Task}.
119216 *
@@ -212,27 +309,14 @@ private Task<FirebaseToken> verifyIdToken(final String token, final boolean chec
212309 return call (new Callable <FirebaseToken >() {
213310 @ Override
214311 public FirebaseToken call () throws Exception {
215-
216312 FirebaseTokenVerifier firebaseTokenVerifier =
217- new FirebaseTokenVerifier .Builder ()
218- .setProjectId (projectId )
219- .setPublicKeysManager (googlePublicKeysManager )
220- .setClock (clock )
221- .build ();
222-
313+ FirebaseTokenVerifier .createIdTokenVerifier (projectId , keyManagers , clock );
223314 FirebaseToken firebaseToken = FirebaseToken .parse (jsonFactory , token );
224-
225315 // This will throw a FirebaseAuthException with details on how the token is invalid.
226316 firebaseTokenVerifier .verifyTokenAndSignature (firebaseToken .getToken ());
227-
317+
228318 if (checkRevoked ) {
229- String uid = firebaseToken .getUid ();
230- UserRecord user = userManager .getUserById (uid );
231- long issuedAt = (long ) firebaseToken .getClaims ().get ("iat" );
232- if (user .getTokensValidAfterTimestamp () > issuedAt * 1000 ) {
233- throw new FirebaseAuthException (FirebaseUserManager .ID_TOKEN_REVOKED_ERROR ,
234- "Firebase auth token revoked" );
235- }
319+ checkRevoked (firebaseToken , "auth token" , FirebaseUserManager .ID_TOKEN_REVOKED_ERROR );
236320 }
237321 return firebaseToken ;
238322 }
@@ -241,7 +325,7 @@ public FirebaseToken call() throws Exception {
241325
242326 private Task <Void > revokeRefreshTokens (String uid ) {
243327 checkNotDestroyed ();
244- int currentTimeSeconds = ( int ) ( System .currentTimeMillis () / 1000 ) ;
328+ long currentTimeSeconds = System .currentTimeMillis () / 1000L ;
245329 final UpdateRequest request = new UpdateRequest (uid ).setValidSince (currentTimeSeconds );
246330 return call (new Callable <Void >() {
247331 @ Override
@@ -320,7 +404,7 @@ public ApiFuture<FirebaseToken> verifyIdTokenAsync(final String token) {
320404 * failure.
321405 *
322406 * @param token A Firebase ID Token to verify and parse.
323- * @param checkRevoked A boolean denoting whether to check if the tokens were revoked.
407+ * @param checkRevoked A boolean indicating whether to check if the tokens were revoked.
324408 * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
325409 * unsuccessfully with the failure Exception.
326410 */
0 commit comments