-
Notifications
You must be signed in to change notification settings - Fork 362
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Auth Emulator (#1044)
* Basic URL replacement and custom tokens * Fix test * Tests actually pass * Mock verifyIdToken * Add another test * Small style change * Small simplification * Significant cleanup of all the useEmulator stuff * Make sure we don't use env var to short-circuit ID Token verification * Make lint pass * Add unit tests that go through the Auth interface * Hiranya nits * Make private method even more private and scary, require env * Make the private method throw * Fix test
- Loading branch information
Showing
10 changed files
with
303 additions
and
54 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,9 +21,12 @@ import { AuthorizedHttpClient, HttpError, HttpRequestConfig, HttpClient } from ' | |
|
||
import * as validator from '../utils/validator'; | ||
import { toWebSafeBase64 } from '../utils'; | ||
import { Algorithm } from 'jsonwebtoken'; | ||
|
||
|
||
const ALGORITHM_RS256 = 'RS256'; | ||
const ALGORITHM_RS256: Algorithm = 'RS256' as const; | ||
const ALGORITHM_NONE: Algorithm = 'none' as const; | ||
|
||
const ONE_HOUR_IN_SECONDS = 60 * 60; | ||
|
||
// List of blacklisted claims which cannot be provided when creating a custom token | ||
|
@@ -39,6 +42,12 @@ const FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identit | |
* CryptoSigner interface represents an object that can be used to sign JWTs. | ||
*/ | ||
export interface CryptoSigner { | ||
|
||
/** | ||
* The name of the signing algorithm. | ||
*/ | ||
readonly algorithm: Algorithm; | ||
|
||
/** | ||
* Cryptographically signs a buffer of data. | ||
* | ||
|
@@ -82,6 +91,8 @@ interface JWTBody { | |
* sign data. Performs all operations locally, and does not make any RPC calls. | ||
*/ | ||
export class ServiceAccountSigner implements CryptoSigner { | ||
|
||
algorithm = ALGORITHM_RS256; | ||
|
||
/** | ||
* Creates a new CryptoSigner instance from the given service account credential. | ||
|
@@ -124,6 +135,8 @@ export class ServiceAccountSigner implements CryptoSigner { | |
* @see https://cloud.google.com/compute/docs/storing-retrieving-metadata | ||
*/ | ||
export class IAMSigner implements CryptoSigner { | ||
algorithm = ALGORITHM_RS256; | ||
|
||
private readonly httpClient: AuthorizedHttpClient; | ||
private serviceAccountId?: string; | ||
|
||
|
@@ -215,6 +228,29 @@ export class IAMSigner implements CryptoSigner { | |
} | ||
} | ||
|
||
/** | ||
* A CryptoSigner implementation that is used when communicating with the Auth emulator. | ||
* It produces unsigned tokens. | ||
*/ | ||
export class EmulatedSigner implements CryptoSigner { | ||
|
||
algorithm = ALGORITHM_NONE; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public sign(_: Buffer): Promise<Buffer> { | ||
return Promise.resolve(Buffer.from('')); | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public getAccountId(): Promise<string> { | ||
return Promise.resolve('[email protected]'); | ||
} | ||
} | ||
|
||
/** | ||
* Create a new CryptoSigner instance for the given app. If the app has been initialized with a service | ||
* account credential, creates a ServiceAccountSigner. Otherwise creates an IAMSigner. | ||
|
@@ -250,7 +286,7 @@ export class FirebaseTokenGenerator { | |
'INTERNAL ASSERT: Must provide a CryptoSigner to use FirebaseTokenGenerator.', | ||
); | ||
} | ||
if (typeof tenantId !== 'undefined' && !validator.isNonEmptyString(tenantId)) { | ||
if (typeof this.tenantId !== 'undefined' && !validator.isNonEmptyString(this.tenantId)) { | ||
throw new FirebaseAuthError( | ||
AuthClientErrorCode.INVALID_ARGUMENT, | ||
'`tenantId` argument must be a non-empty string.'); | ||
|
@@ -298,7 +334,7 @@ export class FirebaseTokenGenerator { | |
} | ||
return this.signer.getAccountId().then((account) => { | ||
const header: JWTHeader = { | ||
alg: ALGORITHM_RS256, | ||
alg: this.signer.algorithm, | ||
typ: 'JWT', | ||
}; | ||
const iat = Math.floor(Date.now() / 1000); | ||
|
@@ -319,6 +355,7 @@ export class FirebaseTokenGenerator { | |
} | ||
const token = `${this.encodeSegment(header)}.${this.encodeSegment(body)}`; | ||
const signPromise = this.signer.sign(Buffer.from(token)); | ||
|
||
return Promise.all([token, signPromise]); | ||
}).then(([token, signature]) => { | ||
return `${token}.${this.encodeSegment(signature)}`; | ||
|
Oops, something went wrong.