@@ -19,7 +19,7 @@ import {Certificate} from './credential';
1919import { FirebaseApp } from '../firebase-app' ;
2020import { FirebaseTokenGenerator } from './token-generator' ;
2121import { FirebaseAuthRequestHandler } from './auth-api-request' ;
22- import { AuthClientErrorCode , FirebaseAuthError } from '../utils/error' ;
22+ import { AuthClientErrorCode , FirebaseAuthError , ErrorInfo } from '../utils/error' ;
2323import { FirebaseServiceInterface , FirebaseServiceInternalsInterface } from '../firebase-service' ;
2424import {
2525 UserImportOptions , UserImportRecord , UserImportResult ,
@@ -51,7 +51,7 @@ export interface ListUsersResult {
5151}
5252
5353
54- /** Inteface representing a decoded ID token. */
54+ /** Interface representing a decoded ID token. */
5555export interface DecodedIdToken {
5656 aud : string ;
5757 auth_time : number ;
@@ -70,6 +70,12 @@ export interface DecodedIdToken {
7070}
7171
7272
73+ /** Interface representing the session cookie options. */
74+ export interface SessionCookieOptions {
75+ expiresIn : number ;
76+ }
77+
78+
7379/**
7480 * Auth service bound to the provided app.
7581 */
@@ -171,23 +177,9 @@ export class Auth implements FirebaseServiceInterface {
171177 if ( ! checkRevoked ) {
172178 return decodedIdToken ;
173179 }
174- // Get tokens valid after time for the corresponding user.
175- return this . getUser ( decodedIdToken . sub )
176- . then ( ( user : UserRecord ) => {
177- // If no tokens valid after time available, token is not revoked.
178- if ( user . tokensValidAfterTime ) {
179- // Get the ID token authentication time and convert to milliseconds UTC.
180- const authTimeUtc = decodedIdToken . auth_time * 1000 ;
181- // Get user tokens valid after time in milliseconds UTC.
182- const validSinceUtc = new Date ( user . tokensValidAfterTime ) . getTime ( ) ;
183- // Check if authentication time is older than valid since time.
184- if ( authTimeUtc < validSinceUtc ) {
185- throw new FirebaseAuthError ( AuthClientErrorCode . ID_TOKEN_REVOKED ) ;
186- }
187- }
188- // All checks above passed. Return the decoded token.
189- return decodedIdToken ;
190- } ) ;
180+ return this . verifyDecodedJWTNotRevoked (
181+ decodedIdToken ,
182+ AuthClientErrorCode . ID_TOKEN_REVOKED ) ;
191183 } ) ;
192184 }
193185
@@ -371,4 +363,90 @@ export class Auth implements FirebaseServiceInterface {
371363 users : UserImportRecord [ ] , options ?: UserImportOptions ) : Promise < UserImportResult > {
372364 return this . authRequestHandler . uploadAccount ( users , options ) ;
373365 }
366+
367+ /**
368+ * Creates a new Firebase session cookie with the specified options that can be used for
369+ * session management (set as a server side session cookie with custom cookie policy).
370+ * The session cookie JWT will have the same payload claims as the provided ID token.
371+ *
372+ * @param {string } idToken The Firebase ID token to exchange for a session cookie.
373+ * @param {SessionCookieOptions } sessionCookieOptions The session cookie options which includes
374+ * custom session duration.
375+ *
376+ * @return {Promise<string> } A promise that resolves on success with the created session cookie.
377+ */
378+ public createSessionCookie (
379+ idToken : string , sessionCookieOptions : SessionCookieOptions ) : Promise < string > {
380+ // Return rejected promise if expiresIn is not available.
381+ if ( ! validator . isNonNullObject ( sessionCookieOptions ) ||
382+ ! validator . isNumber ( sessionCookieOptions . expiresIn ) ) {
383+ return Promise . reject ( new FirebaseAuthError ( AuthClientErrorCode . INVALID_SESSION_COOKIE_DURATION ) ) ;
384+ }
385+ return this . authRequestHandler . createSessionCookie (
386+ idToken , sessionCookieOptions . expiresIn ) ;
387+ }
388+
389+ /**
390+ * Verifies a Firebase session cookie. Returns a Promise with the tokens claims. Rejects
391+ * the promise if the token could not be verified. If checkRevoked is set to true,
392+ * verifies if the session corresponding to the session cookie was revoked. If the corresponding
393+ * user's session was invalidated, an auth/session-cookie-revoked error is thrown. If not
394+ * specified the check is not performed.
395+ *
396+ * @param {string } sessionCookie The session cookie to verify.
397+ * @param {boolean= } checkRevoked Whether to check if the session cookie is revoked.
398+ * @return {Promise<DecodedIdToken> } A Promise that will be fulfilled after a successful
399+ * verification.
400+ */
401+ public verifySessionCookie (
402+ sessionCookie : string , checkRevoked : boolean = false ) : Promise < DecodedIdToken > {
403+ if ( typeof this . tokenGenerator_ === 'undefined' ) {
404+ throw new FirebaseAuthError (
405+ AuthClientErrorCode . INVALID_CREDENTIAL ,
406+ 'Must initialize app with a cert credential or set your Firebase project ID as the ' +
407+ 'GCLOUD_PROJECT environment variable to call auth().verifySessionCookie().' ,
408+ ) ;
409+ }
410+ return this . tokenGenerator_ . verifySessionCookie ( sessionCookie )
411+ . then ( ( decodedIdToken : DecodedIdToken ) => {
412+ // Whether to check if the token was revoked.
413+ if ( ! checkRevoked ) {
414+ return decodedIdToken ;
415+ }
416+ return this . verifyDecodedJWTNotRevoked (
417+ decodedIdToken ,
418+ AuthClientErrorCode . SESSION_COOKIE_REVOKED ) ;
419+ } ) ;
420+ }
421+
422+ /**
423+ * Verifies the decoded Firebase issued JWT is not revoked. Returns a promise that resolves
424+ * with the decoded claims on success. Rejects the promise with revocation error if revoked.
425+ *
426+ * @param {DecodedIdToken } decodedIdToken The JWT's decoded claims.
427+ * @param {ErrorInfo } revocationErrorInfo The revocation error info to throw on revocation
428+ * detection.
429+ * @return {Promise<DecodedIdToken> } A Promise that will be fulfilled after a successful
430+ * verification.
431+ */
432+ private verifyDecodedJWTNotRevoked (
433+ decodedIdToken : DecodedIdToken , revocationErrorInfo : ErrorInfo ) : Promise < DecodedIdToken > {
434+ // Get tokens valid after time for the corresponding user.
435+ return this . getUser ( decodedIdToken . sub )
436+ . then ( ( user : UserRecord ) => {
437+ // If no tokens valid after time available, token is not revoked.
438+ if ( user . tokensValidAfterTime ) {
439+ // Get the ID token authentication time and convert to milliseconds UTC.
440+ const authTimeUtc = decodedIdToken . auth_time * 1000 ;
441+ // Get user tokens valid after time in milliseconds UTC.
442+ const validSinceUtc = new Date ( user . tokensValidAfterTime ) . getTime ( ) ;
443+ // Check if authentication time is older than valid since time.
444+ if ( authTimeUtc < validSinceUtc ) {
445+ throw new FirebaseAuthError ( revocationErrorInfo ) ;
446+ }
447+ }
448+ // All checks above passed. Return the decoded token.
449+ return decodedIdToken ;
450+ } ) ;
451+ }
374452}
0 commit comments