- 
                Notifications
    You must be signed in to change notification settings 
- Fork 407
Defines TenantAwareAuth #551
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
Changes from 2 commits
9bde3e1
              27c9b06
              83d50da
              208d5f5
              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 | 
|---|---|---|
|  | @@ -17,7 +17,9 @@ | |
| import {UserRecord, CreateRequest, UpdateRequest} from './user-record'; | ||
| import {FirebaseApp} from '../firebase-app'; | ||
| import {FirebaseTokenGenerator, CryptoSigner, cryptoSignerFromApp} from './token-generator'; | ||
| import {AuthRequestHandler} from './auth-api-request'; | ||
| import { | ||
| AbstractAuthRequestHandler, AuthRequestHandler, TenantAwareAuthRequestHandler, | ||
| } from './auth-api-request'; | ||
| import {AuthClientErrorCode, FirebaseAuthError, ErrorInfo} from '../utils/error'; | ||
| import {FirebaseServiceInterface, FirebaseServiceInternalsInterface} from '../firebase-service'; | ||
| import { | ||
|  | @@ -32,6 +34,7 @@ import { | |
| AuthProviderConfig, AuthProviderConfigFilter, ListProviderConfigResults, UpdateAuthProviderRequest, | ||
| SAMLConfig, OIDCConfig, OIDCConfigServerResponse, SAMLConfigServerResponse, | ||
| } from './auth-config'; | ||
| import {deepCopy, deepExtend} from '../utils/deep-copy'; | ||
|  | ||
|  | ||
| /** | ||
|  | @@ -72,6 +75,7 @@ export interface DecodedIdToken { | |
| iat: number; | ||
| iss: string; | ||
| sub: string; | ||
| tenant?: string; | ||
| [key: string]: any; | ||
| } | ||
|  | ||
|  | @@ -85,7 +89,7 @@ export interface SessionCookieOptions { | |
| /** | ||
| * Base Auth class. Mainly used for user management APIs. | ||
| */ | ||
| class BaseAuth { | ||
| export class BaseAuth { | ||
| protected readonly tokenGenerator: FirebaseTokenGenerator; | ||
| protected readonly idTokenVerifier: FirebaseTokenVerifier; | ||
| protected readonly sessionCookieVerifier: FirebaseTokenVerifier; | ||
|  | @@ -94,14 +98,14 @@ class BaseAuth { | |
| * The BaseAuth class constructor. | ||
| * | ||
| * @param {string} projectId The corresponding project ID. | ||
| * @param {FirebaseAuthRequestHandler} authRequestHandler The RPC request handler | ||
| * @param {AbstractAuthRequestHandler} authRequestHandler The RPC request handler | ||
| * for this instance. | ||
| * @param {CryptoSigner} cryptoSigner The instance crypto signer used for custom token | ||
| * minting. | ||
| * @constructor | ||
| */ | ||
| constructor(protected readonly projectId: string, | ||
| protected readonly authRequestHandler: AuthRequestHandler, | ||
| protected readonly authRequestHandler: AbstractAuthRequestHandler, | ||
| cryptoSigner: CryptoSigner) { | ||
| this.tokenGenerator = new FirebaseTokenGenerator(cryptoSigner); | ||
| this.sessionCookieVerifier = createSessionCookieVerifier(projectId); | ||
|  | @@ -599,11 +603,126 @@ class BaseAuth { | |
| } | ||
|  | ||
|  | ||
| /** | ||
| * The tenant aware Auth class. | ||
| */ | ||
| export class TenantAwareAuth extends BaseAuth { | ||
| public readonly tenantId: string; | ||
|  | ||
| /** | ||
| * The TenantAwareAuth class constructor. | ||
| * | ||
| * @param {Auth} auth The Auth instance that created this tenant. | ||
| * @param tenantId The corresponding tenant ID. | ||
| * @constructor | ||
| */ | ||
| constructor(private readonly auth: Auth, tenantId: string) { | ||
|         
                  hiranya911 marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| super( | ||
| utils.getProjectId(auth.app), | ||
| new TenantAwareAuthRequestHandler(auth.app, tenantId), | ||
| cryptoSignerFromApp(auth.app)); | ||
| utils.addReadonlyGetter(this, 'tenantId', tenantId); | ||
| 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. Just mark the constructor argument as  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. One problem with this is that if they are not using typescript, they can overwrite this without any error thrown. I tried it and the property can be overwritten. Since this is publicly available, i think we should enforce the readonly. 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. I expected the TS compiler to generate a readonly getter for this, but looks like that only happens if we expose this as a getter in the code. So this is ok. 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. I expected the TS compiler to add a readonly getter, but looks like that only happens if we define a getter explicitly here. So this is ok. | ||
| } | ||
|  | ||
| /** | ||
| * Creates a new custom token that can be sent back to a client to use with | ||
| * signInWithCustomToken(). | ||
| * | ||
| * @param {string} uid The uid to use as the JWT subject. | ||
| * @param {object=} developerClaims Optional additional claims to include in the JWT payload. | ||
| * | ||
| * @return {Promise<string>} A JWT for the provided payload. | ||
| */ | ||
| public createCustomToken(uid: string, developerClaims?: object): Promise<string> { | ||
| // This is not yet supported by the Auth server. It is also not yet determined how this will be | ||
| // supported. | ||
| return Promise.reject( | ||
| new FirebaseAuthError(AuthClientErrorCode.UNSUPPORTED_TENANT_OPERATION)); | ||
| } | ||
|  | ||
| /** | ||
| * Verifies a JWT auth token. Returns a Promise with the tokens claims. Rejects | ||
| * the promise if the token could not be verified. If checkRevoked is set to true, | ||
| * verifies if the session corresponding to the ID token was revoked. If the corresponding | ||
| * user's session was invalidated, an auth/id-token-revoked error is thrown. If not specified | ||
| * the check is not applied. | ||
| * | ||
| * @param {string} idToken The JWT to verify. | ||
| * @param {boolean=} checkRevoked Whether to check if the ID token is revoked. | ||
| * @return {Promise<DecodedIdToken>} A Promise that will be fulfilled after a successful | ||
| * verification. | ||
| */ | ||
| public verifyIdToken(idToken: string, checkRevoked: boolean = false): Promise<DecodedIdToken> { | ||
| return super.verifyIdToken(idToken, checkRevoked) | ||
| .then((decodedClaims) => { | ||
| // Validate tenant ID. | ||
| if (decodedClaims.firebase.tenant !== this.tenantId) { | ||
| throw new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); | ||
| } | ||
| return decodedClaims; | ||
| }); | ||
| } | ||
|  | ||
| /** | ||
| * Creates a new Firebase session cookie with the specified options that can be used for | ||
| * session management (set as a server side session cookie with custom cookie policy). | ||
| * The session cookie JWT will have the same payload claims as the provided ID token. | ||
| * | ||
| * @param {string} idToken The Firebase ID token to exchange for a session cookie. | ||
| * @param {SessionCookieOptions} sessionCookieOptions The session cookie options which includes | ||
| * custom session duration. | ||
| * | ||
| * @return {Promise<string>} A promise that resolves on success with the created session cookie. | ||
| */ | ||
| public createSessionCookie( | ||
| idToken: string, sessionCookieOptions: SessionCookieOptions): Promise<string> { | ||
| // Validate arguments before processing. | ||
| if (!validator.isNonEmptyString(idToken)) { | ||
| return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN)); | ||
| } | ||
| if (!validator.isNonNullObject(sessionCookieOptions) || | ||
| !validator.isNumber(sessionCookieOptions.expiresIn)) { | ||
| return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); | ||
| } | ||
| // This will verify the ID token and then match the tenant ID before creating the session cookie. | ||
| return this.verifyIdToken(idToken) | ||
| .then((decodedIdTokenClaims) => { | ||
| return super.createSessionCookie(idToken, sessionCookieOptions); | ||
| }); | ||
| } | ||
|  | ||
| /** | ||
| * Verifies a Firebase session cookie. Returns a Promise with the tokens claims. Rejects | ||
| * the promise if the token could not be verified. If checkRevoked is set to true, | ||
| * verifies if the session corresponding to the session cookie was revoked. If the corresponding | ||
| * user's session was invalidated, an auth/session-cookie-revoked error is thrown. If not | ||
| * specified the check is not performed. | ||
| * | ||
| * @param {string} sessionCookie The session cookie to verify. | ||
| * @param {boolean=} checkRevoked Whether to check if the session cookie is revoked. | ||
| * @return {Promise<DecodedIdToken>} A Promise that will be fulfilled after a successful | ||
| * verification. | ||
| */ | ||
| public verifySessionCookie( | ||
| sessionCookie: string, checkRevoked: boolean = false): Promise<DecodedIdToken> { | ||
| return super.verifySessionCookie(sessionCookie, checkRevoked) | ||
| .then((decodedClaims) => { | ||
| if (decodedClaims.firebase.tenant !== this.tenantId) { | ||
| throw new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); | ||
| } | ||
| return decodedClaims; | ||
| }); | ||
| } | ||
| } | ||
|  | ||
|  | ||
| /** | ||
| * Auth service bound to the provided app. | ||
| * An Auth instance can have multiple tenants. | ||
| */ | ||
| export class Auth extends BaseAuth implements FirebaseServiceInterface { | ||
| public INTERNAL: AuthInternals = new AuthInternals(); | ||
| private tenantsMap: {[key: string]: TenantAwareAuth}; | ||
|          | ||
| private readonly app_: FirebaseApp; | ||
|  | ||
| /** | ||
|  | @@ -632,6 +751,7 @@ export class Auth extends BaseAuth implements FirebaseServiceInterface { | |
| new AuthRequestHandler(app), | ||
| cryptoSignerFromApp(app)); | ||
| this.app_ = app; | ||
| this.tenantsMap = {}; | ||
| } | ||
|  | ||
| /** | ||
|  | @@ -642,4 +762,20 @@ export class Auth extends BaseAuth implements FirebaseServiceInterface { | |
| get app(): FirebaseApp { | ||
| return this.app_; | ||
| } | ||
|  | ||
| /** | ||
| * Returns a TenantAwareAuth instance for the corresponding tenant ID. | ||
| * | ||
| * @param {string} tenantId The tenant ID whose TenantAwareAuth is to be returned. | ||
| * @return {TenantAwareAuth} The corresponding TenantAwareAuth instance. | ||
| */ | ||
| public forTenant(tenantId: string): TenantAwareAuth { | ||
| if (!validator.isNonEmptyString(tenantId)) { | ||
| throw new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID); | ||
| } | ||
| if (typeof this.tenantsMap[tenantId] === 'undefined') { | ||
| this.tenantsMap[tenantId] = new TenantAwareAuth(this, tenantId); | ||
| } | ||
| return this.tenantsMap[tenantId]; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.