@@ -19,7 +19,7 @@ import {Certificate} from './credential';
19
19
import { FirebaseApp } from '../firebase-app' ;
20
20
import { FirebaseTokenGenerator } from './token-generator' ;
21
21
import { FirebaseAuthRequestHandler } from './auth-api-request' ;
22
- import { AuthClientErrorCode , FirebaseAuthError } from '../utils/error' ;
22
+ import { AuthClientErrorCode , FirebaseAuthError , ErrorInfo } from '../utils/error' ;
23
23
import { FirebaseServiceInterface , FirebaseServiceInternalsInterface } from '../firebase-service' ;
24
24
import {
25
25
UserImportOptions , UserImportRecord , UserImportResult ,
@@ -51,7 +51,7 @@ export interface ListUsersResult {
51
51
}
52
52
53
53
54
- /** Inteface representing a decoded ID token. */
54
+ /** Interface representing a decoded ID token. */
55
55
export interface DecodedIdToken {
56
56
aud : string ;
57
57
auth_time : number ;
@@ -70,6 +70,12 @@ export interface DecodedIdToken {
70
70
}
71
71
72
72
73
+ /** Interface representing the session cookie options. */
74
+ export interface SessionCookieOptions {
75
+ expiresIn : number ;
76
+ }
77
+
78
+
73
79
/**
74
80
* Auth service bound to the provided app.
75
81
*/
@@ -171,23 +177,9 @@ export class Auth implements FirebaseServiceInterface {
171
177
if ( ! checkRevoked ) {
172
178
return decodedIdToken ;
173
179
}
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 ) ;
191
183
} ) ;
192
184
}
193
185
@@ -371,4 +363,90 @@ export class Auth implements FirebaseServiceInterface {
371
363
users : UserImportRecord [ ] , options ?: UserImportOptions ) : Promise < UserImportResult > {
372
364
return this . authRequestHandler . uploadAccount ( users , options ) ;
373
365
}
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
+ }
374
452
}
0 commit comments