diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 03a346aa4a..0464f9c959 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -9,6 +9,21 @@ import { Bucket } from '@google-cloud/storage'; import * as _firestore from '@google-cloud/firestore'; import * as rtdb from '@firebase/database-types'; +// @public +export interface ActionCodeSettings { + android?: { + packageName: string; + installApp?: boolean; + minimumVersion?: string; + }; + dynamicLinkDomain?: string; + handleCodeInApp?: boolean; + iOS?: { + bundleId: string; + }; + url: string; +} + // @public (undocumented) export interface App { name: string; @@ -63,345 +78,189 @@ export interface AppOptions { export const apps: (app.App | null)[]; // @public -export function auth(app?: app.App): auth.Auth; +export class Auth extends BaseAuth { + get app(): App; + tenantManager(): TenantManager; + } + +// @public +export function auth(app?: App): Auth; // @public (undocumented) export namespace auth { - export interface ActionCodeSettings { - android?: { - packageName: string; - installApp?: boolean; - minimumVersion?: string; - }; - dynamicLinkDomain?: string; - handleCodeInApp?: boolean; - iOS?: { - bundleId: string; - }; - url: string; - } // (undocumented) - export interface Auth extends BaseAuth { - // (undocumented) - app: app.App; - tenantManager(): TenantManager; - } - export type AuthFactorType = 'phone'; - export interface AuthProviderConfig { - displayName?: string; - enabled: boolean; - providerId: string; - } - export interface AuthProviderConfigFilter { - maxResults?: number; - pageToken?: string; - type: 'saml' | 'oidc'; - } - // (undocumented) - export interface BaseAuth { - createCustomToken(uid: string, developerClaims?: object): Promise; - createProviderConfig(config: AuthProviderConfig): Promise; - createSessionCookie(idToken: string, sessionCookieOptions: SessionCookieOptions): Promise; - createUser(properties: CreateRequest): Promise; - deleteProviderConfig(providerId: string): Promise; - deleteUser(uid: string): Promise; - deleteUsers(uids: string[]): Promise; - generateEmailVerificationLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise; - generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise; - generateSignInWithEmailLink(email: string, actionCodeSettings: ActionCodeSettings): Promise; - getProviderConfig(providerId: string): Promise; - getUser(uid: string): Promise; - getUserByEmail(email: string): Promise; - getUserByPhoneNumber(phoneNumber: string): Promise; - getUsers(identifiers: UserIdentifier[]): Promise; - importUsers(users: UserImportRecord[], options?: UserImportOptions): Promise; - listProviderConfigs(options: AuthProviderConfigFilter): Promise; - listUsers(maxResults?: number, pageToken?: string): Promise; - revokeRefreshTokens(uid: string): Promise; - setCustomUserClaims(uid: string, customUserClaims: object | null): Promise; - updateProviderConfig(providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise; - updateUser(uid: string, properties: UpdateRequest): Promise; - verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; - verifySessionCookie(sessionCookie: string, checkForRevocation?: boolean): Promise; - } - export interface CreateMultiFactorInfoRequest { - displayName?: string; - factorId: string; - } - export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { - phoneNumber: string; - } - export interface CreateRequest extends UpdateRequest { - multiFactor?: MultiFactorCreateSettings; - uid?: string; - } - export type CreateTenantRequest = UpdateTenantRequest; - export interface DecodedIdToken { - // (undocumented) - [key: string]: any; - aud: string; - auth_time: number; - email?: string; - email_verified?: boolean; - exp: number; - firebase: { - identities: { - [key: string]: any; - }; - sign_in_provider: string; - sign_in_second_factor?: string; - second_factor_identifier?: string; - tenant?: string; - [key: string]: any; - }; - iat: number; - iss: string; - phone_number?: string; - picture?: string; - sub: string; - uid: string; - } - export interface DeleteUsersResult { - errors: FirebaseArrayIndexError[]; - failureCount: number; - successCount: number; - } - export interface EmailIdentifier { - // (undocumented) - email: string; - } - export interface EmailSignInProviderConfig { - enabled: boolean; - passwordRequired?: boolean; - } - export interface GetUsersResult { - notFound: UserIdentifier[]; - users: UserRecord[]; - } + export type ActionCodeSettings = ActionCodeSettings; // (undocumented) - export type HashAlgorithmType = 'SCRYPT' | 'STANDARD_SCRYPT' | 'HMAC_SHA512' | 'HMAC_SHA256' | 'HMAC_SHA1' | 'HMAC_MD5' | 'MD5' | 'PBKDF_SHA1' | 'BCRYPT' | 'PBKDF2_SHA256' | 'SHA512' | 'SHA256' | 'SHA1'; - export interface ListProviderConfigResults { - pageToken?: string; - providerConfigs: AuthProviderConfig[]; - } - export interface ListTenantsResult { - pageToken?: string; - tenants: Tenant[]; - } - export interface ListUsersResult { - pageToken?: string; - users: UserRecord[]; - } - export interface MultiFactorConfig { - factorIds?: AuthFactorType[]; - state: MultiFactorConfigState; - } - export type MultiFactorConfigState = 'ENABLED' | 'DISABLED'; - export interface MultiFactorCreateSettings { - enrolledFactors: CreateMultiFactorInfoRequest[]; - } - export interface MultiFactorInfo { - displayName?: string; - enrollmentTime?: string; - factorId: string; - toJSON(): object; - uid: string; - } - export interface MultiFactorSettings { - enrolledFactors: MultiFactorInfo[]; - toJSON(): object; - } - export interface MultiFactorUpdateSettings { - enrolledFactors: UpdateMultiFactorInfoRequest[] | null; - } - export interface OIDCAuthProviderConfig extends AuthProviderConfig { - clientId: string; - issuer: string; - } - export interface OIDCUpdateAuthProviderRequest { - clientId?: string; - displayName?: string; - enabled?: boolean; - issuer?: string; - } - export interface PhoneIdentifier { - // (undocumented) - phoneNumber: string; - } - export interface PhoneMultiFactorInfo extends MultiFactorInfo { - phoneNumber: string; - } - export interface ProviderIdentifier { - // (undocumented) - providerId: string; - // (undocumented) - providerUid: string; - } - export interface SAMLAuthProviderConfig extends AuthProviderConfig { - callbackURL?: string; - idpEntityId: string; - rpEntityId: string; - ssoURL: string; - x509Certificates: string[]; - } - export interface SAMLUpdateAuthProviderRequest { - callbackURL?: string; - displayName?: string; - enabled?: boolean; - idpEntityId?: string; - rpEntityId?: string; - ssoURL?: string; - x509Certificates?: string[]; - } - export interface SessionCookieOptions { - expiresIn: number; - } - export interface Tenant { - displayName?: string; - emailSignInConfig?: { - enabled: boolean; - passwordRequired?: boolean; - }; - multiFactorConfig?: MultiFactorConfig; - tenantId: string; - testPhoneNumbers?: { - [phoneNumber: string]: string; - }; - toJSON(): object; - } - export interface TenantAwareAuth extends BaseAuth { - tenantId: string; - } - export interface TenantManager { - // (undocumented) - authForTenant(tenantId: string): TenantAwareAuth; - createTenant(tenantOptions: CreateTenantRequest): Promise; - deleteTenant(tenantId: string): Promise; - getTenant(tenantId: string): Promise; - listTenants(maxResults?: number, pageToken?: string): Promise; - updateTenant(tenantId: string, tenantOptions: UpdateTenantRequest): Promise; - } - export interface UidIdentifier { - // (undocumented) - uid: string; - } + export type Auth = Auth; // (undocumented) - export type UpdateAuthProviderRequest = SAMLUpdateAuthProviderRequest | OIDCUpdateAuthProviderRequest; - export interface UpdateMultiFactorInfoRequest { - displayName?: string; - enrollmentTime?: string; - factorId: string; - uid?: string; - } - export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { - phoneNumber: string; - } - export interface UpdateRequest { - disabled?: boolean; - displayName?: string | null; - email?: string; - emailVerified?: boolean; - multiFactor?: MultiFactorUpdateSettings; - password?: string; - phoneNumber?: string | null; - photoURL?: string | null; - } - export interface UpdateTenantRequest { - displayName?: string; - emailSignInConfig?: EmailSignInProviderConfig; - multiFactorConfig?: MultiFactorConfig; - testPhoneNumbers?: { - [phoneNumber: string]: string; - } | null; - } - export type UserIdentifier = UidIdentifier | EmailIdentifier | PhoneIdentifier | ProviderIdentifier; - export interface UserImportOptions { - hash: { - algorithm: HashAlgorithmType; - key?: Buffer; - saltSeparator?: Buffer; - rounds?: number; - memoryCost?: number; - parallelization?: number; - blockSize?: number; - derivedKeyLength?: number; - }; - } - export interface UserImportRecord { - customClaims?: { - [key: string]: any; - }; - disabled?: boolean; - displayName?: string; - email?: string; - emailVerified?: boolean; - metadata?: UserMetadataRequest; - multiFactor?: MultiFactorUpdateSettings; - passwordHash?: Buffer; - passwordSalt?: Buffer; - phoneNumber?: string; - photoURL?: string; - providerData?: UserProviderRequest[]; - tenantId?: string; - uid: string; - } - export interface UserImportResult { - errors: FirebaseArrayIndexError[]; - failureCount: number; - successCount: number; - } - export interface UserInfo { - displayName: string; - email: string; - phoneNumber: string; - photoURL: string; - providerId: string; - toJSON(): object; - uid: string; - } - export interface UserMetadata { - creationTime: string; - lastRefreshTime?: string | null; - lastSignInTime: string; - toJSON(): object; - } - export interface UserMetadataRequest { - creationTime?: string; - lastSignInTime?: string; - } - export interface UserProviderRequest { - displayName?: string; - email?: string; - phoneNumber?: string; - photoURL?: string; - providerId: string; - uid: string; - } - export interface UserRecord { - customClaims?: { - [key: string]: any; - }; - disabled: boolean; - displayName?: string; - email?: string; - emailVerified: boolean; - metadata: UserMetadata; - multiFactor?: MultiFactorSettings; - passwordHash?: string; - passwordSalt?: string; - phoneNumber?: string; - photoURL?: string; - providerData: UserInfo[]; - tenantId?: string | null; - toJSON(): object; - tokensValidAfterTime?: string; - uid: string; - } + export type AuthFactorType = AuthFactorType; + // (undocumented) + export type AuthProviderConfig = AuthProviderConfig; + // (undocumented) + export type AuthProviderConfigFilter = AuthProviderConfigFilter; + // (undocumented) + export type BaseAuth = BaseAuth; + // (undocumented) + export type CreateMultiFactorInfoRequest = CreateMultiFactorInfoRequest; + // (undocumented) + export type CreatePhoneMultiFactorInfoRequest = CreatePhoneMultiFactorInfoRequest; + // (undocumented) + export type CreateRequest = CreateRequest; + // (undocumented) + export type CreateTenantRequest = CreateTenantRequest; + // (undocumented) + export type DecodedIdToken = DecodedIdToken; + // (undocumented) + export type DeleteUsersResult = DeleteUsersResult; + // (undocumented) + export type EmailIdentifier = EmailIdentifier; + // (undocumented) + export type EmailSignInProviderConfig = EmailSignInProviderConfig; + // (undocumented) + export type GetUsersResult = GetUsersResult; + // (undocumented) + export type HashAlgorithmType = HashAlgorithmType; + // (undocumented) + export type ListProviderConfigResults = ListProviderConfigResults; + // (undocumented) + export type ListTenantsResult = ListTenantsResult; + // (undocumented) + export type ListUsersResult = ListUsersResult; + // (undocumented) + export type MultiFactorConfig = MultiFactorConfig; + // (undocumented) + export type MultiFactorConfigState = MultiFactorConfigState; + // (undocumented) + export type MultiFactorCreateSettings = MultiFactorCreateSettings; + // (undocumented) + export type MultiFactorInfo = MultiFactorInfo; + // (undocumented) + export type MultiFactorSettings = MultiFactorSettings; + // (undocumented) + export type MultiFactorUpdateSettings = MultiFactorUpdateSettings; + // (undocumented) + export type OIDCAuthProviderConfig = OIDCAuthProviderConfig; + // (undocumented) + export type OIDCUpdateAuthProviderRequest = OIDCUpdateAuthProviderRequest; + // (undocumented) + export type PhoneIdentifier = PhoneIdentifier; + // (undocumented) + export type PhoneMultiFactorInfo = PhoneMultiFactorInfo; + // (undocumented) + export type ProviderIdentifier = ProviderIdentifier; + // (undocumented) + export type SAMLAuthProviderConfig = SAMLAuthProviderConfig; + // (undocumented) + export type SAMLUpdateAuthProviderRequest = SAMLUpdateAuthProviderRequest; + // (undocumented) + export type SessionCookieOptions = SessionCookieOptions; + // (undocumented) + export type Tenant = Tenant; + // (undocumented) + export type TenantAwareAuth = TenantAwareAuth; + // (undocumented) + export type TenantManager = TenantManager; + // (undocumented) + export type UidIdentifier = UidIdentifier; + // (undocumented) + export type UpdateAuthProviderRequest = UpdateAuthProviderRequest; + // (undocumented) + export type UpdateMultiFactorInfoRequest = UpdateMultiFactorInfoRequest; + // (undocumented) + export type UpdatePhoneMultiFactorInfoRequest = UpdatePhoneMultiFactorInfoRequest; + // (undocumented) + export type UpdateRequest = UpdateRequest; + // (undocumented) + export type UpdateTenantRequest = UpdateTenantRequest; + // (undocumented) + export type UserIdentifier = UserIdentifier; + // (undocumented) + export type UserImportOptions = UserImportOptions; + // (undocumented) + export type UserImportRecord = UserImportRecord; + // (undocumented) + export type UserImportResult = UserImportResult; + // (undocumented) + export type UserInfo = UserInfo; + // (undocumented) + export type UserMetadata = UserMetadata; + // (undocumented) + export type UserMetadataRequest = UserMetadataRequest; + // (undocumented) + export type UserProviderRequest = UserProviderRequest; + // (undocumented) + export type UserRecord = UserRecord; +} + +// @public +export type AuthFactorType = 'phone'; + +// @public +export interface AuthProviderConfig { + displayName?: string; + enabled: boolean; + providerId: string; +} + +// @public +export interface AuthProviderConfigFilter { + maxResults?: number; + pageToken?: string; + type: 'saml' | 'oidc'; +} + +// @public +export class BaseAuth { + createCustomToken(uid: string, developerClaims?: object): Promise; + createProviderConfig(config: AuthProviderConfig): Promise; + createSessionCookie(idToken: string, sessionCookieOptions: SessionCookieOptions): Promise; + createUser(properties: CreateRequest): Promise; + deleteProviderConfig(providerId: string): Promise; + deleteUser(uid: string): Promise; + // (undocumented) + deleteUsers(uids: string[]): Promise; + generateEmailVerificationLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise; + generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise; + generateSignInWithEmailLink(email: string, actionCodeSettings: ActionCodeSettings): Promise; + getProviderConfig(providerId: string): Promise; + getUser(uid: string): Promise; + getUserByEmail(email: string): Promise; + getUserByPhoneNumber(phoneNumber: string): Promise; + getUsers(identifiers: UserIdentifier[]): Promise; + importUsers(users: UserImportRecord[], options?: UserImportOptions): Promise; + listProviderConfigs(options: AuthProviderConfigFilter): Promise; + listUsers(maxResults?: number, pageToken?: string): Promise; + revokeRefreshTokens(uid: string): Promise; + setCustomUserClaims(uid: string, customUserClaims: object | null): Promise; + updateProviderConfig(providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise; + updateUser(uid: string, properties: UpdateRequest): Promise; + verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; + verifySessionCookie(sessionCookie: string, checkRevoked?: boolean): Promise; } // @public (undocumented) export function cert(serviceAccountPathOrObject: string | ServiceAccount, httpAgent?: Agent): Credential; +// @public +export interface CreateMultiFactorInfoRequest { + displayName?: string; + factorId: string; +} + +// @public +export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { + phoneNumber: string; +} + +// @public +export interface CreateRequest extends UpdateRequest { + multiFactor?: MultiFactorCreateSettings; + uid?: string; +} + +// @public +export type CreateTenantRequest = UpdateTenantRequest; + // @public (undocumented) export interface Credential { getAccessToken(): Promise; @@ -436,9 +295,55 @@ export namespace database { const ServerValue: rtdb.ServerValue; } +// @public +export interface DecodedIdToken { + // (undocumented) + [key: string]: any; + aud: string; + auth_time: number; + email?: string; + email_verified?: boolean; + exp: number; + firebase: { + identities: { + [key: string]: any; + }; + sign_in_provider: string; + sign_in_second_factor?: string; + second_factor_identifier?: string; + tenant?: string; + [key: string]: any; + }; + iat: number; + iss: string; + phone_number?: string; + picture?: string; + sub: string; + uid: string; +} + // @public (undocumented) export function deleteApp(app: App): Promise; +// @public +export interface DeleteUsersResult { + errors: FirebaseArrayIndexError[]; + failureCount: number; + successCount: number; +} + +// @public +export interface EmailIdentifier { + // (undocumented) + email: string; +} + +// @public +export interface EmailSignInProviderConfig { + enabled: boolean; + passwordRequired?: boolean; +} + // @public export interface FirebaseArrayIndexError { error: FirebaseError; @@ -495,6 +400,12 @@ export function getApp(name?: string): App; // @public (undocumented) export function getApps(): App[]; +// @public +export interface GetUsersResult { + notFound: UserIdentifier[]; + users: UserRecord[]; +} + // @public export interface GoogleOAuthAccessToken { // (undocumented) @@ -503,6 +414,9 @@ export interface GoogleOAuthAccessToken { expires_in: number; } +// @public (undocumented) +export type HashAlgorithmType = 'SCRYPT' | 'STANDARD_SCRYPT' | 'HMAC_SHA512' | 'HMAC_SHA256' | 'HMAC_SHA1' | 'HMAC_MD5' | 'MD5' | 'PBKDF_SHA1' | 'BCRYPT' | 'PBKDF2_SHA256' | 'SHA512' | 'SHA256' | 'SHA1'; + // @public (undocumented) export function initializeApp(options?: AppOptions, name?: string): app.App; @@ -521,6 +435,24 @@ export namespace instanceId { export type InstanceId = InstanceId; } +// @public +export interface ListProviderConfigResults { + pageToken?: string; + providerConfigs: AuthProviderConfig[]; +} + +// @public +export interface ListTenantsResult { + pageToken?: string; + tenants: Tenant[]; +} + +// @public +export interface ListUsersResult { + pageToken?: string; + users: UserRecord[]; +} + // @public export function machineLearning(app?: app.App): machineLearning.MachineLearning; @@ -875,6 +807,72 @@ export namespace messaging { {}; } +// @public (undocumented) +export interface MultiFactorConfig { + factorIds?: AuthFactorType[]; + state: MultiFactorConfigState; +} + +// @public +export type MultiFactorConfigState = 'ENABLED' | 'DISABLED'; + +// @public +export interface MultiFactorCreateSettings { + enrolledFactors: CreateMultiFactorInfoRequest[]; +} + +// @public +export abstract class MultiFactorInfo { + // (undocumented) + readonly displayName?: string; + // (undocumented) + readonly enrollmentTime?: string; + // (undocumented) + readonly factorId: string; + toJSON(): any; + // (undocumented) + readonly uid: string; +} + +// @public +export class MultiFactorSettings { + // (undocumented) + enrolledFactors: MultiFactorInfo[]; + toJSON(): any; +} + +// @public +export interface MultiFactorUpdateSettings { + enrolledFactors: UpdateMultiFactorInfoRequest[] | null; +} + +// @public +export interface OIDCAuthProviderConfig extends AuthProviderConfig { + clientId: string; + issuer: string; +} + +// @public +export interface OIDCUpdateAuthProviderRequest { + clientId?: string; + displayName?: string; + enabled?: boolean; + issuer?: string; +} + +// @public +export interface PhoneIdentifier { + // (undocumented) + phoneNumber: string; +} + +// @public +export class PhoneMultiFactorInfo extends MultiFactorInfo { + // (undocumented) + readonly phoneNumber: string; + toJSON(): any; +} + // @public export function projectManagement(app?: app.App): projectManagement.ProjectManagement; @@ -939,6 +937,14 @@ export namespace projectManagement { } } +// @public +export interface ProviderIdentifier { + // (undocumented) + providerId: string; + // (undocumented) + providerUid: string; +} + // @public (undocumented) export function refreshToken(refreshTokenPathOrObject: string | object, httpAgent?: Agent): Credential; @@ -1025,6 +1031,26 @@ export namespace remoteConfig { } } +// @public +export interface SAMLAuthProviderConfig extends AuthProviderConfig { + callbackURL?: string; + idpEntityId: string; + rpEntityId: string; + ssoURL: string; + x509Certificates: string[]; +} + +// @public +export interface SAMLUpdateAuthProviderRequest { + callbackURL?: string; + displayName?: string; + enabled?: boolean; + idpEntityId?: string; + rpEntityId?: string; + ssoURL?: string; + x509Certificates?: string[]; +} + // @public (undocumented) export const SDK_VERSION: string; @@ -1078,6 +1104,11 @@ export interface ServiceAccount { projectId?: string; } +// @public +export interface SessionCookieOptions { + expiresIn: number; +} + // @public export function storage(app?: app.App): storage.Storage; @@ -1090,6 +1121,210 @@ export namespace storage { } } +// @public +export class Tenant { + // (undocumented) + readonly displayName?: string; + // (undocumented) + get emailSignInConfig(): EmailSignInProviderConfig | undefined; + // (undocumented) + get multiFactorConfig(): MultiFactorConfig | undefined; + // (undocumented) + readonly tenantId: string; + // (undocumented) + readonly testPhoneNumbers?: { + [phoneNumber: string]: string; + }; + toJSON(): object; + } + +// @public +export class TenantAwareAuth extends BaseAuth { + createSessionCookie(idToken: string, sessionCookieOptions: SessionCookieOptions): Promise; + // (undocumented) + readonly tenantId: string; + verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; + verifySessionCookie(sessionCookie: string, checkRevoked?: boolean): Promise; +} + +// @public +export class TenantManager { + authForTenant(tenantId: string): TenantAwareAuth; + createTenant(tenantOptions: CreateTenantRequest): Promise; + deleteTenant(tenantId: string): Promise; + getTenant(tenantId: string): Promise; + listTenants(maxResults?: number, pageToken?: string): Promise; + updateTenant(tenantId: string, tenantOptions: UpdateTenantRequest): Promise; +} + +// @public +export interface UidIdentifier { + // (undocumented) + uid: string; +} + +// @public (undocumented) +export type UpdateAuthProviderRequest = SAMLUpdateAuthProviderRequest | OIDCUpdateAuthProviderRequest; + +// @public +export interface UpdateMultiFactorInfoRequest { + displayName?: string; + enrollmentTime?: string; + factorId: string; + uid?: string; +} + +// @public +export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { + phoneNumber: string; +} + +// @public +export interface UpdateRequest { + disabled?: boolean; + displayName?: string | null; + email?: string; + emailVerified?: boolean; + multiFactor?: MultiFactorUpdateSettings; + password?: string; + phoneNumber?: string | null; + photoURL?: string | null; +} + +// @public +export interface UpdateTenantRequest { + displayName?: string; + emailSignInConfig?: EmailSignInProviderConfig; + multiFactorConfig?: MultiFactorConfig; + testPhoneNumbers?: { + [phoneNumber: string]: string; + } | null; +} + +// @public +export type UserIdentifier = UidIdentifier | EmailIdentifier | PhoneIdentifier | ProviderIdentifier; + +// @public +export interface UserImportOptions { + hash: { + algorithm: HashAlgorithmType; + key?: Buffer; + saltSeparator?: Buffer; + rounds?: number; + memoryCost?: number; + parallelization?: number; + blockSize?: number; + derivedKeyLength?: number; + }; +} + +// @public +export interface UserImportRecord { + customClaims?: { + [key: string]: any; + }; + disabled?: boolean; + displayName?: string; + email?: string; + emailVerified?: boolean; + metadata?: UserMetadataRequest; + multiFactor?: MultiFactorUpdateSettings; + passwordHash?: Buffer; + passwordSalt?: Buffer; + phoneNumber?: string; + photoURL?: string; + providerData?: UserProviderRequest[]; + tenantId?: string; + uid: string; +} + +// @public +export interface UserImportResult { + errors: FirebaseArrayIndexError[]; + failureCount: number; + successCount: number; +} + +// @public +export class UserInfo { + // (undocumented) + readonly displayName: string; + // (undocumented) + readonly email: string; + // (undocumented) + readonly phoneNumber: string; + // (undocumented) + readonly photoURL: string; + // (undocumented) + readonly providerId: string; + toJSON(): object; + // (undocumented) + readonly uid: string; +} + +// @public +export class UserMetadata { + // (undocumented) + readonly creationTime: string; + readonly lastRefreshTime: string | null; + // (undocumented) + readonly lastSignInTime: string; + toJSON(): object; +} + +// @public +export interface UserMetadataRequest { + creationTime?: string; + lastSignInTime?: string; +} + +// @public +export interface UserProviderRequest { + displayName?: string; + email?: string; + phoneNumber?: string; + photoURL?: string; + providerId: string; + uid: string; +} + +// @public +export class UserRecord { + // (undocumented) + readonly customClaims: { + [key: string]: any; + }; + // (undocumented) + readonly disabled: boolean; + // (undocumented) + readonly displayName: string; + // (undocumented) + readonly email: string; + // (undocumented) + readonly emailVerified: boolean; + // (undocumented) + readonly metadata: UserMetadata; + // (undocumented) + readonly multiFactor?: MultiFactorSettings; + // (undocumented) + readonly passwordHash?: string; + // (undocumented) + readonly passwordSalt?: string; + // (undocumented) + readonly phoneNumber: string; + // (undocumented) + readonly photoURL: string; + // (undocumented) + readonly providerData: UserInfo[]; + // (undocumented) + readonly tenantId?: string | null; + toJSON(): object; + // (undocumented) + readonly tokensValidAfterTime?: string; + // (undocumented) + readonly uid: string; +} + // (No @packageDocumentation comment for this package) diff --git a/gulpfile.js b/gulpfile.js index 0adae29789..9698d11431 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -88,6 +88,7 @@ gulp.task('compile', function() { 'lib/firebase-namespace-api.d.ts', 'lib/core.d.ts', 'lib/app/*.d.ts', + 'lib/auth/*.d.ts', 'lib/instance-id/*.d.ts', '!lib/utils/index.d.ts', ]; diff --git a/package.json b/package.json index 37620aa245..21b22b48a4 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "lint:test": "eslint test/ --ext .ts", "apidocs": "node docgen/generate-docs.js --api node", "api-extractor": "api-extractor run", - "api-extractor:local": "api-extractor run --local" + "api-extractor:local": "npm run build && api-extractor run --local" }, "nyc": { "extension": [ diff --git a/src/app/firebase-app.ts b/src/app/firebase-app.ts index 5c66ca0c60..40177fe793 100644 --- a/src/app/firebase-app.ts +++ b/src/app/firebase-app.ts @@ -277,10 +277,8 @@ export class FirebaseApp implements app.App { * @return The Auth service instance of this app. */ public auth(): Auth { - return this.ensureService_('auth', () => { - const authService: typeof Auth = require('../auth/auth').Auth; - return new authService(this); - }); + const fn = require('../auth/index').auth; + return fn(this); } /** diff --git a/src/auth/action-code-settings-builder.ts b/src/auth/action-code-settings-builder.ts index 14c212b6fe..ec63e4ea63 100644 --- a/src/auth/action-code-settings-builder.ts +++ b/src/auth/action-code-settings-builder.ts @@ -16,9 +16,87 @@ import * as validator from '../utils/validator'; import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; -import { auth } from './index'; -import ActionCodeSettings = auth.ActionCodeSettings; +/** + * This is the interface that defines the required continue/state URL with + * optional Android and iOS bundle identifiers. + */ +export interface ActionCodeSettings { + + /** + * Defines the link continue/state URL, which has different meanings in + * different contexts: + *
    + *
  • When the link is handled in the web action widgets, this is the deep + * link in the `continueUrl` query parameter.
  • + *
  • When the link is handled in the app directly, this is the `continueUrl` + * query parameter in the deep link of the Dynamic Link.
  • + *
+ */ + url: string; + + /** + * Whether to open the link via a mobile app or a browser. + * The default is false. When set to true, the action code link is sent + * as a Universal Link or Android App Link and is opened by the app if + * installed. In the false case, the code is sent to the web widget first + * and then redirects to the app if installed. + */ + handleCodeInApp?: boolean; + + /** + * Defines the iOS bundle ID. This will try to open the link in an iOS app if it + * is installed. + */ + iOS?: { + + /** + * Defines the required iOS bundle ID of the app where the link should be + * handled if the application is already installed on the device. + */ + bundleId: string; + }; + + /** + * Defines the Android package name. This will try to open the link in an + * android app if it is installed. If `installApp` is passed, it specifies + * whether to install the Android app if the device supports it and the app is + * not already installed. If this field is provided without a `packageName`, an + * error is thrown explaining that the `packageName` must be provided in + * conjunction with this field. If `minimumVersion` is specified, and an older + * version of the app is installed, the user is taken to the Play Store to + * upgrade the app. + */ + android?: { + + /** + * Defines the required Android package name of the app where the link should be + * handled if the Android app is installed. + */ + packageName: string; + + /** + * Whether to install the Android app if the device supports it and the app is + * not already installed. + */ + installApp?: boolean; + + /** + * The Android minimum version if available. If the installed app is an older + * version, the user is taken to the GOogle Play Store to upgrade the app. + */ + minimumVersion?: string; + }; + + /** + * Defines the dynamic link domain to use for the current link if it is to be + * opened using Firebase Dynamic Links, as multiple dynamic link domains can be + * configured per project. This field provides the ability to explicitly choose + * configured per project. This fields provides the ability explicitly choose + * one. If none is provided, the oldest domain is used by default. + */ + dynamicLinkDomain?: string; +} /** Defines the email action code server request. */ interface EmailActionCodeRequest { @@ -34,6 +112,8 @@ interface EmailActionCodeRequest { /** * Defines the ActionCodeSettings builder class used to convert the * ActionCodeSettings object to its corresponding server request. + * + * @internal */ export class ActionCodeSettingsBuilder { private continueUrl?: string; @@ -70,7 +150,7 @@ export class ActionCodeSettingsBuilder { this.continueUrl = actionCodeSettings.url; if (typeof actionCodeSettings.handleCodeInApp !== 'undefined' && - !validator.isBoolean(actionCodeSettings.handleCodeInApp)) { + !validator.isBoolean(actionCodeSettings.handleCodeInApp)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.handleCodeInApp" must be a boolean.', @@ -79,14 +159,14 @@ export class ActionCodeSettingsBuilder { this.canHandleCodeInApp = actionCodeSettings.handleCodeInApp || false; if (typeof actionCodeSettings.dynamicLinkDomain !== 'undefined' && - !validator.isNonEmptyString(actionCodeSettings.dynamicLinkDomain)) { + !validator.isNonEmptyString(actionCodeSettings.dynamicLinkDomain)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_DYNAMIC_LINK_DOMAIN, ); } this.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain; - if (typeof actionCodeSettings.iOS !== 'undefined') { + if (typeof actionCodeSettings.iOS !== 'undefined') { if (!validator.isNonNullObject(actionCodeSettings.iOS)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -105,7 +185,7 @@ export class ActionCodeSettingsBuilder { this.ibi = actionCodeSettings.iOS.bundleId; } - if (typeof actionCodeSettings.android !== 'undefined') { + if (typeof actionCodeSettings.android !== 'undefined') { if (!validator.isNonNullObject(actionCodeSettings.android)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -121,13 +201,13 @@ export class ActionCodeSettingsBuilder { '"ActionCodeSettings.android.packageName" must be a valid non-empty string.', ); } else if (typeof actionCodeSettings.android.minimumVersion !== 'undefined' && - !validator.isNonEmptyString(actionCodeSettings.android.minimumVersion)) { + !validator.isNonEmptyString(actionCodeSettings.android.minimumVersion)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.android.minimumVersion" must be a valid non-empty string.', ); } else if (typeof actionCodeSettings.android.installApp !== 'undefined' && - !validator.isBoolean(actionCodeSettings.android.installApp)) { + !validator.isBoolean(actionCodeSettings.android.installApp)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.android.installApp" must be a valid boolean.', @@ -146,7 +226,7 @@ export class ActionCodeSettingsBuilder { * @return {EmailActionCodeRequest} The constructed EmailActionCodeRequest request. */ public buildRequest(): EmailActionCodeRequest { - const request: {[key: string]: any} = { + const request: { [key: string]: any } = { continueUrl: this.continueUrl, canHandleCodeInApp: this.canHandleCodeInApp, dynamicLinkDomain: this.dynamicLinkDomain, diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 7552f5dfb4..8de0f1f29e 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -17,44 +17,31 @@ import * as validator from '../utils/validator'; -import { deepCopy, deepExtend } from '../utils/deep-copy'; -import { - isUidIdentifier, isEmailIdentifier, isPhoneIdentifier, isProviderIdentifier -} from './identifier'; +import { App } from '../app/index'; import { FirebaseApp } from '../app/firebase-app'; +import { deepCopy, deepExtend } from '../utils/deep-copy'; import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; import { ApiSettings, AuthorizedHttpClient, HttpRequestConfig, HttpError, } from '../utils/api-request'; +import * as utils from '../utils/index'; + import { + UserImportOptions, UserImportRecord, UserImportResult, UserImportBuilder, AuthFactorInfo, convertMultiFactorInfoToServerFormat, } from './user-import-builder'; -import * as utils from '../utils/index'; -import { ActionCodeSettingsBuilder } from './action-code-settings-builder'; +import { ActionCodeSettings, ActionCodeSettingsBuilder } from './action-code-settings-builder'; +import { Tenant, TenantServerResponse, CreateTenantRequest, UpdateTenantRequest } from './tenant'; +import { + isUidIdentifier, isEmailIdentifier, isPhoneIdentifier, isProviderIdentifier, + UserIdentifier, UidIdentifier, EmailIdentifier,PhoneIdentifier, ProviderIdentifier, +} from './identifier'; import { SAMLConfig, OIDCConfig, OIDCConfigServerResponse, SAMLConfigServerResponse, - OIDCConfigServerRequest, SAMLConfigServerRequest, + OIDCConfigServerRequest, SAMLConfigServerRequest, CreateRequest, UpdateRequest, + OIDCAuthProviderConfig, SAMLAuthProviderConfig, OIDCUpdateAuthProviderRequest, + SAMLUpdateAuthProviderRequest } from './auth-config'; -import { Tenant, TenantServerResponse } from './tenant'; -import { auth } from './index'; - -import CreateRequest = auth.CreateRequest; -import UpdateRequest = auth.UpdateRequest; -import UserIdentifier = auth.UserIdentifier; -import UidIdentifier = auth.UidIdentifier; -import EmailIdentifier = auth.EmailIdentifier; -import PhoneIdentifier = auth.PhoneIdentifier; -import ProviderIdentifier = auth.ProviderIdentifier; -import UserImportOptions = auth.UserImportOptions; -import UserImportRecord = auth.UserImportRecord; -import UserImportResult = auth.UserImportResult; -import ActionCodeSettings = auth.ActionCodeSettings; -import OIDCAuthProviderConfig = auth.OIDCAuthProviderConfig; -import SAMLAuthProviderConfig = auth.SAMLAuthProviderConfig; -import OIDCUpdateAuthProviderRequest = auth.OIDCUpdateAuthProviderRequest; -import SAMLUpdateAuthProviderRequest = auth.SAMLUpdateAuthProviderRequest; -import CreateTenantRequest = auth.CreateTenantRequest; -import UpdateTenantRequest = auth.UpdateTenantRequest; /** Firebase Auth request header. */ const FIREBASE_AUTH_HEADER = { @@ -143,7 +130,7 @@ class AuthResourceUrlBuilder { * @param {string} version The endpoint API version. * @constructor */ - constructor(protected app: FirebaseApp, protected version: string = 'v1') { + constructor(protected app: App, protected version: string = 'v1') { if (useEmulator()) { this.urlFormat = utils.formatString(FIREBASE_AUTH_EMULATOR_BASE_URL_FORMAT, { host: emulatorHost() @@ -208,7 +195,7 @@ class TenantAwareAuthResourceUrlBuilder extends AuthResourceUrlBuilder { * @param {string} tenantId The tenant ID. * @constructor */ - constructor(protected app: FirebaseApp, protected version: string, protected tenantId: string) { + constructor(protected app: App, protected version: string, protected tenantId: string) { super(app, version); if (useEmulator()) { this.urlFormat = utils.formatString(FIREBASE_AUTH_EMULATOR_TENANT_URL_FORMAT, { @@ -571,7 +558,11 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat } -/** Instantiates the createSessionCookie endpoint settings. */ +/** + * Instantiates the createSessionCookie endpoint settings. + * + * @internal + */ export const FIREBASE_AUTH_CREATE_SESSION_COOKIE = new ApiSettings(':createSessionCookie', 'POST') // Set request validator. @@ -596,11 +587,19 @@ export const FIREBASE_AUTH_CREATE_SESSION_COOKIE = }); -/** Instantiates the uploadAccount endpoint settings. */ +/** + * Instantiates the uploadAccount endpoint settings. + * + * @internal + */ export const FIREBASE_AUTH_UPLOAD_ACCOUNT = new ApiSettings('/accounts:batchCreate', 'POST'); -/** Instantiates the downloadAccount endpoint settings. */ +/** + * Instantiates the downloadAccount endpoint settings. + * + * @internal + */ export const FIREBASE_AUTH_DOWNLOAD_ACCOUNT = new ApiSettings('/accounts:batchGet', 'GET') // Set request validator. .setRequestValidator((request: any) => { @@ -631,7 +630,11 @@ interface GetAccountInfoRequest { }>; } -/** Instantiates the getAccountInfo endpoint settings. */ +/** + * Instantiates the getAccountInfo endpoint settings. + * + * @internal + */ export const FIREBASE_AUTH_GET_ACCOUNT_INFO = new ApiSettings('/accounts:lookup', 'POST') // Set request validator. .setRequestValidator((request: GetAccountInfoRequest) => { @@ -651,6 +654,8 @@ export const FIREBASE_AUTH_GET_ACCOUNT_INFO = new ApiSettings('/accounts:lookup' /** * Instantiates the getAccountInfo endpoint settings for use when fetching info * for multiple accounts. + * + * @internal */ export const FIREBASE_AUTH_GET_ACCOUNTS_INFO = new ApiSettings('/accounts:lookup', 'POST') // Set request validator. @@ -663,7 +668,11 @@ export const FIREBASE_AUTH_GET_ACCOUNTS_INFO = new ApiSettings('/accounts:lookup }); -/** Instantiates the deleteAccount endpoint settings. */ +/** + * Instantiates the deleteAccount endpoint settings. + * + * @internal + */ export const FIREBASE_AUTH_DELETE_ACCOUNT = new ApiSettings('/accounts:delete', 'POST') // Set request validator. .setRequestValidator((request: any) => { @@ -689,6 +698,9 @@ export interface BatchDeleteAccountsResponse { errors?: BatchDeleteErrorInfo[]; } +/** + * @internal + */ export const FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS = new ApiSettings('/accounts:batchDelete', 'POST') .setRequestValidator((request: BatchDeleteAccountsRequest) => { if (!request.localIds) { @@ -719,7 +731,11 @@ export const FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS = new ApiSettings('/accounts:ba }); }); -/** Instantiates the setAccountInfo endpoint settings for updating existing accounts. */ +/** + * Instantiates the setAccountInfo endpoint settings for updating existing accounts. + * + * @internal + */ export const FIREBASE_AUTH_SET_ACCOUNT_INFO = new ApiSettings('/accounts:update', 'POST') // Set request validator. .setRequestValidator((request: any) => { @@ -748,6 +764,8 @@ export const FIREBASE_AUTH_SET_ACCOUNT_INFO = new ApiSettings('/accounts:update' /** * Instantiates the signupNewUser endpoint settings for creating a new user with or without * uid being specified. The backend will create a new one if not provided and return it. + * + * @internal */ export const FIREBASE_AUTH_SIGN_UP_NEW_USER = new ApiSettings('/accounts', 'POST') // Set request validator. @@ -809,7 +827,11 @@ const FIREBASE_AUTH_GET_OOB_CODE = new ApiSettings('/accounts:sendOobCode', 'POS } }); -/** Instantiates the retrieve OIDC configuration endpoint settings. */ +/** + * Instantiates the retrieve OIDC configuration endpoint settings. + * + * @internal + */ const GET_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}', 'GET') // Set response validator. .setResponseValidator((response: any) => { @@ -822,10 +844,18 @@ const GET_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}', 'G } }); -/** Instantiates the delete OIDC configuration endpoint settings. */ +/** + * Instantiates the delete OIDC configuration endpoint settings. + * + * @internal + */ const DELETE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}', 'DELETE'); -/** Instantiates the create OIDC configuration endpoint settings. */ +/** + * Instantiates the create OIDC configuration endpoint settings. + * + * @internal + */ const CREATE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs?oauthIdpConfigId={providerId}', 'POST') // Set response validator. .setResponseValidator((response: any) => { @@ -838,7 +868,11 @@ const CREATE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs?oauthIdpConfig } }); -/** Instantiates the update OIDC configuration endpoint settings. */ +/** + * Instantiates the update OIDC configuration endpoint settings. + * + * @internal + */ const UPDATE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}?updateMask={updateMask}', 'PATCH') // Set response validator. .setResponseValidator((response: any) => { @@ -851,7 +885,11 @@ const UPDATE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}?u } }); -/** Instantiates the list OIDC configuration endpoint settings. */ +/** + * Instantiates the list OIDC configuration endpoint settings. + * + * @internal + */ const LIST_OAUTH_IDP_CONFIGS = new ApiSettings('/oauthIdpConfigs', 'GET') // Set request validator. .setRequestValidator((request: any) => { @@ -872,7 +910,11 @@ const LIST_OAUTH_IDP_CONFIGS = new ApiSettings('/oauthIdpConfigs', 'GET') } }); -/** Instantiates the retrieve SAML configuration endpoint settings. */ +/** + * Instantiates the retrieve SAML configuration endpoint settings. + * + * @internal + */ const GET_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{providerId}', 'GET') // Set response validator. .setResponseValidator((response: any) => { @@ -885,10 +927,18 @@ const GET_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{providerId } }); -/** Instantiates the delete SAML configuration endpoint settings. */ +/** + * Instantiates the delete SAML configuration endpoint settings. + * + * @internal + */ const DELETE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{providerId}', 'DELETE'); -/** Instantiates the create SAML configuration endpoint settings. */ +/** + * Instantiates the create SAML configuration endpoint settings. + * + * @internal + */ const CREATE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs?inboundSamlConfigId={providerId}', 'POST') // Set response validator. .setResponseValidator((response: any) => { @@ -901,7 +951,11 @@ const CREATE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs?inboundS } }); -/** Instantiates the update SAML configuration endpoint settings. */ +/** + * Instantiates the update SAML configuration endpoint settings. + * + * @internal + */ const UPDATE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{providerId}?updateMask={updateMask}', 'PATCH') // Set response validator. .setResponseValidator((response: any) => { @@ -914,7 +968,11 @@ const UPDATE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{provide } }); -/** Instantiates the list SAML configuration endpoint settings. */ +/** + * Instantiates the list SAML configuration endpoint settings. + * + * @internal + */ const LIST_INBOUND_SAML_CONFIGS = new ApiSettings('/inboundSamlConfigs', 'GET') // Set request validator. .setRequestValidator((request: any) => { @@ -937,6 +995,8 @@ const LIST_INBOUND_SAML_CONFIGS = new ApiSettings('/inboundSamlConfigs', 'GET') /** * Class that provides the mechanism to send requests to the Firebase Auth backend endpoints. + * + * @internal */ export abstract class AbstractAuthRequestHandler { @@ -997,7 +1057,7 @@ export abstract class AbstractAuthRequestHandler { * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. * @constructor */ - constructor(protected readonly app: FirebaseApp) { + constructor(protected readonly app: App) { if (typeof app !== 'object' || app === null || !('options' in app)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -1005,7 +1065,7 @@ export abstract class AbstractAuthRequestHandler { ); } - this.httpClient = new AuthHttpClient(app); + this.httpClient = new AuthHttpClient(app as FirebaseApp); } /** @@ -1915,7 +1975,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. * @constructor. */ - constructor(app: FirebaseApp) { + constructor(app: App) { super(app); this.tenantMgmtResourceBuilder = new AuthResourceUrlBuilder(app, 'v2'); } @@ -2061,7 +2121,7 @@ export class TenantAwareAuthRequestHandler extends AbstractAuthRequestHandler { * @param {string} tenantId The request handler's tenant ID. * @constructor */ - constructor(app: FirebaseApp, private readonly tenantId: string) { + constructor(app: App, private readonly tenantId: string) { super(app); } diff --git a/src/auth/auth-config.ts b/src/auth/auth-config.ts index 26db94126e..1449d4191f 100644 --- a/src/auth/auth-config.ts +++ b/src/auth/auth-config.ts @@ -17,14 +17,293 @@ import * as validator from '../utils/validator'; import { deepCopy } from '../utils/deep-copy'; import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; -import { auth } from './index'; -import MultiFactorConfigInterface = auth.MultiFactorConfig; -import MultiFactorConfigState = auth.MultiFactorConfigState; -import AuthFactorType = auth.AuthFactorType; -import EmailSignInProviderConfig = auth.EmailSignInProviderConfig; -import OIDCAuthProviderConfig = auth.OIDCAuthProviderConfig; -import SAMLAuthProviderConfig = auth.SAMLAuthProviderConfig; +/** + * Interface representing base properties of a user enrolled second factor for a + * `CreateRequest`. + */ +export interface CreateMultiFactorInfoRequest { + + /** + * The optional display name for an enrolled second factor. + */ + displayName?: string; + + /** + * The type identifier of the second factor. For SMS second factors, this is `phone`. + */ + factorId: string; +} + +/** + * Interface representing a phone specific user enrolled second factor for a + * `CreateRequest`. + */ +export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { + + /** + * The phone number associated with a phone second factor. + */ + phoneNumber: string; +} + +/** + * Interface representing common properties of a user enrolled second factor + * for an `UpdateRequest`. + */ +export interface UpdateMultiFactorInfoRequest { + + /** + * The ID of the enrolled second factor. This ID is unique to the user. When not provided, + * a new one is provisioned by the Auth server. + */ + uid?: string; + + /** + * The optional display name for an enrolled second factor. + */ + displayName?: string; + + /** + * The optional date the second factor was enrolled, formatted as a UTC string. + */ + enrollmentTime?: string; + + /** + * The type identifier of the second factor. For SMS second factors, this is `phone`. + */ + factorId: string; +} + +/** + * Interface representing a phone specific user enrolled second factor + * for an `UpdateRequest`. + */ +export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { + + /** + * The phone number associated with a phone second factor. + */ + phoneNumber: string; +} + +/** + * The multi-factor related user settings for create operations. + */ +export interface MultiFactorCreateSettings { + + /** + * The created user's list of enrolled second factors. + */ + enrolledFactors: CreateMultiFactorInfoRequest[]; +} + +/** + * The multi-factor related user settings for update operations. + */ +export interface MultiFactorUpdateSettings { + + /** + * The updated list of enrolled second factors. The provided list overwrites the user's + * existing list of second factors. + * When null is passed, all of the user's existing second factors are removed. + */ + enrolledFactors: UpdateMultiFactorInfoRequest[] | null; +} + +/** + * Interface representing the properties to update on the provided user. + */ +export interface UpdateRequest { + + /** + * Whether or not the user is disabled: `true` for disabled; + * `false` for enabled. + */ + disabled?: boolean; + + /** + * The user's display name. + */ + displayName?: string | null; + + /** + * The user's primary email. + */ + email?: string; + + /** + * Whether or not the user's primary email is verified. + */ + emailVerified?: boolean; + + /** + * The user's unhashed password. + */ + password?: string; + + /** + * The user's primary phone number. + */ + phoneNumber?: string | null; + + /** + * The user's photo URL. + */ + photoURL?: string | null; + + /** + * The user's updated multi-factor related properties. + */ + multiFactor?: MultiFactorUpdateSettings; +} + +/** + * Interface representing the properties to set on a new user record to be + * created. + */ +export interface CreateRequest extends UpdateRequest { + + /** + * The user's `uid`. + */ + uid?: string; + + /** + * The user's multi-factor related properties. + */ + multiFactor?: MultiFactorCreateSettings; +} + +/** + * The response interface for listing provider configs. This is only available + * when listing all identity providers' configurations via + * {@link auth.Auth.listProviderConfigs `listProviderConfigs()`}. + */ +export interface ListProviderConfigResults { + + /** + * The list of providers for the specified type in the current page. + */ + providerConfigs: AuthProviderConfig[]; + + /** + * The next page token, if available. + */ + pageToken?: string; +} + +/** + * The filter interface used for listing provider configurations. This is used + * when specifying how to list configured identity providers via + * {@link auth.Auth.listProviderConfigs `listProviderConfigs()`}. + */ +export interface AuthProviderConfigFilter { + + /** + * The Auth provider configuration filter. This can be either `saml` or `oidc`. + * The former is used to look up SAML providers only, while the latter is used + * for OIDC providers. + */ + type: 'saml' | 'oidc'; + + /** + * The maximum number of results to return per page. The default and maximum is + * 100. + */ + maxResults?: number; + + /** + * The next page token. When not specified, the lookup starts from the beginning + * of the list. + */ + pageToken?: string; +} + +/** + * The request interface for updating a SAML Auth provider. This is used + * when updating a SAML provider's configuration via + * {@link auth.Auth.updateProviderConfig `updateProviderConfig()`}. + */ +export interface SAMLUpdateAuthProviderRequest { + + /** + * The SAML provider's updated display name. If not provided, the existing + * configuration's value is not modified. + */ + displayName?: string; + + /** + * Whether the SAML provider is enabled or not. If not provided, the existing + * configuration's setting is not modified. + */ + enabled?: boolean; + + /** + * The SAML provider's updated IdP entity ID. If not provided, the existing + * configuration's value is not modified. + */ + idpEntityId?: string; + + /** + * The SAML provider's updated SSO URL. If not provided, the existing + * configuration's value is not modified. + */ + ssoURL?: string; + + /** + * The SAML provider's updated list of X.509 certificated. If not provided, the + * existing configuration list is not modified. + */ + x509Certificates?: string[]; + + /** + * The SAML provider's updated RP entity ID. If not provided, the existing + * configuration's value is not modified. + */ + rpEntityId?: string; + + /** + * The SAML provider's callback URL. If not provided, the existing + * configuration's value is not modified. + */ + callbackURL?: string; +} + +/** + * The request interface for updating an OIDC Auth provider. This is used + * when updating an OIDC provider's configuration via + * {@link auth.Auth.updateProviderConfig `updateProviderConfig()`}. + */ +export interface OIDCUpdateAuthProviderRequest { + + /** + * The OIDC provider's updated display name. If not provided, the existing + * configuration's value is not modified. + */ + displayName?: string; + + /** + * Whether the OIDC provider is enabled or not. If not provided, the existing + * configuration's setting is not modified. + */ + enabled?: boolean; + + /** + * The OIDC provider's updated client ID. If not provided, the existing + * configuration's value is not modified. + */ + clientId?: string; + + /** + * The OIDC provider's updated issuer. If not provided, the existing + * configuration's value is not modified. + */ + issuer?: string; +} + +export type UpdateAuthProviderRequest = + SAMLUpdateAuthProviderRequest | OIDCUpdateAuthProviderRequest; /** A maximum of 10 test phone number / code pairs can be configured. */ export const MAXIMUM_TEST_PHONE_NUMBERS = 10; @@ -117,11 +396,35 @@ export interface MultiFactorAuthServerConfig { enabledProviders?: AuthFactorServerType[]; } +/** + * Identifies a second factor type. + */ +export type AuthFactorType = 'phone'; + +/** + * Identifies a multi-factor configuration state. + */ +export type MultiFactorConfigState = 'ENABLED' | 'DISABLED'; + +export interface MultiFactorConfig { + /** + * The multi-factor config state. + */ + state: MultiFactorConfigState; + + /** + * The list of identifiers for enabled second factors. + * Currently only ‘phone’ is supported. + */ + factorIds?: AuthFactorType[]; +} + /** * Defines the multi-factor config class used to convert client side MultiFactorConfig * to a format that is understood by the Auth server. */ -export class MultiFactorAuthConfig implements MultiFactorConfigInterface { +export class MultiFactorAuthConfig implements MultiFactorConfig { + public readonly state: MultiFactorConfigState; public readonly factorIds: AuthFactorType[]; @@ -131,8 +434,9 @@ export class MultiFactorAuthConfig implements MultiFactorConfigInterface { * * @param options The options object to convert to a server request. * @return The resulting server request. + * @internal */ - public static buildServerRequest(options: MultiFactorConfigInterface): MultiFactorAuthServerConfig { + public static buildServerRequest(options: MultiFactorConfig): MultiFactorAuthServerConfig { const request: MultiFactorAuthServerConfig = {}; MultiFactorAuthConfig.validate(options); if (Object.prototype.hasOwnProperty.call(options, 'state')) { @@ -158,7 +462,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfigInterface { * * @param options The options object to validate. */ - private static validate(options: MultiFactorConfigInterface): void { + private static validate(options: MultiFactorConfig): void { const validKeys = { state: true, factorIds: true, @@ -214,6 +518,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfigInterface { * @param response The server side response used to initialize the * MultiFactorAuthConfig object. * @constructor + * @internal */ constructor(response: MultiFactorAuthServerConfig) { if (typeof response.state === 'undefined') { @@ -278,10 +583,28 @@ export function validateTestPhoneNumbers( } } +/** + * The email sign in configuration. + */ +export interface EmailSignInProviderConfig { + /** + * Whether email provider is enabled. + */ + enabled: boolean; + + /** + * Whether password is required for email sign-in. When not required, + * email sign-in can be performed with password or via email link sign-in. + */ + passwordRequired?: boolean; // In the backend API, default is true if not provided +} + /** * Defines the email sign-in config class used to convert client side EmailSignInConfig * to a format that is understood by the Auth server. + * + * @internal */ export class EmailSignInConfig implements EmailSignInProviderConfig { public readonly enabled: boolean; @@ -293,6 +616,7 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { * * @param {any} options The options object to convert to a server request. * @return {EmailSignInConfigServerRequest} The resulting server request. + * @internal */ public static buildServerRequest(options: EmailSignInProviderConfig): EmailSignInConfigServerRequest { const request: EmailSignInConfigServerRequest = {}; @@ -375,10 +699,117 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { } } +/** + * The base Auth provider configuration interface. + */ +export interface AuthProviderConfig { + + /** + * The provider ID defined by the developer. + * For a SAML provider, this is always prefixed by `saml.`. + * For an OIDC provider, this is always prefixed by `oidc.`. + */ + providerId: string; + + /** + * The user-friendly display name to the current configuration. This name is + * also used as the provider label in the Cloud Console. + */ + displayName?: string; + + /** + * Whether the provider configuration is enabled or disabled. A user + * cannot sign in using a disabled provider. + */ + enabled: boolean; +} + +/** + * The + * [SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) + * Auth provider configuration interface. A SAML provider can be created via + * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. + */ +export interface SAMLAuthProviderConfig extends AuthProviderConfig { + + /** + * The SAML IdP entity identifier. + */ + idpEntityId: string; + + /** + * The SAML IdP SSO URL. This must be a valid URL. + */ + ssoURL: string; + + /** + * The list of SAML IdP X.509 certificates issued by CA for this provider. + * Multiple certificates are accepted to prevent outages during + * IdP key rotation (for example ADFS rotates every 10 days). When the Auth + * server receives a SAML response, it will match the SAML response with the + * certificate on record. Otherwise the response is rejected. + * Developers are expected to manage the certificate updates as keys are + * rotated. + */ + x509Certificates: string[]; + + /** + * The SAML relying party (service provider) entity ID. + * This is defined by the developer but needs to be provided to the SAML IdP. + */ + rpEntityId: string; + + /** + * This is fixed and must always be the same as the OAuth redirect URL + * provisioned by Firebase Auth, + * `https://project-id.firebaseapp.com/__/auth/handler` unless a custom + * `authDomain` is used. + * The callback URL should also be provided to the SAML IdP during + * configuration. + */ + callbackURL?: string; +} + +/** + * The [OIDC](https://openid.net/specs/openid-connect-core-1_0-final.html) Auth + * provider configuration interface. An OIDC provider can be created via + * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. + */ +export interface OIDCAuthProviderConfig extends AuthProviderConfig { + + /** + * This is the required client ID used to confirm the audience of an OIDC + * provider's + * [ID token](https://openid.net/specs/openid-connect-core-1_0-final.html#IDToken). + */ + clientId: string; + + /** + * This is the required provider issuer used to match the provider issuer of + * the ID token and to determine the corresponding OIDC discovery document, eg. + * [`/.well-known/openid-configuration`](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig). + * This is needed for the following: + *
    + *
  • To verify the provided issuer.
  • + *
  • Determine the authentication/authorization endpoint during the OAuth + * `id_token` authentication flow.
  • + *
  • To retrieve the public signing keys via `jwks_uri` to verify the OIDC + * provider's ID token's signature.
  • + *
  • To determine the claims_supported to construct the user attributes to be + * returned in the additional user info response.
  • + *
+ * ID token validation will be performed as defined in the + * [spec](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). + */ + issuer: string; +} + /** * Defines the SAMLConfig class used to convert a client side configuration to its * server side representation. + * + * @internal */ export class SAMLConfig implements SAMLAuthProviderConfig { public readonly enabled: boolean; @@ -643,6 +1074,8 @@ export class SAMLConfig implements SAMLAuthProviderConfig { /** * Defines the OIDCConfig class used to convert a client side configuration to its * server side representation. + * + * @internal */ export class OIDCConfig implements OIDCAuthProviderConfig { public readonly enabled: boolean; diff --git a/src/auth/auth-namespace.ts b/src/auth/auth-namespace.ts new file mode 100644 index 0000000000..b4f8361f0f --- /dev/null +++ b/src/auth/auth-namespace.ts @@ -0,0 +1,181 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { App, getApp } from '../app/index'; +import { Auth } from './auth'; +import { FirebaseApp } from '../app/firebase-app'; + +// Import all public types with aliases, and re-export from the auth namespace. + +import { ActionCodeSettings as TActionCodeSettings } from './action-code-settings-builder'; + +import { Auth as TAuth } from './auth'; + +import { + AuthFactorType as TAuthFactorType, + AuthProviderConfig as TAuthProviderConfig, + AuthProviderConfigFilter as TAuthProviderConfigFilter, + CreateRequest as TCreateRequest, + CreateMultiFactorInfoRequest as TCreateMultiFactorInfoRequest, + CreatePhoneMultiFactorInfoRequest as TCreatePhoneMultiFactorInfoRequest, + EmailSignInProviderConfig as TEmailSignInProviderConfig, + ListProviderConfigResults as TListProviderConfigResults, + MultiFactorCreateSettings as TMultiFactorCreateSettings, + MultiFactorConfig as TMultiFactorConfig, + MultiFactorConfigState as TMultiFactorConfigState, + MultiFactorUpdateSettings as TMultiFactorUpdateSettings, + OIDCAuthProviderConfig as TOIDCAuthProviderConfig, + OIDCUpdateAuthProviderRequest as TOIDCUpdateAuthProviderRequest, + SAMLAuthProviderConfig as TSAMLAuthProviderConfig, + SAMLUpdateAuthProviderRequest as TSAMLUpdateAuthProviderRequest, + UpdateAuthProviderRequest as TUpdateAuthProviderRequest, + UpdateMultiFactorInfoRequest as TUpdateMultiFactorInfoRequest, + UpdatePhoneMultiFactorInfoRequest as TUpdatePhoneMultiFactorInfoRequest, + UpdateRequest as TUpdateRequest, +} from './auth-config'; + +import { + BaseAuth as TBaseAuth, + DeleteUsersResult as TDeleteUsersResult, + GetUsersResult as TGetUsersResult, + ListUsersResult as TListUsersResult, + SessionCookieOptions as TSessionCookieOptions, +} from './base-auth'; + +import { + EmailIdentifier as TEmailIdentifier, + PhoneIdentifier as TPhoneIdentifier, + ProviderIdentifier as TProviderIdentifier, + UserIdentifier as TUserIdentifier, + UidIdentifier as TUidIdentifier, +} from './identifier'; + +import { + CreateTenantRequest as TCreateTenantRequest, + Tenant as TTenant, + UpdateTenantRequest as TUpdateTenantRequest, +} from './tenant'; + +import { + ListTenantsResult as TListTenantsResult, + TenantAwareAuth as TTenantAwareAuth, + TenantManager as TTenantManager, +} from './tenant-manager'; + +import { DecodedIdToken as TDecodedIdToken } from './token-verifier'; + +import { + HashAlgorithmType as THashAlgorithmType, + UserImportOptions as TUserImportOptions, + UserImportRecord as TUserImportRecord, + UserImportResult as TUserImportResult, + UserMetadataRequest as TUserMetadataRequest, + UserProviderRequest as TUserProviderRequest, +} from './user-import-builder'; + +import { + MultiFactorInfo as TMultiFactorInfo, + MultiFactorSettings as TMultiFactorSettings, + PhoneMultiFactorInfo as TPhoneMultiFactorInfo, + UserInfo as TUserInfo, + UserMetadata as TUserMetadata, + UserRecord as TUserRecord, +} from './user-record'; + +/** + * Gets the {@link auth.Auth `Auth`} service for the default app or a + * given app. + * + * `admin.auth()` can be called with no arguments to access the default app's + * {@link auth.Auth `Auth`} service or as `admin.auth(app)` to access the + * {@link auth.Auth `Auth`} service associated with a specific app. + * + * @example + * ```javascript + * // Get the Auth service for the default app + * var defaultAuth = admin.auth(); + * ``` + * + * @example + * ```javascript + * // Get the Auth service for a given app + * var otherAuth = admin.auth(otherApp); + * ``` + * + */ +export function auth(app?: App): Auth { + if (typeof app === 'undefined') { + app = getApp(); + } + + const firebaseApp: FirebaseApp = app as FirebaseApp; + return firebaseApp.getOrInitService('auth', (app) => new Auth(app)); +} + +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace auth { + export type ActionCodeSettings = TActionCodeSettings; + export type Auth = TAuth; + export type AuthFactorType = TAuthFactorType; + export type AuthProviderConfig = TAuthProviderConfig; + export type AuthProviderConfigFilter = TAuthProviderConfigFilter; + export type BaseAuth = TBaseAuth; + export type CreateMultiFactorInfoRequest = TCreateMultiFactorInfoRequest; + export type CreatePhoneMultiFactorInfoRequest = TCreatePhoneMultiFactorInfoRequest; + export type CreateRequest = TCreateRequest; + export type CreateTenantRequest = TCreateTenantRequest; + export type DecodedIdToken = TDecodedIdToken; + export type DeleteUsersResult = TDeleteUsersResult; + export type EmailIdentifier = TEmailIdentifier; + export type EmailSignInProviderConfig = TEmailSignInProviderConfig; + export type GetUsersResult = TGetUsersResult; + export type HashAlgorithmType = THashAlgorithmType; + export type ListProviderConfigResults = TListProviderConfigResults; + export type ListTenantsResult = TListTenantsResult; + export type ListUsersResult = TListUsersResult; + export type MultiFactorCreateSettings = TMultiFactorCreateSettings; + export type MultiFactorConfig = TMultiFactorConfig; + export type MultiFactorConfigState = TMultiFactorConfigState; + export type MultiFactorInfo = TMultiFactorInfo; + export type MultiFactorUpdateSettings = TMultiFactorUpdateSettings; + export type MultiFactorSettings = TMultiFactorSettings; + export type OIDCAuthProviderConfig = TOIDCAuthProviderConfig; + export type OIDCUpdateAuthProviderRequest = TOIDCUpdateAuthProviderRequest; + export type PhoneIdentifier = TPhoneIdentifier; + export type PhoneMultiFactorInfo = TPhoneMultiFactorInfo; + export type ProviderIdentifier = TProviderIdentifier; + export type SAMLAuthProviderConfig = TSAMLAuthProviderConfig; + export type SAMLUpdateAuthProviderRequest = TSAMLUpdateAuthProviderRequest; + export type SessionCookieOptions = TSessionCookieOptions; + export type Tenant = TTenant; + export type TenantAwareAuth = TTenantAwareAuth; + export type TenantManager = TTenantManager; + export type UidIdentifier = TUidIdentifier; + export type UpdateAuthProviderRequest = TUpdateAuthProviderRequest; + export type UpdateMultiFactorInfoRequest = TUpdateMultiFactorInfoRequest; + export type UpdatePhoneMultiFactorInfoRequest = TUpdatePhoneMultiFactorInfoRequest; + export type UpdateRequest = TUpdateRequest; + export type UpdateTenantRequest = TUpdateTenantRequest; + export type UserIdentifier = TUserIdentifier; + export type UserImportOptions = TUserImportOptions; + export type UserImportRecord = TUserImportRecord; + export type UserImportResult = TUserImportResult; + export type UserInfo = TUserInfo; + export type UserMetadata = TUserMetadata; + export type UserMetadataRequest = TUserMetadataRequest; + export type UserProviderRequest = TUserProviderRequest; + export type UserRecord = TUserRecord; +} diff --git a/src/auth/auth.ts b/src/auth/auth.ts index c205605224..0378466b76 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -15,804 +15,26 @@ * limitations under the License. */ -import { UserRecord } from './user-record'; -import { - isUidIdentifier, isEmailIdentifier, isPhoneIdentifier, isProviderIdentifier, -} from './identifier'; -import { FirebaseApp } from '../app/firebase-app'; -import { FirebaseTokenGenerator, EmulatedSigner, cryptoSignerFromApp } from './token-generator'; -import { - AbstractAuthRequestHandler, AuthRequestHandler, TenantAwareAuthRequestHandler, useEmulator, -} from './auth-api-request'; -import { AuthClientErrorCode, FirebaseAuthError, ErrorInfo } from '../utils/error'; -import * as utils from '../utils/index'; -import * as validator from '../utils/validator'; -import { auth } from './index'; -import { - FirebaseTokenVerifier, createSessionCookieVerifier, createIdTokenVerifier, ALGORITHM_RS256 -} from './token-verifier'; -import { - SAMLConfig, OIDCConfig, OIDCConfigServerResponse, SAMLConfigServerResponse, -} from './auth-config'; +import { App } from '../app/index'; +import { AuthRequestHandler } from './auth-api-request'; import { TenantManager } from './tenant-manager'; - -import UserIdentifier = auth.UserIdentifier; -import CreateRequest = auth.CreateRequest; -import UpdateRequest = auth.UpdateRequest; -import ActionCodeSettings = auth.ActionCodeSettings; -import UserImportOptions = auth.UserImportOptions; -import UserImportRecord = auth.UserImportRecord; -import UserImportResult = auth.UserImportResult; -import AuthProviderConfig = auth.AuthProviderConfig; -import AuthProviderConfigFilter = auth.AuthProviderConfigFilter; -import ListProviderConfigResults = auth.ListProviderConfigResults; -import UpdateAuthProviderRequest = auth.UpdateAuthProviderRequest; -import GetUsersResult = auth.GetUsersResult; -import ListUsersResult = auth.ListUsersResult; -import DeleteUsersResult = auth.DeleteUsersResult; -import DecodedIdToken = auth.DecodedIdToken; -import SessionCookieOptions = auth.SessionCookieOptions; -import OIDCAuthProviderConfig = auth.OIDCAuthProviderConfig; -import SAMLAuthProviderConfig = auth.SAMLAuthProviderConfig; -import BaseAuthInterface = auth.BaseAuth; -import AuthInterface = auth.Auth; -import TenantAwareAuthInterface = auth.TenantAwareAuth; - -/** - * Base Auth class. Mainly used for user management APIs. - */ -export class BaseAuth implements BaseAuthInterface { - - protected readonly tokenGenerator: FirebaseTokenGenerator; - protected readonly idTokenVerifier: FirebaseTokenVerifier; - protected readonly sessionCookieVerifier: FirebaseTokenVerifier; - - /** - * The BaseAuth class constructor. - * - * @param app The FirebaseApp to associate with this Auth instance. - * @param authRequestHandler The RPC request handler for this instance. - * @param tokenGenerator Optional token generator. If not specified, a - * (non-tenant-aware) instance will be created. Use this paramter to - * specify a tenant-aware tokenGenerator. - * @constructor - */ - constructor(app: FirebaseApp, protected readonly authRequestHandler: T, tokenGenerator?: FirebaseTokenGenerator) { - if (tokenGenerator) { - this.tokenGenerator = tokenGenerator; - } else { - const cryptoSigner = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); - this.tokenGenerator = new FirebaseTokenGenerator(cryptoSigner); - } - - this.sessionCookieVerifier = createSessionCookieVerifier(app); - this.idTokenVerifier = createIdTokenVerifier(app); - } - - /** - * 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} A JWT for the provided payload. - */ - public createCustomToken(uid: string, developerClaims?: object): Promise { - return this.tokenGenerator.createCustomToken(uid, developerClaims); - } - - /** - * 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} A Promise that will be fulfilled after a successful - * verification. - */ - public verifyIdToken(idToken: string, checkRevoked = false): Promise { - return this.idTokenVerifier.verifyJWT(idToken) - .then((decodedIdToken: DecodedIdToken) => { - // Whether to check if the token was revoked. - if (!checkRevoked) { - return decodedIdToken; - } - return this.verifyDecodedJWTNotRevoked( - decodedIdToken, - AuthClientErrorCode.ID_TOKEN_REVOKED); - }); - } - - /** - * Looks up the user identified by the provided user id and returns a promise that is - * fulfilled with a user record for the given user if that user is found. - * - * @param {string} uid The uid of the user to look up. - * @return {Promise} A promise that resolves with the corresponding user record. - */ - public getUser(uid: string): Promise { - return this.authRequestHandler.getAccountInfoByUid(uid) - .then((response: any) => { - // Returns the user record populated with server response. - return new UserRecord(response.users[0]); - }); - } - - /** - * Looks up the user identified by the provided email and returns a promise that is - * fulfilled with a user record for the given user if that user is found. - * - * @param {string} email The email of the user to look up. - * @return {Promise} A promise that resolves with the corresponding user record. - */ - public getUserByEmail(email: string): Promise { - return this.authRequestHandler.getAccountInfoByEmail(email) - .then((response: any) => { - // Returns the user record populated with server response. - return new UserRecord(response.users[0]); - }); - } - - /** - * Looks up the user identified by the provided phone number and returns a promise that is - * fulfilled with a user record for the given user if that user is found. - * - * @param {string} phoneNumber The phone number of the user to look up. - * @return {Promise} A promise that resolves with the corresponding user record. - */ - public getUserByPhoneNumber(phoneNumber: string): Promise { - return this.authRequestHandler.getAccountInfoByPhoneNumber(phoneNumber) - .then((response: any) => { - // Returns the user record populated with server response. - return new UserRecord(response.users[0]); - }); - } - - /** - * Gets the user data corresponding to the specified identifiers. - * - * There are no ordering guarantees; in particular, the nth entry in the result list is not - * guaranteed to correspond to the nth entry in the input parameters list. - * - * Only a maximum of 100 identifiers may be supplied. If more than 100 identifiers are supplied, - * this method will immediately throw a FirebaseAuthError. - * - * @param identifiers The identifiers used to indicate which user records should be returned. Must - * have <= 100 entries. - * @return {Promise} A promise that resolves to the corresponding user records. - * @throws FirebaseAuthError If any of the identifiers are invalid or if more than 100 - * identifiers are specified. - */ - public getUsers(identifiers: UserIdentifier[]): Promise { - if (!validator.isArray(identifiers)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, '`identifiers` parameter must be an array'); - } - return this.authRequestHandler - .getAccountInfoByIdentifiers(identifiers) - .then((response: any) => { - /** - * Checks if the specified identifier is within the list of - * UserRecords. - */ - const isUserFound = ((id: UserIdentifier, userRecords: UserRecord[]): boolean => { - return !!userRecords.find((userRecord) => { - if (isUidIdentifier(id)) { - return id.uid === userRecord.uid; - } else if (isEmailIdentifier(id)) { - return id.email === userRecord.email; - } else if (isPhoneIdentifier(id)) { - return id.phoneNumber === userRecord.phoneNumber; - } else if (isProviderIdentifier(id)) { - const matchingUserInfo = userRecord.providerData.find((userInfo) => { - return id.providerId === userInfo.providerId; - }); - return !!matchingUserInfo && id.providerUid === matchingUserInfo.uid; - } else { - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'Unhandled identifier type'); - } - }); - }); - - const users = response.users ? response.users.map((user: any) => new UserRecord(user)) : []; - const notFound = identifiers.filter((id) => !isUserFound(id, users)); - - return { users, notFound }; - }); - } - - /** - * Exports a batch of user accounts. Batch size is determined by the maxResults argument. - * Starting point of the batch is determined by the pageToken argument. - * - * @param {number=} maxResults The page size, 1000 if undefined. This is also the maximum - * allowed limit. - * @param {string=} pageToken The next page token. If not specified, returns users starting - * without any offset. - * @return {Promise<{users: UserRecord[], pageToken?: string}>} A promise that resolves with - * the current batch of downloaded users and the next page token. For the last page, an - * empty list of users and no page token are returned. - */ - public listUsers(maxResults?: number, pageToken?: string): Promise { - return this.authRequestHandler.downloadAccount(maxResults, pageToken) - .then((response: any) => { - // List of users to return. - const users: UserRecord[] = []; - // Convert each user response to a UserRecord. - response.users.forEach((userResponse: any) => { - users.push(new UserRecord(userResponse)); - }); - // Return list of user records and the next page token if available. - const result = { - users, - pageToken: response.nextPageToken, - }; - // Delete result.pageToken if undefined. - if (typeof result.pageToken === 'undefined') { - delete result.pageToken; - } - return result; - }); - } - - /** - * Creates a new user with the properties provided. - * - * @param {CreateRequest} properties The properties to set on the new user record to be created. - * @return {Promise} A promise that resolves with the newly created user record. - */ - public createUser(properties: CreateRequest): Promise { - return this.authRequestHandler.createNewAccount(properties) - .then((uid) => { - // Return the corresponding user record. - return this.getUser(uid); - }) - .catch((error) => { - if (error.code === 'auth/user-not-found') { - // Something must have happened after creating the user and then retrieving it. - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'Unable to create the user record provided.'); - } - throw error; - }); - } - - /** - * Deletes the user identified by the provided user id and returns a promise that is - * fulfilled when the user is found and successfully deleted. - * - * @param {string} uid The uid of the user to delete. - * @return {Promise} A promise that resolves when the user is successfully deleted. - */ - public deleteUser(uid: string): Promise { - return this.authRequestHandler.deleteAccount(uid) - .then(() => { - // Return nothing on success. - }); - } - - public deleteUsers(uids: string[]): Promise { - if (!validator.isArray(uids)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, '`uids` parameter must be an array'); - } - return this.authRequestHandler.deleteAccounts(uids, /*force=*/true) - .then((batchDeleteAccountsResponse) => { - const result: DeleteUsersResult = { - failureCount: 0, - successCount: uids.length, - errors: [], - }; - - if (!validator.isNonEmptyArray(batchDeleteAccountsResponse.errors)) { - return result; - } - - result.failureCount = batchDeleteAccountsResponse.errors.length; - result.successCount = uids.length - batchDeleteAccountsResponse.errors.length; - result.errors = batchDeleteAccountsResponse.errors.map((batchDeleteErrorInfo) => { - if (batchDeleteErrorInfo.index === undefined) { - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'Corrupt BatchDeleteAccountsResponse detected'); - } - - const errMsgToError = (msg?: string): FirebaseAuthError => { - // We unconditionally set force=true, so the 'NOT_DISABLED' error - // should not be possible. - const code = msg && msg.startsWith('NOT_DISABLED') ? - AuthClientErrorCode.USER_NOT_DISABLED : AuthClientErrorCode.INTERNAL_ERROR; - return new FirebaseAuthError(code, batchDeleteErrorInfo.message); - }; - - return { - index: batchDeleteErrorInfo.index, - error: errMsgToError(batchDeleteErrorInfo.message), - }; - }); - - return result; - }); - } - - /** - * Updates an existing user with the properties provided. - * - * @param {string} uid The uid identifier of the user to update. - * @param {UpdateRequest} properties The properties to update on the existing user. - * @return {Promise} A promise that resolves with the modified user record. - */ - public updateUser(uid: string, properties: UpdateRequest): Promise { - return this.authRequestHandler.updateExistingAccount(uid, properties) - .then((existingUid) => { - // Return the corresponding user record. - return this.getUser(existingUid); - }); - } - - /** - * Sets additional developer claims on an existing user identified by the provided UID. - * - * @param {string} uid The user to edit. - * @param {object} customUserClaims The developer claims to set. - * @return {Promise} A promise that resolves when the operation completes - * successfully. - */ - public setCustomUserClaims(uid: string, customUserClaims: object | null): Promise { - return this.authRequestHandler.setCustomUserClaims(uid, customUserClaims) - .then(() => { - // Return nothing on success. - }); - } - - /** - * Revokes all refresh tokens for the specified user identified by the provided UID. - * In addition to revoking all refresh tokens for a user, all ID tokens issued before - * revocation will also be revoked on the Auth backend. Any request with an ID token - * generated before revocation will be rejected with a token expired error. - * - * @param {string} uid The user whose tokens are to be revoked. - * @return {Promise} A promise that resolves when the operation completes - * successfully. - */ - public revokeRefreshTokens(uid: string): Promise { - return this.authRequestHandler.revokeRefreshTokens(uid) - .then(() => { - // Return nothing on success. - }); - } - - /** - * Imports the list of users provided to Firebase Auth. This is useful when - * migrating from an external authentication system without having to use the Firebase CLI SDK. - * At most, 1000 users are allowed to be imported one at a time. - * When importing a list of password users, UserImportOptions are required to be specified. - * - * @param {UserImportRecord[]} users The list of user records to import to Firebase Auth. - * @param {UserImportOptions=} options The user import options, required when the users provided - * include password credentials. - * @return {Promise} A promise that resolves when the operation completes - * with the result of the import. This includes the number of successful imports, the number - * of failed uploads and their corresponding errors. - */ - public importUsers( - users: UserImportRecord[], options?: UserImportOptions): Promise { - return this.authRequestHandler.uploadAccount(users, options); - } - - /** - * 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} A promise that resolves on success with the created session cookie. - */ - public createSessionCookie( - idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { - // Return rejected promise if expiresIn is not available. - if (!validator.isNonNullObject(sessionCookieOptions) || - !validator.isNumber(sessionCookieOptions.expiresIn)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); - } - return this.authRequestHandler.createSessionCookie( - idToken, sessionCookieOptions.expiresIn); - } - - /** - * 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} A Promise that will be fulfilled after a successful - * verification. - */ - public verifySessionCookie( - sessionCookie: string, checkRevoked = false): Promise { - return this.sessionCookieVerifier.verifyJWT(sessionCookie) - .then((decodedIdToken: DecodedIdToken) => { - // Whether to check if the token was revoked. - if (!checkRevoked) { - return decodedIdToken; - } - return this.verifyDecodedJWTNotRevoked( - decodedIdToken, - AuthClientErrorCode.SESSION_COOKIE_REVOKED); - }); - } - - /** - * Generates the out of band email action link for password reset flows for the - * email specified using the action code settings provided. - * Returns a promise that resolves with the generated link. - * - * @param {string} email The email of the user whose password is to be reset. - * @param {ActionCodeSettings=} actionCodeSettings The optional action code setings which defines whether - * the link is to be handled by a mobile app and the additional state information to be passed in the - * deep link, etc. - * @return {Promise} A promise that resolves with the password reset link. - */ - public generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise { - return this.authRequestHandler.getEmailActionLink('PASSWORD_RESET', email, actionCodeSettings); - } - - /** - * Generates the out of band email action link for email verification flows for the - * email specified using the action code settings provided. - * Returns a promise that resolves with the generated link. - * - * @param {string} email The email of the user to be verified. - * @param {ActionCodeSettings=} actionCodeSettings The optional action code setings which defines whether - * the link is to be handled by a mobile app and the additional state information to be passed in the - * deep link, etc. - * @return {Promise} A promise that resolves with the email verification link. - */ - public generateEmailVerificationLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise { - return this.authRequestHandler.getEmailActionLink('VERIFY_EMAIL', email, actionCodeSettings); - } - - /** - * Generates the out of band email action link for email link sign-in flows for the - * email specified using the action code settings provided. - * Returns a promise that resolves with the generated link. - * - * @param {string} email The email of the user signing in. - * @param {ActionCodeSettings} actionCodeSettings The required action code setings which defines whether - * the link is to be handled by a mobile app and the additional state information to be passed in the - * deep link, etc. - * @return {Promise} A promise that resolves with the email sign-in link. - */ - public generateSignInWithEmailLink(email: string, actionCodeSettings: ActionCodeSettings): Promise { - return this.authRequestHandler.getEmailActionLink('EMAIL_SIGNIN', email, actionCodeSettings); - } - - /** - * Returns the list of existing provider configuation matching the filter provided. - * At most, 100 provider configs are allowed to be imported at a time. - * - * @param {AuthProviderConfigFilter} options The provider config filter to apply. - * @return {Promise} A promise that resolves with the list of provider configs - * meeting the filter requirements. - */ - public listProviderConfigs(options: AuthProviderConfigFilter): Promise { - const processResponse = (response: any, providerConfigs: AuthProviderConfig[]): ListProviderConfigResults => { - // Return list of provider configuration and the next page token if available. - const result: ListProviderConfigResults = { - providerConfigs, - }; - // Delete result.pageToken if undefined. - if (Object.prototype.hasOwnProperty.call(response, 'nextPageToken')) { - result.pageToken = response.nextPageToken; - } - return result; - }; - if (options && options.type === 'oidc') { - return this.authRequestHandler.listOAuthIdpConfigs(options.maxResults, options.pageToken) - .then((response: any) => { - // List of provider configurations to return. - const providerConfigs: OIDCConfig[] = []; - // Convert each provider config response to a OIDCConfig. - response.oauthIdpConfigs.forEach((configResponse: any) => { - providerConfigs.push(new OIDCConfig(configResponse)); - }); - // Return list of provider configuration and the next page token if available. - return processResponse(response, providerConfigs); - }); - } else if (options && options.type === 'saml') { - return this.authRequestHandler.listInboundSamlConfigs(options.maxResults, options.pageToken) - .then((response: any) => { - // List of provider configurations to return. - const providerConfigs: SAMLConfig[] = []; - // Convert each provider config response to a SAMLConfig. - response.inboundSamlConfigs.forEach((configResponse: any) => { - providerConfigs.push(new SAMLConfig(configResponse)); - }); - // Return list of provider configuration and the next page token if available. - return processResponse(response, providerConfigs); - }); - } - return Promise.reject( - new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - '"AuthProviderConfigFilter.type" must be either "saml" or "oidc"')); - } - - /** - * Looks up an Auth provider configuration by ID. - * Returns a promise that resolves with the provider configuration corresponding to the provider ID specified. - * - * @param {string} providerId The provider ID corresponding to the provider config to return. - * @return {Promise} - */ - public getProviderConfig(providerId: string): Promise { - if (OIDCConfig.isProviderId(providerId)) { - return this.authRequestHandler.getOAuthIdpConfig(providerId) - .then((response: OIDCConfigServerResponse) => { - return new OIDCConfig(response); - }); - } else if (SAMLConfig.isProviderId(providerId)) { - return this.authRequestHandler.getInboundSamlConfig(providerId) - .then((response: SAMLConfigServerResponse) => { - return new SAMLConfig(response); - }); - } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); - } - - /** - * Deletes the provider configuration corresponding to the provider ID passed. - * - * @param {string} providerId The provider ID corresponding to the provider config to delete. - * @return {Promise} A promise that resolves on completion. - */ - public deleteProviderConfig(providerId: string): Promise { - if (OIDCConfig.isProviderId(providerId)) { - return this.authRequestHandler.deleteOAuthIdpConfig(providerId); - } else if (SAMLConfig.isProviderId(providerId)) { - return this.authRequestHandler.deleteInboundSamlConfig(providerId); - } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); - } - - /** - * Returns a promise that resolves with the updated AuthProviderConfig when the provider configuration corresponding - * to the provider ID specified is updated with the specified configuration. - * - * @param {string} providerId The provider ID corresponding to the provider config to update. - * @param {UpdateAuthProviderRequest} updatedConfig The updated configuration. - * @return {Promise} A promise that resolves with the updated provider configuration. - */ - public updateProviderConfig( - providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise { - if (!validator.isNonNullObject(updatedConfig)) { - return Promise.reject(new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, - 'Request is missing "UpdateAuthProviderRequest" configuration.', - )); - } - if (OIDCConfig.isProviderId(providerId)) { - return this.authRequestHandler.updateOAuthIdpConfig(providerId, updatedConfig) - .then((response) => { - return new OIDCConfig(response); - }); - } else if (SAMLConfig.isProviderId(providerId)) { - return this.authRequestHandler.updateInboundSamlConfig(providerId, updatedConfig) - .then((response) => { - return new SAMLConfig(response); - }); - } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); - } - - /** - * Returns a promise that resolves with the newly created AuthProviderConfig when the new provider configuration is - * created. - * @param {AuthProviderConfig} config The provider configuration to create. - * @return {Promise} A promise that resolves with the created provider configuration. - */ - public createProviderConfig(config: AuthProviderConfig): Promise { - if (!validator.isNonNullObject(config)) { - return Promise.reject(new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, - 'Request is missing "AuthProviderConfig" configuration.', - )); - } - if (OIDCConfig.isProviderId(config.providerId)) { - return this.authRequestHandler.createOAuthIdpConfig(config as OIDCAuthProviderConfig) - .then((response) => { - return new OIDCConfig(response); - }); - } else if (SAMLConfig.isProviderId(config.providerId)) { - return this.authRequestHandler.createInboundSamlConfig(config as SAMLAuthProviderConfig) - .then((response) => { - return new SAMLConfig(response); - }); - } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); - } - - /** - * Verifies the decoded Firebase issued JWT is not revoked. Returns a promise that resolves - * with the decoded claims on success. Rejects the promise with revocation error if revoked. - * - * @param {DecodedIdToken} decodedIdToken The JWT's decoded claims. - * @param {ErrorInfo} revocationErrorInfo The revocation error info to throw on revocation - * detection. - * @return {Promise} A Promise that will be fulfilled after a successful - * verification. - */ - private verifyDecodedJWTNotRevoked( - decodedIdToken: DecodedIdToken, revocationErrorInfo: ErrorInfo): Promise { - // Get tokens valid after time for the corresponding user. - return this.getUser(decodedIdToken.sub) - .then((user: UserRecord) => { - // If no tokens valid after time available, token is not revoked. - if (user.tokensValidAfterTime) { - // Get the ID token authentication time and convert to milliseconds UTC. - const authTimeUtc = decodedIdToken.auth_time * 1000; - // Get user tokens valid after time in milliseconds UTC. - const validSinceUtc = new Date(user.tokensValidAfterTime).getTime(); - // Check if authentication time is older than valid since time. - if (authTimeUtc < validSinceUtc) { - throw new FirebaseAuthError(revocationErrorInfo); - } - } - // All checks above passed. Return the decoded token. - return decodedIdToken; - }); - } - - /** - * Enable or disable ID token verification. This is used to safely short-circuit token verification with the - * Auth emulator. When disabled ONLY unsigned tokens will pass verification, production tokens will not pass. - * - * WARNING: This is a dangerous method that will compromise your app's security and break your app in - * production. Developers should never call this method, it is for internal testing use only. - * - * @internal - */ - // @ts-expect-error: this method appears unused but is used privately. - private setJwtVerificationEnabled(enabled: boolean): void { - if (!enabled && !useEmulator()) { - // We only allow verification to be disabled in conjunction with - // the emulator environment variable. - throw new Error('This method is only available when connected to the Authentication emulator.'); - } - - const algorithm = enabled ? ALGORITHM_RS256 : 'none'; - this.idTokenVerifier.setAlgorithm(algorithm); - this.sessionCookieVerifier.setAlgorithm(algorithm); - } -} - - -/** - * The tenant aware Auth class. - */ -export class TenantAwareAuth - extends BaseAuth - implements TenantAwareAuthInterface { - - public readonly tenantId: string; - - /** - * The TenantAwareAuth class constructor. - * - * @param {object} app The app that created this tenant. - * @param tenantId The corresponding tenant ID. - * @constructor - */ - constructor(app: FirebaseApp, tenantId: string) { - const cryptoSigner = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); - const tokenGenerator = new FirebaseTokenGenerator(cryptoSigner, tenantId); - super(app, new TenantAwareAuthRequestHandler(app, tenantId), tokenGenerator); - utils.addReadonlyGetter(this, 'tenantId', tenantId); - } - - /** - * 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} A Promise that will be fulfilled after a successful - * verification. - */ - public verifyIdToken(idToken: string, checkRevoked = false): Promise { - 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} A promise that resolves on success with the created session cookie. - */ - public createSessionCookie( - idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { - // 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(() => { - 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} A Promise that will be fulfilled after a successful - * verification. - */ - public verifySessionCookie( - sessionCookie: string, checkRevoked = false): Promise { - return super.verifySessionCookie(sessionCookie, checkRevoked) - .then((decodedClaims) => { - if (decodedClaims.firebase.tenant !== this.tenantId) { - throw new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); - } - return decodedClaims; - }); - } -} - +import { BaseAuth } from './base-auth'; /** * Auth service bound to the provided app. * An Auth instance can have multiple tenants. */ -export class Auth extends BaseAuth implements AuthInterface { +export class Auth extends BaseAuth { private readonly tenantManager_: TenantManager; - private readonly app_: FirebaseApp; + private readonly app_: App; /** - * @param {object} app The app for this Auth service. + * @param app The app for this Auth service. * @constructor + * @internal */ - constructor(app: FirebaseApp) { + constructor(app: App) { super(app, new AuthRequestHandler(app)); this.app_ = app; this.tenantManager_ = new TenantManager(app); @@ -821,9 +43,9 @@ export class Auth extends BaseAuth implements AuthInterface /** * Returns the app associated with this Auth instance. * - * @return {FirebaseApp} The app associated with this Auth instance. + * @return The app associated with this Auth instance. */ - get app(): FirebaseApp { + get app(): App { return this.app_; } diff --git a/src/auth/base-auth.ts b/src/auth/base-auth.ts new file mode 100644 index 0000000000..502e4b2f1b --- /dev/null +++ b/src/auth/base-auth.ts @@ -0,0 +1,757 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { App } from '../app'; +import { FirebaseArrayIndexError } from '../firebase-namespace-api'; +import { AuthClientErrorCode, ErrorInfo, FirebaseAuthError } from '../utils/error'; +import * as validator from '../utils/validator'; + +import { AbstractAuthRequestHandler, useEmulator } from './auth-api-request'; +import { FirebaseTokenGenerator, EmulatedSigner, cryptoSignerFromApp } from './token-generator'; +import { + FirebaseTokenVerifier, createSessionCookieVerifier, createIdTokenVerifier, ALGORITHM_RS256, + DecodedIdToken, +} from './token-verifier'; +import { + AuthProviderConfig, SAMLAuthProviderConfig, AuthProviderConfigFilter, ListProviderConfigResults, + SAMLConfig, OIDCConfig, OIDCConfigServerResponse, SAMLConfigServerResponse, + UpdateAuthProviderRequest, OIDCAuthProviderConfig, CreateRequest, UpdateRequest, +} from './auth-config'; +import { UserRecord } from './user-record'; +import { + UserIdentifier, isUidIdentifier, isEmailIdentifier, isPhoneIdentifier, isProviderIdentifier, +} from './identifier'; +import { UserImportOptions, UserImportRecord, UserImportResult } from './user-import-builder'; +import { ActionCodeSettings } from './action-code-settings-builder'; + +/** Represents the result of the {@link auth.Auth.getUsers} API. */ +export interface GetUsersResult { + /** + * Set of user records, corresponding to the set of users that were + * requested. Only users that were found are listed here. The result set is + * unordered. + */ + users: UserRecord[]; + + /** Set of identifiers that were requested, but not found. */ + notFound: UserIdentifier[]; +} + +/** + * Interface representing the object returned from a + * {@link auth.Auth.listUsers `listUsers()`} operation. Contains the list + * of users for the current batch and the next page token if available. + */ +export interface ListUsersResult { + + /** + * The list of {@link auth.UserRecord `UserRecord`} objects for the + * current downloaded batch. + */ + users: UserRecord[]; + + /** + * The next page token if available. This is needed for the next batch download. + */ + pageToken?: string; +} + +/** + * Represents the result of the + * {@link auth.Auth.deleteUsers `deleteUsers()`} + * API. + */ +export interface DeleteUsersResult { + /** + * The number of user records that failed to be deleted (possibly zero). + */ + failureCount: number; + + /** + * The number of users that were deleted successfully (possibly zero). + * Users that did not exist prior to calling `deleteUsers()` are + * considered to be successfully deleted. + */ + successCount: number; + + /** + * A list of `FirebaseArrayIndexError` instances describing the errors that + * were encountered during the deletion. Length of this list is equal to + * the return value of [`failureCount`](#failureCount). + */ + errors: FirebaseArrayIndexError[]; +} + +/** + * Interface representing the session cookie options needed for the + * {@link auth.Auth.createSessionCookie `createSessionCookie()`} method. + */ +export interface SessionCookieOptions { + + /** + * The session cookie custom expiration in milliseconds. The minimum allowed is + * 5 minutes and the maxium allowed is 2 weeks. + */ + expiresIn: number; +} + +/** + * Base Auth class. Mainly used for user management APIs. + */ +export class BaseAuth { + + /** @internal */ + protected readonly tokenGenerator: FirebaseTokenGenerator; + /** @internal */ + protected readonly idTokenVerifier: FirebaseTokenVerifier; + /** @internal */ + protected readonly sessionCookieVerifier: FirebaseTokenVerifier; + + /** + * The BaseAuth class constructor. + * + * @param app The FirebaseApp to associate with this Auth instance. + * @param authRequestHandler The RPC request handler for this instance. + * @param tokenGenerator Optional token generator. If not specified, a + * (non-tenant-aware) instance will be created. Use this paramter to + * specify a tenant-aware tokenGenerator. + * @constructor + * @internal + */ + constructor( + app: App, + /** @internal */protected readonly authRequestHandler: AbstractAuthRequestHandler, + tokenGenerator?: FirebaseTokenGenerator) { + if (tokenGenerator) { + this.tokenGenerator = tokenGenerator; + } else { + const cryptoSigner = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); + this.tokenGenerator = new FirebaseTokenGenerator(cryptoSigner); + } + + this.sessionCookieVerifier = createSessionCookieVerifier(app); + this.idTokenVerifier = createIdTokenVerifier(app); + } + + /** + * 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} A JWT for the provided payload. + */ + public createCustomToken(uid: string, developerClaims?: object): Promise { + return this.tokenGenerator.createCustomToken(uid, developerClaims); + } + + /** + * 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} A Promise that will be fulfilled after a successful + * verification. + */ + public verifyIdToken(idToken: string, checkRevoked = false): Promise { + return this.idTokenVerifier.verifyJWT(idToken) + .then((decodedIdToken: DecodedIdToken) => { + // Whether to check if the token was revoked. + if (!checkRevoked) { + return decodedIdToken; + } + return this.verifyDecodedJWTNotRevoked( + decodedIdToken, + AuthClientErrorCode.ID_TOKEN_REVOKED); + }); + } + + /** + * Looks up the user identified by the provided user id and returns a promise that is + * fulfilled with a user record for the given user if that user is found. + * + * @param {string} uid The uid of the user to look up. + * @return {Promise} A promise that resolves with the corresponding user record. + */ + public getUser(uid: string): Promise { + return this.authRequestHandler.getAccountInfoByUid(uid) + .then((response: any) => { + // Returns the user record populated with server response. + return new UserRecord(response.users[0]); + }); + } + + /** + * Looks up the user identified by the provided email and returns a promise that is + * fulfilled with a user record for the given user if that user is found. + * + * @param {string} email The email of the user to look up. + * @return {Promise} A promise that resolves with the corresponding user record. + */ + public getUserByEmail(email: string): Promise { + return this.authRequestHandler.getAccountInfoByEmail(email) + .then((response: any) => { + // Returns the user record populated with server response. + return new UserRecord(response.users[0]); + }); + } + + /** + * Looks up the user identified by the provided phone number and returns a promise that is + * fulfilled with a user record for the given user if that user is found. + * + * @param {string} phoneNumber The phone number of the user to look up. + * @return {Promise} A promise that resolves with the corresponding user record. + */ + public getUserByPhoneNumber(phoneNumber: string): Promise { + return this.authRequestHandler.getAccountInfoByPhoneNumber(phoneNumber) + .then((response: any) => { + // Returns the user record populated with server response. + return new UserRecord(response.users[0]); + }); + } + + /** + * Gets the user data corresponding to the specified identifiers. + * + * There are no ordering guarantees; in particular, the nth entry in the result list is not + * guaranteed to correspond to the nth entry in the input parameters list. + * + * Only a maximum of 100 identifiers may be supplied. If more than 100 identifiers are supplied, + * this method will immediately throw a FirebaseAuthError. + * + * @param identifiers The identifiers used to indicate which user records should be returned. Must + * have <= 100 entries. + * @return {Promise} A promise that resolves to the corresponding user records. + * @throws FirebaseAuthError If any of the identifiers are invalid or if more than 100 + * identifiers are specified. + */ + public getUsers(identifiers: UserIdentifier[]): Promise { + if (!validator.isArray(identifiers)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, '`identifiers` parameter must be an array'); + } + return this.authRequestHandler + .getAccountInfoByIdentifiers(identifiers) + .then((response: any) => { + /** + * Checks if the specified identifier is within the list of + * UserRecords. + */ + const isUserFound = ((id: UserIdentifier, userRecords: UserRecord[]): boolean => { + return !!userRecords.find((userRecord) => { + if (isUidIdentifier(id)) { + return id.uid === userRecord.uid; + } else if (isEmailIdentifier(id)) { + return id.email === userRecord.email; + } else if (isPhoneIdentifier(id)) { + return id.phoneNumber === userRecord.phoneNumber; + } else if (isProviderIdentifier(id)) { + const matchingUserInfo = userRecord.providerData.find((userInfo) => { + return id.providerId === userInfo.providerId; + }); + return !!matchingUserInfo && id.providerUid === matchingUserInfo.uid; + } else { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'Unhandled identifier type'); + } + }); + }); + + const users = response.users ? response.users.map((user: any) => new UserRecord(user)) : []; + const notFound = identifiers.filter((id) => !isUserFound(id, users)); + + return { users, notFound }; + }); + } + + /** + * Exports a batch of user accounts. Batch size is determined by the maxResults argument. + * Starting point of the batch is determined by the pageToken argument. + * + * @param {number=} maxResults The page size, 1000 if undefined. This is also the maximum + * allowed limit. + * @param {string=} pageToken The next page token. If not specified, returns users starting + * without any offset. + * @return {Promise<{users: UserRecord[], pageToken?: string}>} A promise that resolves with + * the current batch of downloaded users and the next page token. For the last page, an + * empty list of users and no page token are returned. + */ + public listUsers(maxResults?: number, pageToken?: string): Promise { + return this.authRequestHandler.downloadAccount(maxResults, pageToken) + .then((response: any) => { + // List of users to return. + const users: UserRecord[] = []; + // Convert each user response to a UserRecord. + response.users.forEach((userResponse: any) => { + users.push(new UserRecord(userResponse)); + }); + // Return list of user records and the next page token if available. + const result = { + users, + pageToken: response.nextPageToken, + }; + // Delete result.pageToken if undefined. + if (typeof result.pageToken === 'undefined') { + delete result.pageToken; + } + return result; + }); + } + + /** + * Creates a new user with the properties provided. + * + * @param {CreateRequest} properties The properties to set on the new user record to be created. + * @return {Promise} A promise that resolves with the newly created user record. + */ + public createUser(properties: CreateRequest): Promise { + return this.authRequestHandler.createNewAccount(properties) + .then((uid) => { + // Return the corresponding user record. + return this.getUser(uid); + }) + .catch((error) => { + if (error.code === 'auth/user-not-found') { + // Something must have happened after creating the user and then retrieving it. + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'Unable to create the user record provided.'); + } + throw error; + }); + } + + /** + * Deletes the user identified by the provided user id and returns a promise that is + * fulfilled when the user is found and successfully deleted. + * + * @param {string} uid The uid of the user to delete. + * @return {Promise} A promise that resolves when the user is successfully deleted. + */ + public deleteUser(uid: string): Promise { + return this.authRequestHandler.deleteAccount(uid) + .then(() => { + // Return nothing on success. + }); + } + + public deleteUsers(uids: string[]): Promise { + if (!validator.isArray(uids)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, '`uids` parameter must be an array'); + } + return this.authRequestHandler.deleteAccounts(uids, /*force=*/true) + .then((batchDeleteAccountsResponse) => { + const result: DeleteUsersResult = { + failureCount: 0, + successCount: uids.length, + errors: [], + }; + + if (!validator.isNonEmptyArray(batchDeleteAccountsResponse.errors)) { + return result; + } + + result.failureCount = batchDeleteAccountsResponse.errors.length; + result.successCount = uids.length - batchDeleteAccountsResponse.errors.length; + result.errors = batchDeleteAccountsResponse.errors.map((batchDeleteErrorInfo) => { + if (batchDeleteErrorInfo.index === undefined) { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'Corrupt BatchDeleteAccountsResponse detected'); + } + + const errMsgToError = (msg?: string): FirebaseAuthError => { + // We unconditionally set force=true, so the 'NOT_DISABLED' error + // should not be possible. + const code = msg && msg.startsWith('NOT_DISABLED') ? + AuthClientErrorCode.USER_NOT_DISABLED : AuthClientErrorCode.INTERNAL_ERROR; + return new FirebaseAuthError(code, batchDeleteErrorInfo.message); + }; + + return { + index: batchDeleteErrorInfo.index, + error: errMsgToError(batchDeleteErrorInfo.message), + }; + }); + + return result; + }); + } + + /** + * Updates an existing user with the properties provided. + * + * @param {string} uid The uid identifier of the user to update. + * @param {UpdateRequest} properties The properties to update on the existing user. + * @return {Promise} A promise that resolves with the modified user record. + */ + public updateUser(uid: string, properties: UpdateRequest): Promise { + return this.authRequestHandler.updateExistingAccount(uid, properties) + .then((existingUid) => { + // Return the corresponding user record. + return this.getUser(existingUid); + }); + } + + /** + * Sets additional developer claims on an existing user identified by the provided UID. + * + * @param {string} uid The user to edit. + * @param {object} customUserClaims The developer claims to set. + * @return {Promise} A promise that resolves when the operation completes + * successfully. + */ + public setCustomUserClaims(uid: string, customUserClaims: object | null): Promise { + return this.authRequestHandler.setCustomUserClaims(uid, customUserClaims) + .then(() => { + // Return nothing on success. + }); + } + + /** + * Revokes all refresh tokens for the specified user identified by the provided UID. + * In addition to revoking all refresh tokens for a user, all ID tokens issued before + * revocation will also be revoked on the Auth backend. Any request with an ID token + * generated before revocation will be rejected with a token expired error. + * + * @param {string} uid The user whose tokens are to be revoked. + * @return {Promise} A promise that resolves when the operation completes + * successfully. + */ + public revokeRefreshTokens(uid: string): Promise { + return this.authRequestHandler.revokeRefreshTokens(uid) + .then(() => { + // Return nothing on success. + }); + } + + /** + * Imports the list of users provided to Firebase Auth. This is useful when + * migrating from an external authentication system without having to use the Firebase CLI SDK. + * At most, 1000 users are allowed to be imported one at a time. + * When importing a list of password users, UserImportOptions are required to be specified. + * + * @param {UserImportRecord[]} users The list of user records to import to Firebase Auth. + * @param {UserImportOptions=} options The user import options, required when the users provided + * include password credentials. + * @return {Promise} A promise that resolves when the operation completes + * with the result of the import. This includes the number of successful imports, the number + * of failed uploads and their corresponding errors. + */ + public importUsers( + users: UserImportRecord[], options?: UserImportOptions): Promise { + return this.authRequestHandler.uploadAccount(users, options); + } + + /** + * 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} A promise that resolves on success with the created session cookie. + */ + public createSessionCookie( + idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { + // Return rejected promise if expiresIn is not available. + if (!validator.isNonNullObject(sessionCookieOptions) || + !validator.isNumber(sessionCookieOptions.expiresIn)) { + return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); + } + return this.authRequestHandler.createSessionCookie( + idToken, sessionCookieOptions.expiresIn); + } + + /** + * 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} A Promise that will be fulfilled after a successful + * verification. + */ + public verifySessionCookie( + sessionCookie: string, checkRevoked = false): Promise { + return this.sessionCookieVerifier.verifyJWT(sessionCookie) + .then((decodedIdToken: DecodedIdToken) => { + // Whether to check if the token was revoked. + if (!checkRevoked) { + return decodedIdToken; + } + return this.verifyDecodedJWTNotRevoked( + decodedIdToken, + AuthClientErrorCode.SESSION_COOKIE_REVOKED); + }); + } + + /** + * Generates the out of band email action link for password reset flows for the + * email specified using the action code settings provided. + * Returns a promise that resolves with the generated link. + * + * @param {string} email The email of the user whose password is to be reset. + * @param {ActionCodeSettings=} actionCodeSettings The optional action code setings which defines whether + * the link is to be handled by a mobile app and the additional state information to be passed in the + * deep link, etc. + * @return {Promise} A promise that resolves with the password reset link. + */ + public generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise { + return this.authRequestHandler.getEmailActionLink('PASSWORD_RESET', email, actionCodeSettings); + } + + /** + * Generates the out of band email action link for email verification flows for the + * email specified using the action code settings provided. + * Returns a promise that resolves with the generated link. + * + * @param {string} email The email of the user to be verified. + * @param {ActionCodeSettings=} actionCodeSettings The optional action code setings which defines whether + * the link is to be handled by a mobile app and the additional state information to be passed in the + * deep link, etc. + * @return {Promise} A promise that resolves with the email verification link. + */ + public generateEmailVerificationLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise { + return this.authRequestHandler.getEmailActionLink('VERIFY_EMAIL', email, actionCodeSettings); + } + + /** + * Generates the out of band email action link for email link sign-in flows for the + * email specified using the action code settings provided. + * Returns a promise that resolves with the generated link. + * + * @param {string} email The email of the user signing in. + * @param {ActionCodeSettings} actionCodeSettings The required action code setings which defines whether + * the link is to be handled by a mobile app and the additional state information to be passed in the + * deep link, etc. + * @return {Promise} A promise that resolves with the email sign-in link. + */ + public generateSignInWithEmailLink(email: string, actionCodeSettings: ActionCodeSettings): Promise { + return this.authRequestHandler.getEmailActionLink('EMAIL_SIGNIN', email, actionCodeSettings); + } + + /** + * Returns the list of existing provider configuation matching the filter provided. + * At most, 100 provider configs are allowed to be imported at a time. + * + * @param {AuthProviderConfigFilter} options The provider config filter to apply. + * @return {Promise} A promise that resolves with the list of provider configs + * meeting the filter requirements. + */ + public listProviderConfigs(options: AuthProviderConfigFilter): Promise { + const processResponse = (response: any, providerConfigs: AuthProviderConfig[]): ListProviderConfigResults => { + // Return list of provider configuration and the next page token if available. + const result: ListProviderConfigResults = { + providerConfigs, + }; + // Delete result.pageToken if undefined. + if (Object.prototype.hasOwnProperty.call(response, 'nextPageToken')) { + result.pageToken = response.nextPageToken; + } + return result; + }; + if (options && options.type === 'oidc') { + return this.authRequestHandler.listOAuthIdpConfigs(options.maxResults, options.pageToken) + .then((response: any) => { + // List of provider configurations to return. + const providerConfigs: OIDCConfig[] = []; + // Convert each provider config response to a OIDCConfig. + response.oauthIdpConfigs.forEach((configResponse: any) => { + providerConfigs.push(new OIDCConfig(configResponse)); + }); + // Return list of provider configuration and the next page token if available. + return processResponse(response, providerConfigs); + }); + } else if (options && options.type === 'saml') { + return this.authRequestHandler.listInboundSamlConfigs(options.maxResults, options.pageToken) + .then((response: any) => { + // List of provider configurations to return. + const providerConfigs: SAMLConfig[] = []; + // Convert each provider config response to a SAMLConfig. + response.inboundSamlConfigs.forEach((configResponse: any) => { + providerConfigs.push(new SAMLConfig(configResponse)); + }); + // Return list of provider configuration and the next page token if available. + return processResponse(response, providerConfigs); + }); + } + return Promise.reject( + new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"AuthProviderConfigFilter.type" must be either "saml" or "oidc"')); + } + + /** + * Looks up an Auth provider configuration by ID. + * Returns a promise that resolves with the provider configuration corresponding to the provider ID specified. + * + * @param {string} providerId The provider ID corresponding to the provider config to return. + * @return {Promise} + */ + public getProviderConfig(providerId: string): Promise { + if (OIDCConfig.isProviderId(providerId)) { + return this.authRequestHandler.getOAuthIdpConfig(providerId) + .then((response: OIDCConfigServerResponse) => { + return new OIDCConfig(response); + }); + } else if (SAMLConfig.isProviderId(providerId)) { + return this.authRequestHandler.getInboundSamlConfig(providerId) + .then((response: SAMLConfigServerResponse) => { + return new SAMLConfig(response); + }); + } + return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + } + + /** + * Deletes the provider configuration corresponding to the provider ID passed. + * + * @param {string} providerId The provider ID corresponding to the provider config to delete. + * @return {Promise} A promise that resolves on completion. + */ + public deleteProviderConfig(providerId: string): Promise { + if (OIDCConfig.isProviderId(providerId)) { + return this.authRequestHandler.deleteOAuthIdpConfig(providerId); + } else if (SAMLConfig.isProviderId(providerId)) { + return this.authRequestHandler.deleteInboundSamlConfig(providerId); + } + return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + } + + /** + * Returns a promise that resolves with the updated AuthProviderConfig when the provider configuration corresponding + * to the provider ID specified is updated with the specified configuration. + * + * @param {string} providerId The provider ID corresponding to the provider config to update. + * @param {UpdateAuthProviderRequest} updatedConfig The updated configuration. + * @return {Promise} A promise that resolves with the updated provider configuration. + */ + public updateProviderConfig( + providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise { + if (!validator.isNonNullObject(updatedConfig)) { + return Promise.reject(new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + 'Request is missing "UpdateAuthProviderRequest" configuration.', + )); + } + if (OIDCConfig.isProviderId(providerId)) { + return this.authRequestHandler.updateOAuthIdpConfig(providerId, updatedConfig) + .then((response) => { + return new OIDCConfig(response); + }); + } else if (SAMLConfig.isProviderId(providerId)) { + return this.authRequestHandler.updateInboundSamlConfig(providerId, updatedConfig) + .then((response) => { + return new SAMLConfig(response); + }); + } + return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + } + + /** + * Returns a promise that resolves with the newly created AuthProviderConfig when the new provider configuration is + * created. + * @param {AuthProviderConfig} config The provider configuration to create. + * @return {Promise} A promise that resolves with the created provider configuration. + */ + public createProviderConfig(config: AuthProviderConfig): Promise { + if (!validator.isNonNullObject(config)) { + return Promise.reject(new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + 'Request is missing "AuthProviderConfig" configuration.', + )); + } + if (OIDCConfig.isProviderId(config.providerId)) { + return this.authRequestHandler.createOAuthIdpConfig(config as OIDCAuthProviderConfig) + .then((response) => { + return new OIDCConfig(response); + }); + } else if (SAMLConfig.isProviderId(config.providerId)) { + return this.authRequestHandler.createInboundSamlConfig(config as SAMLAuthProviderConfig) + .then((response) => { + return new SAMLConfig(response); + }); + } + return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + } + + /** + * Verifies the decoded Firebase issued JWT is not revoked. Returns a promise that resolves + * with the decoded claims on success. Rejects the promise with revocation error if revoked. + * + * @param {DecodedIdToken} decodedIdToken The JWT's decoded claims. + * @param {ErrorInfo} revocationErrorInfo The revocation error info to throw on revocation + * detection. + * @return {Promise} A Promise that will be fulfilled after a successful + * verification. + */ + private verifyDecodedJWTNotRevoked( + decodedIdToken: DecodedIdToken, revocationErrorInfo: ErrorInfo): Promise { + // Get tokens valid after time for the corresponding user. + return this.getUser(decodedIdToken.sub) + .then((user: UserRecord) => { + // If no tokens valid after time available, token is not revoked. + if (user.tokensValidAfterTime) { + // Get the ID token authentication time and convert to milliseconds UTC. + const authTimeUtc = decodedIdToken.auth_time * 1000; + // Get user tokens valid after time in milliseconds UTC. + const validSinceUtc = new Date(user.tokensValidAfterTime).getTime(); + // Check if authentication time is older than valid since time. + if (authTimeUtc < validSinceUtc) { + throw new FirebaseAuthError(revocationErrorInfo); + } + } + // All checks above passed. Return the decoded token. + return decodedIdToken; + }); + } + + /** + * Enable or disable ID token verification. This is used to safely short-circuit token verification with the + * Auth emulator. When disabled ONLY unsigned tokens will pass verification, production tokens will not pass. + * + * WARNING: This is a dangerous method that will compromise your app's security and break your app in + * production. Developers should never call this method, it is for internal testing use only. + * + * @internal + */ + // @ts-expect-error: this method appears unused but is used privately. + private setJwtVerificationEnabled(enabled: boolean): void { + if (!enabled && !useEmulator()) { + // We only allow verification to be disabled in conjunction with + // the emulator environment variable. + throw new Error('This method is only available when connected to the Authentication emulator.'); + } + + const algorithm = enabled ? ALGORITHM_RS256 : 'none'; + this.idTokenVerifier.setAlgorithm(algorithm); + this.sessionCookieVerifier.setAlgorithm(algorithm); + } +} \ No newline at end of file diff --git a/src/auth/identifier.ts b/src/auth/identifier.ts index b9e93b1fc0..709ff81a06 100644 --- a/src/auth/identifier.ts +++ b/src/auth/identifier.ts @@ -14,13 +14,48 @@ * limitations under the License. */ -import { auth } from './index'; +/** + * Used for looking up an account by uid. + * + * See auth.getUsers() + */ +export interface UidIdentifier { + uid: string; +} + +/** + * Used for looking up an account by email. + * + * See auth.getUsers() + */ +export interface EmailIdentifier { + email: string; +} + +/** + * Used for looking up an account by phone number. + * + * See auth.getUsers() + */ +export interface PhoneIdentifier { + phoneNumber: string; +} + +/** + * Used for looking up an account by federated provider. + * + * See auth.getUsers() + */ +export interface ProviderIdentifier { + providerId: string; + providerUid: string; +} -import UserIdentifier = auth.UserIdentifier; -import UidIdentifier = auth.UidIdentifier; -import EmailIdentifier = auth.EmailIdentifier; -import PhoneIdentifier = auth.PhoneIdentifier; -import ProviderIdentifier = auth.ProviderIdentifier; +/** + * Identifies a user to be looked up. + */ +export type UserIdentifier = + UidIdentifier | EmailIdentifier | PhoneIdentifier | ProviderIdentifier; /* * User defined type guards. See diff --git a/src/auth/index.ts b/src/auth/index.ts index 1ba9b56af5..91609e66f7 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -14,2050 +14,81 @@ * limitations under the License. */ -import { app, FirebaseArrayIndexError } from '../firebase-namespace-api'; - -/** - * Gets the {@link auth.Auth `Auth`} service for the default app or a - * given app. - * - * `admin.auth()` can be called with no arguments to access the default app's - * {@link auth.Auth `Auth`} service or as `admin.auth(app)` to access the - * {@link auth.Auth `Auth`} service associated with a specific app. - * - * @example - * ```javascript - * // Get the Auth service for the default app - * var defaultAuth = admin.auth(); - * ``` - * - * @example - * ```javascript - * // Get the Auth service for a given app - * var otherAuth = admin.auth(otherApp); - * ``` - * - */ -export declare function auth(app?: app.App): auth.Auth; - -/* eslint-disable @typescript-eslint/no-namespace */ -export namespace auth { - /** - * Interface representing a user's metadata. - */ - export interface UserMetadata { - - /** - * The date the user last signed in, formatted as a UTC string. - */ - lastSignInTime: string; - - /** - * The date the user was created, formatted as a UTC string. - */ - creationTime: string; - - /** - * The time at which the user was last active (ID token refreshed), - * formatted as a UTC Date string (eg 'Sat, 03 Feb 2001 04:05:06 GMT'). - * Returns null if the user was never active. - */ - lastRefreshTime?: string | null; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): object; - } - - /** - * Interface representing a user's info from a third-party identity provider - * such as Google or Facebook. - */ - export interface UserInfo { - - /** - * The user identifier for the linked provider. - */ - uid: string; - - /** - * The display name for the linked provider. - */ - displayName: string; - - /** - * The email for the linked provider. - */ - email: string; - - /** - * The phone number for the linked provider. - */ - phoneNumber: string; - - /** - * The photo URL for the linked provider. - */ - photoURL: string; - - /** - * The linked provider ID (for example, "google.com" for the Google provider). - */ - providerId: string; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): object; - } - - /** - * Interface representing the common properties of a user enrolled second factor. - */ - export interface MultiFactorInfo { - - /** - * The ID of the enrolled second factor. This ID is unique to the user. - */ - uid: string; - - /** - * The optional display name of the enrolled second factor. - */ - displayName?: string; - - /** - * The optional date the second factor was enrolled, formatted as a UTC string. - */ - enrollmentTime?: string; - - /** - * The type identifier of the second factor. For SMS second factors, this is `phone`. - */ - factorId: string; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): object; - } - - /** - * Interface representing a phone specific user enrolled second factor. - */ - export interface PhoneMultiFactorInfo extends MultiFactorInfo { - - /** - * The phone number associated with a phone second factor. - */ - phoneNumber: string; - } - - /** - * Interface representing a user. - */ - export interface UserRecord { - - /** - * The user's `uid`. - */ - uid: string; - - /** - * The user's primary email, if set. - */ - email?: string; - - /** - * Whether or not the user's primary email is verified. - */ - emailVerified: boolean; - - /** - * The user's display name. - */ - displayName?: string; - - /** - * The user's primary phone number, if set. - */ - phoneNumber?: string; - - /** - * The user's photo URL. - */ - photoURL?: string; - - /** - * Whether or not the user is disabled: `true` for disabled; `false` for - * enabled. - */ - disabled: boolean; - - /** - * Additional metadata about the user. - */ - metadata: UserMetadata; - - /** - * An array of providers (for example, Google, Facebook) linked to the user. - */ - providerData: UserInfo[]; - - /** - * The user's hashed password (base64-encoded), only if Firebase Auth hashing - * algorithm (SCRYPT) is used. If a different hashing algorithm had been used - * when uploading this user, as is typical when migrating from another Auth - * system, this will be an empty string. If no password is set, this is - * null. This is only available when the user is obtained from - * {@link auth.Auth.listUsers `listUsers()`}. - * - */ - passwordHash?: string; - - /** - * The user's password salt (base64-encoded), only if Firebase Auth hashing - * algorithm (SCRYPT) is used. If a different hashing algorithm had been used to - * upload this user, typical when migrating from another Auth system, this will - * be an empty string. If no password is set, this is null. This is only - * available when the user is obtained from - * {@link auth.Auth.listUsers `listUsers()`}. - * - */ - passwordSalt?: string; - - /** - * The user's custom claims object if available, typically used to define - * user roles and propagated to an authenticated user's ID token. - * This is set via - * {@link auth.Auth.setCustomUserClaims `setCustomUserClaims()`} - */ - customClaims?: { [key: string]: any }; - - /** - * The date the user's tokens are valid after, formatted as a UTC string. - * This is updated every time the user's refresh token are revoked either - * from the {@link auth.Auth.revokeRefreshTokens `revokeRefreshTokens()`} - * API or from the Firebase Auth backend on big account changes (password - * resets, password or email updates, etc). - */ - tokensValidAfterTime?: string; - - /** - * The ID of the tenant the user belongs to, if available. - */ - tenantId?: string | null; - - /** - * The multi-factor related properties for the current user, if available. - */ - multiFactor?: MultiFactorSettings; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): object; - } - - /** - * The multi-factor related user settings. - */ - export interface MultiFactorSettings { - /** - * List of second factors enrolled with the current user. - * Currently only phone second factors are supported. - */ - enrolledFactors: MultiFactorInfo[]; - - /** - * @return A JSON-serializable representation of this multi-factor object. - */ - toJSON(): object; - } - - /** - * The multi-factor related user settings for create operations. - */ - export interface MultiFactorCreateSettings { - - /** - * The created user's list of enrolled second factors. - */ - enrolledFactors: CreateMultiFactorInfoRequest[]; - } - - /** - * The multi-factor related user settings for update operations. - */ - export interface MultiFactorUpdateSettings { - - /** - * The updated list of enrolled second factors. The provided list overwrites the user's - * existing list of second factors. - * When null is passed, all of the user's existing second factors are removed. - */ - enrolledFactors: UpdateMultiFactorInfoRequest[] | null; - } - - /** - * Interface representing common properties of a user enrolled second factor - * for an `UpdateRequest`. - */ - export interface UpdateMultiFactorInfoRequest { - - /** - * The ID of the enrolled second factor. This ID is unique to the user. When not provided, - * a new one is provisioned by the Auth server. - */ - uid?: string; - - /** - * The optional display name for an enrolled second factor. - */ - displayName?: string; - - /** - * The optional date the second factor was enrolled, formatted as a UTC string. - */ - enrollmentTime?: string; - - /** - * The type identifier of the second factor. For SMS second factors, this is `phone`. - */ - factorId: string; - } - - /** - * Interface representing a phone specific user enrolled second factor - * for an `UpdateRequest`. - */ - export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { - - /** - * The phone number associated with a phone second factor. - */ - phoneNumber: string; - } - - /** - * Interface representing the properties to update on the provided user. - */ - export interface UpdateRequest { - - /** - * Whether or not the user is disabled: `true` for disabled; - * `false` for enabled. - */ - disabled?: boolean; - - /** - * The user's display name. - */ - displayName?: string | null; - - /** - * The user's primary email. - */ - email?: string; - - /** - * Whether or not the user's primary email is verified. - */ - emailVerified?: boolean; - - /** - * The user's unhashed password. - */ - password?: string; - - /** - * The user's primary phone number. - */ - phoneNumber?: string | null; - - /** - * The user's photo URL. - */ - photoURL?: string | null; - - /** - * The user's updated multi-factor related properties. - */ - multiFactor?: MultiFactorUpdateSettings; - } - - /** - * Interface representing base properties of a user enrolled second factor for a - * `CreateRequest`. - */ - export interface CreateMultiFactorInfoRequest { - - /** - * The optional display name for an enrolled second factor. - */ - displayName?: string; - - /** - * The type identifier of the second factor. For SMS second factors, this is `phone`. - */ - factorId: string; - } - - /** - * Interface representing a phone specific user enrolled second factor for a - * `CreateRequest`. - */ - export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { - - /** - * The phone number associated with a phone second factor. - */ - phoneNumber: string; - } - - /** - * Interface representing the properties to set on a new user record to be - * created. - */ - export interface CreateRequest extends UpdateRequest { - - /** - * The user's `uid`. - */ - uid?: string; - - /** - * The user's multi-factor related properties. - */ - multiFactor?: MultiFactorCreateSettings; - } - - /** - * Interface representing a decoded Firebase ID token, returned from the - * {@link auth.Auth.verifyIdToken `verifyIdToken()`} method. - * - * Firebase ID tokens are OpenID Connect spec-compliant JSON Web Tokens (JWTs). - * See the - * [ID Token section of the OpenID Connect spec](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) - * for more information about the specific properties below. - */ - export interface DecodedIdToken { - - /** - * The audience for which this token is intended. - * - * This value is a string equal to your Firebase project ID, the unique - * identifier for your Firebase project, which can be found in [your project's - * settings](https://console.firebase.google.com/project/_/settings/general/android:com.random.android). - */ - aud: string; - - /** - * Time, in seconds since the Unix epoch, when the end-user authentication - * occurred. - * - * This value is not set when this particular ID token was created, but when the - * user initially logged in to this session. In a single session, the Firebase - * SDKs will refresh a user's ID tokens every hour. Each ID token will have a - * different [`iat`](#iat) value, but the same `auth_time` value. - */ - auth_time: number; - - /** - * The email of the user to whom the ID token belongs, if available. - */ - email?: string; - - /** - * Whether or not the email of the user to whom the ID token belongs is - * verified, provided the user has an email. - */ - email_verified?: boolean; - - /** - * The ID token's expiration time, in seconds since the Unix epoch. That is, the - * time at which this ID token expires and should no longer be considered valid. - * - * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new - * ID token with up to a one hour expiration. - */ - exp: number; - - /** - * Information about the sign in event, including which sign in provider was - * used and provider-specific identity details. - * - * This data is provided by the Firebase Authentication service and is a - * reserved claim in the ID token. - */ - firebase: { - - /** - * Provider-specific identity details corresponding - * to the provider used to sign in the user. - */ - identities: { - [key: string]: any; - }; - - /** - * The ID of the provider used to sign in the user. - * One of `"anonymous"`, `"password"`, `"facebook.com"`, `"github.com"`, - * `"google.com"`, `"twitter.com"`, `"apple.com"`, `"microsoft.com"`, - * "yahoo.com"`, `"phone"`, `"playgames.google.com"`, `"gc.apple.com"`, - * or `"custom"`. - * - * Additional Identity Platform provider IDs include `"linkedin.com"`, - * OIDC and SAML identity providers prefixed with `"saml."` and `"oidc."` - * respectively. - */ - sign_in_provider: string; - - /** - * The type identifier or `factorId` of the second factor, provided the - * ID token was obtained from a multi-factor authenticated user. - * For phone, this is `"phone"`. - */ - sign_in_second_factor?: string; - - /** - * The `uid` of the second factor used to sign in, provided the - * ID token was obtained from a multi-factor authenticated user. - */ - second_factor_identifier?: string; - - /** - * The ID of the tenant the user belongs to, if available. - */ - tenant?: string; - [key: string]: any; - }; - - /** - * The ID token's issued-at time, in seconds since the Unix epoch. That is, the - * time at which this ID token was issued and should start to be considered - * valid. - * - * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new - * ID token with a new issued-at time. If you want to get the time at which the - * user session corresponding to the ID token initially occurred, see the - * [`auth_time`](#auth_time) property. - */ - iat: number; - - /** - * The issuer identifier for the issuer of the response. - * - * This value is a URL with the format - * `https://securetoken.google.com/`, where `` is the - * same project ID specified in the [`aud`](#aud) property. - */ - iss: string; - - /** - * The phone number of the user to whom the ID token belongs, if available. - */ - phone_number?: string; - - /** - * The photo URL for the user to whom the ID token belongs, if available. - */ - picture?: string; - - /** - * The `uid` corresponding to the user who the ID token belonged to. - * - * As a convenience, this value is copied over to the [`uid`](#uid) property. - */ - sub: string; - - /** - * The `uid` corresponding to the user who the ID token belonged to. - * - * This value is not actually in the JWT token claims itself. It is added as a - * convenience, and is set as the value of the [`sub`](#sub) property. - */ - uid: string; - [key: string]: any; - } - - /** Represents the result of the {@link auth.Auth.getUsers} API. */ - export interface GetUsersResult { - /** - * Set of user records, corresponding to the set of users that were - * requested. Only users that were found are listed here. The result set is - * unordered. - */ - users: UserRecord[]; - - /** Set of identifiers that were requested, but not found. */ - notFound: UserIdentifier[]; - } - - /** - * Interface representing the object returned from a - * {@link auth.Auth.listUsers `listUsers()`} operation. Contains the list - * of users for the current batch and the next page token if available. - */ - export interface ListUsersResult { - - /** - * The list of {@link auth.UserRecord `UserRecord`} objects for the - * current downloaded batch. - */ - users: UserRecord[]; - - /** - * The next page token if available. This is needed for the next batch download. - */ - pageToken?: string; - } - - export type HashAlgorithmType = 'SCRYPT' | 'STANDARD_SCRYPT' | 'HMAC_SHA512' | - 'HMAC_SHA256' | 'HMAC_SHA1' | 'HMAC_MD5' | 'MD5' | 'PBKDF_SHA1' | 'BCRYPT' | - 'PBKDF2_SHA256' | 'SHA512' | 'SHA256' | 'SHA1'; - - /** - * Interface representing the user import options needed for - * {@link auth.Auth.importUsers `importUsers()`} method. This is used to - * provide the password hashing algorithm information. - */ - export interface UserImportOptions { - - /** - * The password hashing information. - */ - hash: { - - /** - * The password hashing algorithm identifier. The following algorithm - * identifiers are supported: - * `SCRYPT`, `STANDARD_SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, `HMAC_SHA1`, - * `HMAC_MD5`, `MD5`, `PBKDF_SHA1`, `BCRYPT`, `PBKDF2_SHA256`, `SHA512`, - * `SHA256` and `SHA1`. - */ - algorithm: HashAlgorithmType; - - /** - * The signing key used in the hash algorithm in buffer bytes. - * Required by hashing algorithms `SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, - * `HAMC_SHA1` and `HMAC_MD5`. - */ - key?: Buffer; - - /** - * The salt separator in buffer bytes which is appended to salt when - * verifying a password. This is only used by the `SCRYPT` algorithm. - */ - saltSeparator?: Buffer; - - /** - * The number of rounds for hashing calculation. - * Required for `SCRYPT`, `MD5`, `SHA512`, `SHA256`, `SHA1`, `PBKDF_SHA1` and - * `PBKDF2_SHA256`. - */ - rounds?: number; - - /** - * The memory cost required for `SCRYPT` algorithm, or the CPU/memory cost. - * Required for `STANDARD_SCRYPT` algorithm. - */ - memoryCost?: number; - - /** - * The parallelization of the hashing algorithm. Required for the - * `STANDARD_SCRYPT` algorithm. - */ - parallelization?: number; - - /** - * The block size (normally 8) of the hashing algorithm. Required for the - * `STANDARD_SCRYPT` algorithm. - */ - blockSize?: number; - /** - * The derived key length of the hashing algorithm. Required for the - * `STANDARD_SCRYPT` algorithm. - */ - derivedKeyLength?: number; - }; - } - - /** - * Interface representing the response from the - * {@link auth.Auth.importUsers `importUsers()`} method for batch - * importing users to Firebase Auth. - */ - export interface UserImportResult { - - /** - * The number of user records that failed to import to Firebase Auth. - */ - failureCount: number; - - /** - * The number of user records that successfully imported to Firebase Auth. - */ - successCount: number; - - /** - * An array of errors corresponding to the provided users to import. The - * length of this array is equal to [`failureCount`](#failureCount). - */ - errors: FirebaseArrayIndexError[]; - } - - /** - * Represents the result of the - * {@link auth.Auth.deleteUsers `deleteUsers()`} - * API. - */ - export interface DeleteUsersResult { - /** - * The number of user records that failed to be deleted (possibly zero). - */ - failureCount: number; - - /** - * The number of users that were deleted successfully (possibly zero). - * Users that did not exist prior to calling `deleteUsers()` are - * considered to be successfully deleted. - */ - successCount: number; - - /** - * A list of `FirebaseArrayIndexError` instances describing the errors that - * were encountered during the deletion. Length of this list is equal to - * the return value of [`failureCount`](#failureCount). - */ - errors: FirebaseArrayIndexError[]; - } - - /** - * User metadata to include when importing a user. - */ - export interface UserMetadataRequest { - - /** - * The date the user last signed in, formatted as a UTC string. - */ - lastSignInTime?: string; - - /** - * The date the user was created, formatted as a UTC string. - */ - creationTime?: string; - } - - /** - * User provider data to include when importing a user. - */ - export interface UserProviderRequest { - - /** - * The user identifier for the linked provider. - */ - uid: string; - - /** - * The display name for the linked provider. - */ - displayName?: string; - - /** - * The email for the linked provider. - */ - email?: string; - - /** - * The phone number for the linked provider. - */ - phoneNumber?: string; - - /** - * The photo URL for the linked provider. - */ - photoURL?: string; - - /** - * The linked provider ID (for example, "google.com" for the Google provider). - */ - providerId: string; - } - - /** - * Interface representing a user to import to Firebase Auth via the - * {@link auth.Auth.importUsers `importUsers()`} method. - */ - export interface UserImportRecord { - - /** - * The user's `uid`. - */ - uid: string; - - /** - * The user's primary email, if set. - */ - email?: string; - - /** - * Whether or not the user's primary email is verified. - */ - emailVerified?: boolean; - - /** - * The user's display name. - */ - displayName?: string; - - /** - * The user's primary phone number, if set. - */ - phoneNumber?: string; - - /** - * The user's photo URL. - */ - photoURL?: string; - - /** - * Whether or not the user is disabled: `true` for disabled; `false` for - * enabled. - */ - disabled?: boolean; - - /** - * Additional metadata about the user. - */ - metadata?: UserMetadataRequest; - - /** - * An array of providers (for example, Google, Facebook) linked to the user. - */ - providerData?: UserProviderRequest[]; - - /** - * The user's custom claims object if available, typically used to define - * user roles and propagated to an authenticated user's ID token. - */ - customClaims?: { [key: string]: any }; - - /** - * The buffer of bytes representing the user's hashed password. - * When a user is to be imported with a password hash, - * {@link auth.UserImportOptions `UserImportOptions`} are required to be - * specified to identify the hashing algorithm used to generate this hash. - */ - passwordHash?: Buffer; - - /** - * The buffer of bytes representing the user's password salt. - */ - passwordSalt?: Buffer; - - /** - * The identifier of the tenant where user is to be imported to. - * When not provided in an `admin.auth.Auth` context, the user is uploaded to - * the default parent project. - * When not provided in an `admin.auth.TenantAwareAuth` context, the user is uploaded - * to the tenant corresponding to that `TenantAwareAuth` instance's tenant ID. - */ - tenantId?: string; - - /** - * The user's multi-factor related properties. - */ - multiFactor?: MultiFactorUpdateSettings; - } - - /** - * Interface representing the session cookie options needed for the - * {@link auth.Auth.createSessionCookie `createSessionCookie()`} method. - */ - export interface SessionCookieOptions { - - /** - * The session cookie custom expiration in milliseconds. The minimum allowed is - * 5 minutes and the maxium allowed is 2 weeks. - */ - expiresIn: number; - } - - /** - * This is the interface that defines the required continue/state URL with - * optional Android and iOS bundle identifiers. - */ - export interface ActionCodeSettings { - - /** - * Defines the link continue/state URL, which has different meanings in - * different contexts: - *
    - *
  • When the link is handled in the web action widgets, this is the deep - * link in the `continueUrl` query parameter.
  • - *
  • When the link is handled in the app directly, this is the `continueUrl` - * query parameter in the deep link of the Dynamic Link.
  • - *
- */ - url: string; - - /** - * Whether to open the link via a mobile app or a browser. - * The default is false. When set to true, the action code link is sent - * as a Universal Link or Android App Link and is opened by the app if - * installed. In the false case, the code is sent to the web widget first - * and then redirects to the app if installed. - */ - handleCodeInApp?: boolean; - - /** - * Defines the iOS bundle ID. This will try to open the link in an iOS app if it - * is installed. - */ - iOS?: { - - /** - * Defines the required iOS bundle ID of the app where the link should be - * handled if the application is already installed on the device. - */ - bundleId: string; - }; - - /** - * Defines the Android package name. This will try to open the link in an - * android app if it is installed. If `installApp` is passed, it specifies - * whether to install the Android app if the device supports it and the app is - * not already installed. If this field is provided without a `packageName`, an - * error is thrown explaining that the `packageName` must be provided in - * conjunction with this field. If `minimumVersion` is specified, and an older - * version of the app is installed, the user is taken to the Play Store to - * upgrade the app. - */ - android?: { - - /** - * Defines the required Android package name of the app where the link should be - * handled if the Android app is installed. - */ - packageName: string; - - /** - * Whether to install the Android app if the device supports it and the app is - * not already installed. - */ - installApp?: boolean; - - /** - * The Android minimum version if available. If the installed app is an older - * version, the user is taken to the GOogle Play Store to upgrade the app. - */ - minimumVersion?: string; - }; - - /** - * Defines the dynamic link domain to use for the current link if it is to be - * opened using Firebase Dynamic Links, as multiple dynamic link domains can be - * configured per project. This field provides the ability to explicitly choose - * configured per project. This fields provides the ability explicitly choose - * one. If none is provided, the oldest domain is used by default. - */ - dynamicLinkDomain?: string; - } - - /** - * Interface representing a tenant configuration. - * - * Multi-tenancy support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform) - * - * Before multi-tenancy can be used on a Google Cloud Identity Platform project, - * tenants must be allowed on that project via the Cloud Console UI. - * - * A tenant configuration provides information such as the display name, tenant - * identifier and email authentication configuration. - * For OIDC/SAML provider configuration management, `TenantAwareAuth` instances should - * be used instead of a `Tenant` to retrieve the list of configured IdPs on a tenant. - * When configuring these providers, note that tenants will inherit - * whitelisted domains and authenticated redirect URIs of their parent project. - * - * All other settings of a tenant will also be inherited. These will need to be managed - * from the Cloud Console UI. - */ - export interface Tenant { - - /** - * The tenant identifier. - */ - tenantId: string; - - /** - * The tenant display name. - */ - displayName?: string; - - /** - * The email sign in provider configuration. - */ - emailSignInConfig?: { - - /** - * Whether email provider is enabled. - */ - enabled: boolean; - - /** - * Whether password is required for email sign-in. When not required, - * email sign-in can be performed with password or via email link sign-in. - */ - passwordRequired?: boolean; - }; - - /** - * The multi-factor auth configuration on the current tenant. - */ - multiFactorConfig?: MultiFactorConfig; - - /** - * The map containing the test phone number / code pairs for the tenant. - */ - testPhoneNumbers?: { [phoneNumber: string]: string }; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): object; - } - - /** - * Identifies a second factor type. - */ - export type AuthFactorType = 'phone'; - - /** - * Identifies a multi-factor configuration state. - */ - export type MultiFactorConfigState = 'ENABLED' | 'DISABLED'; - - /** - * Interface representing a multi-factor configuration. - * This can be used to define whether multi-factor authentication is enabled - * or disabled and the list of second factor challenges that are supported. - */ - export interface MultiFactorConfig { - /** - * The multi-factor config state. - */ - state: MultiFactorConfigState; - - /** - * The list of identifiers for enabled second factors. - * Currently only ‘phone’ is supported. - */ - factorIds?: AuthFactorType[]; - } - - /** - * The email sign in configuration. - */ - export interface EmailSignInProviderConfig { - /** - * Whether email provider is enabled. - */ - enabled: boolean; - - /** - * Whether password is required for email sign-in. When not required, - * email sign-in can be performed with password or via email link sign-in. - */ - passwordRequired?: boolean; // In the backend API, default is true if not provided - } - - /** - * Interface representing the properties to update on the provided tenant. - */ - export interface UpdateTenantRequest { - - /** - * The tenant display name. - */ - displayName?: string; - - /** - * The email sign in configuration. - */ - emailSignInConfig?: EmailSignInProviderConfig; - - /** - * The multi-factor auth configuration to update on the tenant. - */ - multiFactorConfig?: MultiFactorConfig; - - /** - * The updated map containing the test phone number / code pairs for the tenant. - * Passing null clears the previously save phone number / code pairs. - */ - testPhoneNumbers?: { [phoneNumber: string]: string } | null; - } - - /** - * Interface representing the properties to set on a new tenant. - */ - export type CreateTenantRequest = UpdateTenantRequest; - - /** - * Interface representing the object returned from a - * {@link auth.TenantManager.listTenants `listTenants()`} - * operation. - * Contains the list of tenants for the current batch and the next page token if available. - */ - export interface ListTenantsResult { - - /** - * The list of {@link auth.Tenant `Tenant`} objects for the downloaded batch. - */ - tenants: Tenant[]; - - /** - * The next page token if available. This is needed for the next batch download. - */ - pageToken?: string; - } - - /** - * The filter interface used for listing provider configurations. This is used - * when specifying how to list configured identity providers via - * {@link auth.Auth.listProviderConfigs `listProviderConfigs()`}. - */ - export interface AuthProviderConfigFilter { - - /** - * The Auth provider configuration filter. This can be either `saml` or `oidc`. - * The former is used to look up SAML providers only, while the latter is used - * for OIDC providers. - */ - type: 'saml' | 'oidc'; - - /** - * The maximum number of results to return per page. The default and maximum is - * 100. - */ - maxResults?: number; - - /** - * The next page token. When not specified, the lookup starts from the beginning - * of the list. - */ - pageToken?: string; - } - - /** - * The base Auth provider configuration interface. - */ - export interface AuthProviderConfig { - - /** - * The provider ID defined by the developer. - * For a SAML provider, this is always prefixed by `saml.`. - * For an OIDC provider, this is always prefixed by `oidc.`. - */ - providerId: string; - - /** - * The user-friendly display name to the current configuration. This name is - * also used as the provider label in the Cloud Console. - */ - displayName?: string; - - /** - * Whether the provider configuration is enabled or disabled. A user - * cannot sign in using a disabled provider. - */ - enabled: boolean; - } - - /** - * The - * [SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) - * Auth provider configuration interface. A SAML provider can be created via - * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. - */ - export interface SAMLAuthProviderConfig extends AuthProviderConfig { - - /** - * The SAML IdP entity identifier. - */ - idpEntityId: string; - - /** - * The SAML IdP SSO URL. This must be a valid URL. - */ - ssoURL: string; - - /** - * The list of SAML IdP X.509 certificates issued by CA for this provider. - * Multiple certificates are accepted to prevent outages during - * IdP key rotation (for example ADFS rotates every 10 days). When the Auth - * server receives a SAML response, it will match the SAML response with the - * certificate on record. Otherwise the response is rejected. - * Developers are expected to manage the certificate updates as keys are - * rotated. - */ - x509Certificates: string[]; - - /** - * The SAML relying party (service provider) entity ID. - * This is defined by the developer but needs to be provided to the SAML IdP. - */ - rpEntityId: string; - - /** - * This is fixed and must always be the same as the OAuth redirect URL - * provisioned by Firebase Auth, - * `https://project-id.firebaseapp.com/__/auth/handler` unless a custom - * `authDomain` is used. - * The callback URL should also be provided to the SAML IdP during - * configuration. - */ - callbackURL?: string; - } - - /** - * The [OIDC](https://openid.net/specs/openid-connect-core-1_0-final.html) Auth - * provider configuration interface. An OIDC provider can be created via - * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. - */ - export interface OIDCAuthProviderConfig extends AuthProviderConfig { - - /** - * This is the required client ID used to confirm the audience of an OIDC - * provider's - * [ID token](https://openid.net/specs/openid-connect-core-1_0-final.html#IDToken). - */ - clientId: string; - - /** - * This is the required provider issuer used to match the provider issuer of - * the ID token and to determine the corresponding OIDC discovery document, eg. - * [`/.well-known/openid-configuration`](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig). - * This is needed for the following: - *
    - *
  • To verify the provided issuer.
  • - *
  • Determine the authentication/authorization endpoint during the OAuth - * `id_token` authentication flow.
  • - *
  • To retrieve the public signing keys via `jwks_uri` to verify the OIDC - * provider's ID token's signature.
  • - *
  • To determine the claims_supported to construct the user attributes to be - * returned in the additional user info response.
  • - *
- * ID token validation will be performed as defined in the - * [spec](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). - */ - issuer: string; - } - - /** - * The request interface for updating a SAML Auth provider. This is used - * when updating a SAML provider's configuration via - * {@link auth.Auth.updateProviderConfig `updateProviderConfig()`}. - */ - export interface SAMLUpdateAuthProviderRequest { - - /** - * The SAML provider's updated display name. If not provided, the existing - * configuration's value is not modified. - */ - displayName?: string; - - /** - * Whether the SAML provider is enabled or not. If not provided, the existing - * configuration's setting is not modified. - */ - enabled?: boolean; - - /** - * The SAML provider's updated IdP entity ID. If not provided, the existing - * configuration's value is not modified. - */ - idpEntityId?: string; - - /** - * The SAML provider's updated SSO URL. If not provided, the existing - * configuration's value is not modified. - */ - ssoURL?: string; - - /** - * The SAML provider's updated list of X.509 certificated. If not provided, the - * existing configuration list is not modified. - */ - x509Certificates?: string[]; - - /** - * The SAML provider's updated RP entity ID. If not provided, the existing - * configuration's value is not modified. - */ - rpEntityId?: string; - - /** - * The SAML provider's callback URL. If not provided, the existing - * configuration's value is not modified. - */ - callbackURL?: string; - } - - /** - * The request interface for updating an OIDC Auth provider. This is used - * when updating an OIDC provider's configuration via - * {@link auth.Auth.updateProviderConfig `updateProviderConfig()`}. - */ - export interface OIDCUpdateAuthProviderRequest { - - /** - * The OIDC provider's updated display name. If not provided, the existing - * configuration's value is not modified. - */ - displayName?: string; - - /** - * Whether the OIDC provider is enabled or not. If not provided, the existing - * configuration's setting is not modified. - */ - enabled?: boolean; - - /** - * The OIDC provider's updated client ID. If not provided, the existing - * configuration's value is not modified. - */ - clientId?: string; - - /** - * The OIDC provider's updated issuer. If not provided, the existing - * configuration's value is not modified. - */ - issuer?: string; - } - - /** - * The response interface for listing provider configs. This is only available - * when listing all identity providers' configurations via - * {@link auth.Auth.listProviderConfigs `listProviderConfigs()`}. - */ - export interface ListProviderConfigResults { - - /** - * The list of providers for the specified type in the current page. - */ - providerConfigs: AuthProviderConfig[]; - - /** - * The next page token, if available. - */ - pageToken?: string; - } - - export type UpdateAuthProviderRequest = - SAMLUpdateAuthProviderRequest | OIDCUpdateAuthProviderRequest; - - /** - * Used for looking up an account by uid. - * - * See auth.getUsers() - */ - export interface UidIdentifier { - uid: string; - } - - /** - * Used for looking up an account by email. - * - * See auth.getUsers() - */ - export interface EmailIdentifier { - email: string; - } - - /** - * Used for looking up an account by phone number. - * - * See auth.getUsers() - */ - export interface PhoneIdentifier { - phoneNumber: string; - } - - /** - * Used for looking up an account by federated provider. - * - * See auth.getUsers() - */ - export interface ProviderIdentifier { - providerId: string; - providerUid: string; - } - - /** - * Identifies a user to be looked up. - */ - export type UserIdentifier = - UidIdentifier | EmailIdentifier | PhoneIdentifier | ProviderIdentifier; - - export interface BaseAuth { - - /** - * Creates a new Firebase custom token (JWT) that can be sent back to a client - * device to use to sign in with the client SDKs' `signInWithCustomToken()` - * methods. (Tenant-aware instances will also embed the tenant ID in the - * token.) - * - * See [Create Custom Tokens](/docs/auth/admin/create-custom-tokens) for code - * samples and detailed documentation. - * - * @param uid The `uid` to use as the custom token's subject. - * @param developerClaims Optional additional claims to include - * in the custom token's payload. - * - * @return A promise fulfilled with a custom token for the - * provided `uid` and payload. - */ - createCustomToken(uid: string, developerClaims?: object): Promise; - - /** - * Creates a new user. - * - * See [Create a user](/docs/auth/admin/manage-users#create_a_user) for code - * samples and detailed documentation. - * - * @param properties The properties to set on the - * new user record to be created. - * - * @return A promise fulfilled with the user - * data corresponding to the newly created user. - */ - createUser(properties: CreateRequest): Promise; - - /** - * Deletes an existing user. - * - * See [Delete a user](/docs/auth/admin/manage-users#delete_a_user) for code - * samples and detailed documentation. - * - * @param uid The `uid` corresponding to the user to delete. - * - * @return An empty promise fulfilled once the user has been - * deleted. - */ - deleteUser(uid: string): Promise; - - /** - * Deletes the users specified by the given uids. - * - * Deleting a non-existing user won't generate an error (i.e. this method - * is idempotent.) Non-existing users are considered to be successfully - * deleted, and are therefore counted in the - * `DeleteUsersResult.successCount` value. - * - * Only a maximum of 1000 identifiers may be supplied. If more than 1000 - * identifiers are supplied, this method throws a FirebaseAuthError. - * - * This API is currently rate limited at the server to 1 QPS. If you exceed - * this, you may get a quota exceeded error. Therefore, if you want to - * delete more than 1000 users, you may need to add a delay to ensure you - * don't go over this limit. - * - * @param uids The `uids` corresponding to the users to delete. - * - * @return A Promise that resolves to the total number of successful/failed - * deletions, as well as the array of errors that corresponds to the - * failed deletions. - */ - deleteUsers(uids: string[]): Promise; - - /** - * Gets the user data for the user corresponding to a given `uid`. - * - * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) - * for code samples and detailed documentation. - * - * @param uid The `uid` corresponding to the user whose data to fetch. - * - * @return A promise fulfilled with the user - * data corresponding to the provided `uid`. - */ - getUser(uid: string): Promise; - - /** - * Gets the user data for the user corresponding to a given email. - * - * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) - * for code samples and detailed documentation. - * - * @param email The email corresponding to the user whose data to - * fetch. - * - * @return A promise fulfilled with the user - * data corresponding to the provided email. - */ - getUserByEmail(email: string): Promise; - - /** - * Gets the user data for the user corresponding to a given phone number. The - * phone number has to conform to the E.164 specification. - * - * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) - * for code samples and detailed documentation. - * - * @param phoneNumber The phone number corresponding to the user whose - * data to fetch. - * - * @return A promise fulfilled with the user - * data corresponding to the provided phone number. - */ - getUserByPhoneNumber(phoneNumber: string): Promise; - - /** - * Gets the user data corresponding to the specified identifiers. - * - * There are no ordering guarantees; in particular, the nth entry in the result list is not - * guaranteed to correspond to the nth entry in the input parameters list. - * - * Only a maximum of 100 identifiers may be supplied. If more than 100 identifiers are supplied, - * this method throws a FirebaseAuthError. - * - * @param identifiers The identifiers used to indicate which user records should be returned. - * Must have <= 100 entries. - * @return {Promise} A promise that resolves to the corresponding user records. - * @throws FirebaseAuthError If any of the identifiers are invalid or if more than 100 - * identifiers are specified. - */ - getUsers(identifiers: UserIdentifier[]): Promise; - - /** - * Retrieves a list of users (single batch only) with a size of `maxResults` - * starting from the offset as specified by `pageToken`. This is used to - * retrieve all the users of a specified project in batches. - * - * See [List all users](/docs/auth/admin/manage-users#list_all_users) - * for code samples and detailed documentation. - * - * @param maxResults The page size, 1000 if undefined. This is also - * the maximum allowed limit. - * @param pageToken The next page token. If not specified, returns - * users starting without any offset. - * @return A promise that resolves with - * the current batch of downloaded users and the next page token. - */ - listUsers(maxResults?: number, pageToken?: string): Promise; - - /** - * Updates an existing user. - * - * See [Update a user](/docs/auth/admin/manage-users#update_a_user) for code - * samples and detailed documentation. - * - * @param uid The `uid` corresponding to the user to update. - * @param properties The properties to update on - * the provided user. - * - * @return A promise fulfilled with the - * updated user data. - */ - updateUser(uid: string, properties: UpdateRequest): Promise; - - /** - * Verifies a Firebase ID token (JWT). If the token is valid, the promise is - * fulfilled with the token's decoded claims; otherwise, the promise is - * rejected. - * An optional flag can be passed to additionally check whether the ID token - * was revoked. - * - * See [Verify ID Tokens](/docs/auth/admin/verify-id-tokens) for code samples - * and detailed documentation. - * - * @param idToken The ID token to verify. - * @param checkRevoked Whether to check if the ID token was revoked. - * This requires an extra request to the Firebase Auth backend to check - * the `tokensValidAfterTime` time for the corresponding user. - * When not specified, this additional check is not applied. - * - * @return A promise fulfilled with the - * token's decoded claims if the ID token is valid; otherwise, a rejected - * promise. - */ - verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; - - /** - * Sets additional developer claims on an existing user identified by the - * provided `uid`, typically used to define user roles and levels of - * access. These claims should propagate to all devices where the user is - * already signed in (after token expiration or when token refresh is forced) - * and the next time the user signs in. If a reserved OIDC claim name - * is used (sub, iat, iss, etc), an error is thrown. They are set on the - * authenticated user's ID token JWT. - * - * See - * [Defining user roles and access levels](/docs/auth/admin/custom-claims) - * for code samples and detailed documentation. - * - * @param uid The `uid` of the user to edit. - * @param customUserClaims The developer claims to set. If null is - * passed, existing custom claims are deleted. Passing a custom claims payload - * larger than 1000 bytes will throw an error. Custom claims are added to the - * user's ID token which is transmitted on every authenticated request. - * For profile non-access related user attributes, use database or other - * separate storage systems. - * @return A promise that resolves when the operation completes - * successfully. - */ - setCustomUserClaims(uid: string, customUserClaims: object | null): Promise; - - /** - * Revokes all refresh tokens for an existing user. - * - * This API will update the user's - * {@link auth.UserRecord.tokensValidAfterTime `tokensValidAfterTime`} to - * the current UTC. It is important that the server on which this is called has - * its clock set correctly and synchronized. - * - * While this will revoke all sessions for a specified user and disable any - * new ID tokens for existing sessions from getting minted, existing ID tokens - * may remain active until their natural expiration (one hour). To verify that - * ID tokens are revoked, use - * {@link auth.Auth.verifyIdToken `verifyIdToken(idToken, true)`} - * where `checkRevoked` is set to true. - * - * @param uid The `uid` corresponding to the user whose refresh tokens - * are to be revoked. - * - * @return An empty promise fulfilled once the user's refresh - * tokens have been revoked. - */ - revokeRefreshTokens(uid: string): Promise; - - /** - * Imports the provided list of users into Firebase Auth. - * A maximum of 1000 users are allowed to be imported one at a time. - * When importing users with passwords, - * {@link auth.UserImportOptions `UserImportOptions`} are required to be - * specified. - * This operation is optimized for bulk imports and will ignore checks on `uid`, - * `email` and other identifier uniqueness which could result in duplications. - * - * @param users The list of user records to import to Firebase Auth. - * @param options The user import options, required when the users provided include - * password credentials. - * @return A promise that resolves when - * the operation completes with the result of the import. This includes the - * number of successful imports, the number of failed imports and their - * corresponding errors. - */ - importUsers( - users: UserImportRecord[], - options?: UserImportOptions, - ): Promise; - - /** - * Creates a new Firebase session cookie with the specified options. The created - * JWT string can be set as a server-side session cookie with a custom cookie - * policy, and be used for session management. The session cookie JWT will have - * the same payload claims as the provided ID token. - * - * See [Manage Session Cookies](/docs/auth/admin/manage-cookies) for code - * samples and detailed documentation. - * - * @param idToken The Firebase ID token to exchange for a session - * cookie. - * @param sessionCookieOptions The session - * cookie options which includes custom session duration. - * - * @return A promise that resolves on success with the - * created session cookie. - */ - createSessionCookie( - idToken: string, - sessionCookieOptions: SessionCookieOptions, - ): Promise; - - /** - * Verifies a Firebase session cookie. Returns a Promise with the cookie claims. - * Rejects the promise if the cookie 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 revoked, an - * `auth/session-cookie-revoked` error is thrown. If not specified the check is - * not performed. - * - * See [Verify Session Cookies](/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions) - * for code samples and detailed documentation - * - * @param sessionCookie The session cookie to verify. - * @param checkForRevocation Whether to check if the session cookie was - * revoked. This requires an extra request to the Firebase Auth backend to - * check the `tokensValidAfterTime` time for the corresponding user. - * When not specified, this additional check is not performed. - * - * @return A promise fulfilled with the - * session cookie's decoded claims if the session cookie is valid; otherwise, - * a rejected promise. - */ - verifySessionCookie( - sessionCookie: string, - checkForRevocation?: boolean, - ): Promise; - - /** - * Generates the out of band email action link to reset a user's password. - * The link is generated for the user with the specified email address. The - * optional {@link auth.ActionCodeSettings `ActionCodeSettings`} object - * defines whether the link is to be handled by a mobile app or browser and the - * additional state information to be passed in the deep link, etc. - * - * @example - * ```javascript - * var actionCodeSettings = { - * url: 'https://www.example.com/?email=user@example.com', - * iOS: { - * bundleId: 'com.example.ios' - * }, - * android: { - * packageName: 'com.example.android', - * installApp: true, - * minimumVersion: '12' - * }, - * handleCodeInApp: true, - * dynamicLinkDomain: 'custom.page.link' - * }; - * admin.auth() - * .generatePasswordResetLink('user@example.com', actionCodeSettings) - * .then(function(link) { - * // The link was successfully generated. - * }) - * .catch(function(error) { - * // Some error occurred, you can inspect the code: error.code - * }); - * ``` - * - * @param email The email address of the user whose password is to be - * reset. - * @param actionCodeSettings The action - * code settings. If specified, the state/continue URL is set as the - * "continueUrl" parameter in the password reset link. The default password - * reset landing page will use this to display a link to go back to the app - * if it is installed. - * If the actionCodeSettings is not specified, no URL is appended to the - * action URL. - * The state URL provided must belong to a domain that is whitelisted by the - * developer in the console. Otherwise an error is thrown. - * Mobile app redirects are only applicable if the developer configures - * and accepts the Firebase Dynamic Links terms of service. - * The Android package name and iOS bundle ID are respected only if they - * are configured in the same Firebase Auth project. - * @return A promise that resolves with the generated link. - */ - generatePasswordResetLink( - email: string, - actionCodeSettings?: ActionCodeSettings, - ): Promise; - - /** - * Generates the out of band email action link to verify the user's ownership - * of the specified email. The - * {@link auth.ActionCodeSettings `ActionCodeSettings`} object provided - * as an argument to this method defines whether the link is to be handled by a - * mobile app or browser along with additional state information to be passed in - * the deep link, etc. - * - * @example - * ```javascript - * var actionCodeSettings = { - * url: 'https://www.example.com/cart?email=user@example.com&cartId=123', - * iOS: { - * bundleId: 'com.example.ios' - * }, - * android: { - * packageName: 'com.example.android', - * installApp: true, - * minimumVersion: '12' - * }, - * handleCodeInApp: true, - * dynamicLinkDomain: 'custom.page.link' - * }; - * admin.auth() - * .generateEmailVerificationLink('user@example.com', actionCodeSettings) - * .then(function(link) { - * // The link was successfully generated. - * }) - * .catch(function(error) { - * // Some error occurred, you can inspect the code: error.code - * }); - * ``` - * - * @param email The email account to verify. - * @param actionCodeSettings The action - * code settings. If specified, the state/continue URL is set as the - * "continueUrl" parameter in the email verification link. The default email - * verification landing page will use this to display a link to go back to - * the app if it is installed. - * If the actionCodeSettings is not specified, no URL is appended to the - * action URL. - * The state URL provided must belong to a domain that is whitelisted by the - * developer in the console. Otherwise an error is thrown. - * Mobile app redirects are only applicable if the developer configures - * and accepts the Firebase Dynamic Links terms of service. - * The Android package name and iOS bundle ID are respected only if they - * are configured in the same Firebase Auth project. - * @return A promise that resolves with the generated link. - */ - generateEmailVerificationLink( - email: string, - actionCodeSettings?: ActionCodeSettings, - ): Promise; - - /** - * Generates the out of band email action link to sign in or sign up the owner - * of the specified email. The - * {@link auth.ActionCodeSettings `ActionCodeSettings`} object provided - * as an argument to this method defines whether the link is to be handled by a - * mobile app or browser along with additional state information to be passed in - * the deep link, etc. - * - * @example - * ```javascript - * var actionCodeSettings = { - * // The URL to redirect to for sign-in completion. This is also the deep - * // link for mobile redirects. The domain (www.example.com) for this URL - * // must be whitelisted in the Firebase Console. - * url: 'https://www.example.com/finishSignUp?cartId=1234', - * iOS: { - * bundleId: 'com.example.ios' - * }, - * android: { - * packageName: 'com.example.android', - * installApp: true, - * minimumVersion: '12' - * }, - * // This must be true. - * handleCodeInApp: true, - * dynamicLinkDomain: 'custom.page.link' - * }; - * admin.auth() - * .generateSignInWithEmailLink('user@example.com', actionCodeSettings) - * .then(function(link) { - * // The link was successfully generated. - * }) - * .catch(function(error) { - * // Some error occurred, you can inspect the code: error.code - * }); - * ``` - * - * @param email The email account to sign in with. - * @param actionCodeSettings The action - * code settings. These settings provide Firebase with instructions on how - * to construct the email link. This includes the sign in completion URL or - * the deep link for redirects and the mobile apps to use when the - * sign-in link is opened on an Android or iOS device. - * Mobile app redirects are only applicable if the developer configures - * and accepts the Firebase Dynamic Links terms of service. - * The Android package name and iOS bundle ID are respected only if they - * are configured in the same Firebase Auth project. - * @return A promise that resolves with the generated link. - */ - generateSignInWithEmailLink( - email: string, - actionCodeSettings: ActionCodeSettings, - ): Promise; - - /** - * Returns the list of existing provider configurations matching the filter - * provided. At most, 100 provider configs can be listed at a time. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param options The provider config filter to apply. - * @return A promise that resolves with the list of provider configs meeting the - * filter requirements. - */ - listProviderConfigs( - options: AuthProviderConfigFilter - ): Promise; - - /** - * Looks up an Auth provider configuration by the provided ID. - * Returns a promise that resolves with the provider configuration - * corresponding to the provider ID specified. If the specified ID does not - * exist, an `auth/configuration-not-found` error is thrown. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param providerId The provider ID corresponding to the provider - * config to return. - * @return A promise that resolves - * with the configuration corresponding to the provided ID. - */ - getProviderConfig(providerId: string): Promise; - - /** - * Deletes the provider configuration corresponding to the provider ID passed. - * If the specified ID does not exist, an `auth/configuration-not-found` error - * is thrown. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param providerId The provider ID corresponding to the provider - * config to delete. - * @return A promise that resolves on completion. - */ - deleteProviderConfig(providerId: string): Promise; - - /** - * Returns a promise that resolves with the updated `AuthProviderConfig` - * corresponding to the provider ID specified. - * If the specified ID does not exist, an `auth/configuration-not-found` error - * is thrown. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param providerId The provider ID corresponding to the provider - * config to update. - * @param updatedConfig The updated configuration. - * @return A promise that resolves with the updated provider configuration. - */ - updateProviderConfig( - providerId: string, updatedConfig: UpdateAuthProviderRequest - ): Promise; - - /** - * Returns a promise that resolves with the newly created `AuthProviderConfig` - * when the new provider configuration is created. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param config The provider configuration to create. - * @return A promise that resolves with the created provider configuration. - */ - createProviderConfig( - config: AuthProviderConfig - ): Promise; - } - - /** - * Tenant-aware `Auth` interface used for managing users, configuring SAML/OIDC providers, - * generating email links for password reset, email verification, etc for specific tenants. - * - * Multi-tenancy support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform) - * - * Each tenant contains its own identity providers, settings and sets of users. - * Using `TenantAwareAuth`, users for a specific tenant and corresponding OIDC/SAML - * configurations can also be managed, ID tokens for users signed in to a specific tenant - * can be verified, and email action links can also be generated for users belonging to the - * tenant. - * - * `TenantAwareAuth` instances for a specific `tenantId` can be instantiated by calling - * `auth.tenantManager().authForTenant(tenantId)`. - */ - export interface TenantAwareAuth extends BaseAuth { - - /** - * The tenant identifier corresponding to this `TenantAwareAuth` instance. - * All calls to the user management APIs, OIDC/SAML provider management APIs, email link - * generation APIs, etc will only be applied within the scope of this tenant. - */ - tenantId: string; - } - - export interface Auth extends BaseAuth { - app: app.App; - - /** - * @return The tenant manager instance associated with the current project. - */ - tenantManager(): TenantManager; - } - - /** - * Defines the tenant manager used to help manage tenant related operations. - * This includes: - *
    - *
  • The ability to create, update, list, get and delete tenants for the underlying - * project.
  • - *
  • Getting a `TenantAwareAuth` instance for running Auth related operations - * (user management, provider configuration management, token verification, - * email link generation, etc) in the context of a specified tenant.
  • - *
- */ - export interface TenantManager { - /** - * @param tenantId The tenant ID whose `TenantAwareAuth` instance is to be returned. - * - * @return The `TenantAwareAuth` instance corresponding to this tenant identifier. - */ - authForTenant(tenantId: string): TenantAwareAuth; - - /** - * Gets the tenant configuration for the tenant corresponding to a given `tenantId`. - * - * @param tenantId The tenant identifier corresponding to the tenant whose data to fetch. - * - * @return A promise fulfilled with the tenant configuration to the provided `tenantId`. - */ - getTenant(tenantId: string): Promise; - - /** - * Retrieves a list of tenants (single batch only) with a size of `maxResults` - * starting from the offset as specified by `pageToken`. This is used to - * retrieve all the tenants of a specified project in batches. - * - * @param maxResults The page size, 1000 if undefined. This is also - * the maximum allowed limit. - * @param pageToken The next page token. If not specified, returns - * tenants starting without any offset. - * - * @return A promise that resolves with - * a batch of downloaded tenants and the next page token. - */ - listTenants(maxResults?: number, pageToken?: string): Promise; - - /** - * Deletes an existing tenant. - * - * @param tenantId The `tenantId` corresponding to the tenant to delete. - * - * @return An empty promise fulfilled once the tenant has been deleted. - */ - deleteTenant(tenantId: string): Promise; - - /** - * Creates a new tenant. - * When creating new tenants, tenants that use separate billing and quota will require their - * own project and must be defined as `full_service`. - * - * @param tenantOptions The properties to set on the new tenant configuration to be created. - * - * @return A promise fulfilled with the tenant configuration corresponding to the newly - * created tenant. - */ - createTenant(tenantOptions: CreateTenantRequest): Promise; - - /** - * Updates an existing tenant configuration. - * - * @param tenantId The `tenantId` corresponding to the tenant to delete. - * @param tenantOptions The properties to update on the provided tenant. - * - * @return A promise fulfilled with the update tenant data. - */ - updateTenant(tenantId: string, tenantOptions: UpdateTenantRequest): Promise; - } -} +export { ActionCodeSettings } from './action-code-settings-builder'; + +export { + Auth, +} from './auth'; + +export { + AuthFactorType, + AuthProviderConfig, + AuthProviderConfigFilter, + CreateMultiFactorInfoRequest, + CreatePhoneMultiFactorInfoRequest, + CreateRequest, + EmailSignInProviderConfig, + ListProviderConfigResults, + MultiFactorConfig, + MultiFactorConfigState, + MultiFactorCreateSettings, + MultiFactorUpdateSettings, + OIDCAuthProviderConfig, + OIDCUpdateAuthProviderRequest, + SAMLAuthProviderConfig, + SAMLUpdateAuthProviderRequest, + UpdateAuthProviderRequest, + UpdateMultiFactorInfoRequest, + UpdatePhoneMultiFactorInfoRequest, + UpdateRequest, +} from './auth-config'; + +export { + BaseAuth, + DeleteUsersResult, + GetUsersResult, + ListUsersResult, + SessionCookieOptions, +} from './base-auth'; + +export { + EmailIdentifier, + PhoneIdentifier, + ProviderIdentifier, + UidIdentifier, + UserIdentifier, +} from './identifier'; + +export { + CreateTenantRequest, + Tenant, + UpdateTenantRequest, +} from './tenant'; + +export { + ListTenantsResult, + TenantAwareAuth, + TenantManager, +} from './tenant-manager'; + +export { DecodedIdToken } from './token-verifier'; + +export { + HashAlgorithmType, + UserImportOptions, + UserImportRecord, + UserImportResult, + UserMetadataRequest, + UserProviderRequest, +} from './user-import-builder'; + +export { + MultiFactorInfo, + MultiFactorSettings, + PhoneMultiFactorInfo, + UserInfo, + UserMetadata, + UserRecord, +} from './user-record'; + +export { auth } from './auth-namespace'; diff --git a/src/auth/tenant-manager.ts b/src/auth/tenant-manager.ts index b11f4d605b..e58f803615 100644 --- a/src/auth/tenant-manager.ts +++ b/src/auth/tenant-manager.ts @@ -14,18 +14,134 @@ * limitations under the License. */ -import { AuthRequestHandler } from './auth-api-request'; -import { FirebaseApp } from '../app/firebase-app'; -import { TenantAwareAuth } from './auth'; -import { Tenant, TenantServerResponse } from './tenant'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; import * as validator from '../utils/validator'; -import { auth } from './index'; +import { App } from '../app'; +import * as utils from '../utils/index'; +import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; + +import { BaseAuth, SessionCookieOptions } from './base-auth'; +import { Tenant, TenantServerResponse, CreateTenantRequest, UpdateTenantRequest } from './tenant'; +import { FirebaseTokenGenerator, EmulatedSigner, cryptoSignerFromApp } from './token-generator'; +import { + AuthRequestHandler, TenantAwareAuthRequestHandler, useEmulator, +} from './auth-api-request'; +import { DecodedIdToken } from './token-verifier'; + +/** + * Interface representing the object returned from a + * {@link auth.TenantManager.listTenants `listTenants()`} + * operation. + * Contains the list of tenants for the current batch and the next page token if available. + */ +export interface ListTenantsResult { -import ListTenantsResult = auth.ListTenantsResult; -import TenantManagerInterface = auth.TenantManager; -import CreateTenantRequest = auth.CreateTenantRequest; -import UpdateTenantRequest = auth.UpdateTenantRequest; + /** + * The list of {@link auth.Tenant `Tenant`} objects for the downloaded batch. + */ + tenants: Tenant[]; + + /** + * The next page token if available. This is needed for the next batch download. + */ + pageToken?: string; +} + +/** + * The tenant aware Auth class. + */ +export class TenantAwareAuth extends BaseAuth { + + public readonly tenantId: string; + + /** + * The TenantAwareAuth class constructor. + * + * @param {object} app The app that created this tenant. + * @param tenantId The corresponding tenant ID. + * @constructor + * @internal + */ + constructor(app: App, tenantId: string) { + const cryptoSigner = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); + const tokenGenerator = new FirebaseTokenGenerator(cryptoSigner, tenantId); + super(app, new TenantAwareAuthRequestHandler(app, tenantId), tokenGenerator); + utils.addReadonlyGetter(this, 'tenantId', tenantId); + } + + /** + * 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} A Promise that will be fulfilled after a successful + * verification. + */ + public verifyIdToken(idToken: string, checkRevoked = false): Promise { + 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} A promise that resolves on success with the created session cookie. + */ + public createSessionCookie( + idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { + // 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(() => { + 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} A Promise that will be fulfilled after a successful + * verification. + */ + public verifySessionCookie( + sessionCookie: string, checkRevoked = false): Promise { + return super.verifySessionCookie(sessionCookie, checkRevoked) + .then((decodedClaims) => { + if (decodedClaims.firebase.tenant !== this.tenantId) { + throw new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + } + return decodedClaims; + }); + } +} /** * Data structure used to help manage tenant related operations. @@ -34,15 +150,19 @@ import UpdateTenantRequest = auth.UpdateTenantRequest; * - Getting a TenantAwareAuth instance for running Auth related operations (user mgmt, provider config mgmt, etc) * in the context of a specified tenant. */ -export class TenantManager implements TenantManagerInterface { +export class TenantManager { private readonly authRequestHandler: AuthRequestHandler; private readonly tenantsMap: {[key: string]: TenantAwareAuth}; /** * Initializes a TenantManager instance for a specified FirebaseApp. + * * @param app The app for this TenantManager instance. + * + * @constructor + * @internal */ - constructor(private readonly app: FirebaseApp) { + constructor(private readonly app: App) { this.authRequestHandler = new AuthRequestHandler(app); this.tenantsMap = {}; } diff --git a/src/auth/tenant.ts b/src/auth/tenant.ts index a7b1d188f4..8bbc0438c8 100644 --- a/src/auth/tenant.ts +++ b/src/auth/tenant.ts @@ -17,14 +17,45 @@ import * as validator from '../utils/validator'; import { deepCopy } from '../utils/deep-copy'; import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; + import { EmailSignInConfig, EmailSignInConfigServerRequest, MultiFactorAuthServerConfig, - MultiFactorAuthConfig, validateTestPhoneNumbers, + MultiFactorConfig, validateTestPhoneNumbers, EmailSignInProviderConfig, + MultiFactorAuthConfig, } from './auth-config'; -import { auth } from './index'; -import TenantInterface = auth.Tenant; -import UpdateTenantRequest = auth.UpdateTenantRequest; +/** + * Interface representing the properties to update on the provided tenant. + */ +export interface UpdateTenantRequest { + + /** + * The tenant display name. + */ + displayName?: string; + + /** + * The email sign in configuration. + */ + emailSignInConfig?: EmailSignInProviderConfig; + + /** + * The multi-factor auth configuration to update on the tenant. + */ + multiFactorConfig?: MultiFactorConfig; + + /** + * The updated map containing the test phone number / code pairs for the tenant. + * Passing null clears the previously save phone number / code pairs. + */ + testPhoneNumbers?: { [phoneNumber: string]: string } | null; +} + +/** + * Interface representing the properties to set on a new tenant. + */ +export type CreateTenantRequest = UpdateTenantRequest; + /** The corresponding server side representation of a TenantOptions object. */ export interface TenantOptionsServerRequest extends EmailSignInConfigServerRequest { @@ -46,11 +77,12 @@ export interface TenantServerResponse { /** * Tenant class that defines a Firebase Auth tenant. */ -export class Tenant implements TenantInterface { +export class Tenant { + public readonly tenantId: string; public readonly displayName?: string; - public readonly emailSignInConfig?: EmailSignInConfig; - public readonly multiFactorConfig?: MultiFactorAuthConfig; + private readonly emailSignInConfig_?: EmailSignInConfig; + private readonly multiFactorConfig_?: MultiFactorAuthConfig; public readonly testPhoneNumbers?: {[phoneNumber: string]: string}; /** @@ -59,6 +91,8 @@ export class Tenant implements TenantInterface { * @param {TenantOptions} tenantOptions The properties to convert to a server request. * @param {boolean} createRequest Whether this is a create request. * @return {object} The equivalent server request. + * + * @internal */ public static buildServerRequest( tenantOptions: UpdateTenantRequest, createRequest: boolean): TenantOptionsServerRequest { @@ -85,6 +119,8 @@ export class Tenant implements TenantInterface { * * @param {string} resourceName The server side resource name * @return {?string} The tenant ID corresponding to the resource, null otherwise. + * + * @internal */ public static getTenantIdFromResourceName(resourceName: string): string | null { // name is of form projects/project1/tenants/tenant1 @@ -160,6 +196,7 @@ export class Tenant implements TenantInterface { * * @param response The server side response used to initialize the Tenant object. * @constructor + * @internal */ constructor(response: TenantServerResponse) { const tenantId = Tenant.getTenantIdFromResourceName(response.name); @@ -172,28 +209,36 @@ export class Tenant implements TenantInterface { this.tenantId = tenantId; this.displayName = response.displayName; try { - this.emailSignInConfig = new EmailSignInConfig(response); + this.emailSignInConfig_ = new EmailSignInConfig(response); } catch (e) { // If allowPasswordSignup is undefined, it is disabled by default. - this.emailSignInConfig = new EmailSignInConfig({ + this.emailSignInConfig_ = new EmailSignInConfig({ allowPasswordSignup: false, }); } if (typeof response.mfaConfig !== 'undefined') { - this.multiFactorConfig = new MultiFactorAuthConfig(response.mfaConfig); + this.multiFactorConfig_ = new MultiFactorAuthConfig(response.mfaConfig); } if (typeof response.testPhoneNumbers !== 'undefined') { this.testPhoneNumbers = deepCopy(response.testPhoneNumbers || {}); } } + get emailSignInConfig(): EmailSignInProviderConfig | undefined { + return this.emailSignInConfig_; + } + + get multiFactorConfig(): MultiFactorConfig | undefined { + return this.multiFactorConfig_; + } + /** @return {object} The plain object representation of the tenant. */ public toJSON(): object { const json = { tenantId: this.tenantId, displayName: this.displayName, - emailSignInConfig: this.emailSignInConfig?.toJSON(), - multiFactorConfig: this.multiFactorConfig?.toJSON(), + emailSignInConfig: this.emailSignInConfig_?.toJSON(), + multiFactorConfig: this.multiFactorConfig_?.toJSON(), testPhoneNumbers: this.testPhoneNumbers, }; if (typeof json.multiFactorConfig === 'undefined') { diff --git a/src/auth/token-generator.ts b/src/auth/token-generator.ts index 61db2cc9b2..a9c03130c6 100644 --- a/src/auth/token-generator.ts +++ b/src/auth/token-generator.ts @@ -23,6 +23,7 @@ import { AuthorizedHttpClient, HttpError, HttpRequestConfig, HttpClient } from ' import * as validator from '../utils/validator'; import { toWebSafeBase64 } from '../utils'; import { Algorithm } from 'jsonwebtoken'; +import { App } from '../app'; const ALGORITHM_RS256: Algorithm = 'RS256' as const; @@ -260,13 +261,13 @@ export class EmulatedSigner implements CryptoSigner { * @param {FirebaseApp} app A FirebaseApp instance. * @return {CryptoSigner} A CryptoSigner instance. */ -export function cryptoSignerFromApp(app: FirebaseApp): CryptoSigner { +export function cryptoSignerFromApp(app: App): CryptoSigner { const credential = app.options.credential; if (credential instanceof ServiceAccountCredential) { return new ServiceAccountSigner(credential); } - return new IAMSigner(new AuthorizedHttpClient(app), app.options.serviceAccountId); + return new IAMSigner(new AuthorizedHttpClient(app as FirebaseApp), app.options.serviceAccountId); } /** diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index 4c244523a4..99c6372687 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -19,10 +19,156 @@ import * as util from '../utils/index'; import * as validator from '../utils/validator'; import * as jwt from 'jsonwebtoken'; import { HttpClient, HttpRequestConfig, HttpError } from '../utils/api-request'; -import { FirebaseApp } from '../app/firebase-app'; -import { auth } from './index'; +import { App } from '../app'; -import DecodedIdToken = auth.DecodedIdToken; +/** + * Interface representing a decoded Firebase ID token, returned from the + * {@link auth.Auth.verifyIdToken `verifyIdToken()`} method. + * + * Firebase ID tokens are OpenID Connect spec-compliant JSON Web Tokens (JWTs). + * See the + * [ID Token section of the OpenID Connect spec](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) + * for more information about the specific properties below. + */ +export interface DecodedIdToken { + + /** + * The audience for which this token is intended. + * + * This value is a string equal to your Firebase project ID, the unique + * identifier for your Firebase project, which can be found in [your project's + * settings](https://console.firebase.google.com/project/_/settings/general/android:com.random.android). + */ + aud: string; + + /** + * Time, in seconds since the Unix epoch, when the end-user authentication + * occurred. + * + * This value is not set when this particular ID token was created, but when the + * user initially logged in to this session. In a single session, the Firebase + * SDKs will refresh a user's ID tokens every hour. Each ID token will have a + * different [`iat`](#iat) value, but the same `auth_time` value. + */ + auth_time: number; + + /** + * The email of the user to whom the ID token belongs, if available. + */ + email?: string; + + /** + * Whether or not the email of the user to whom the ID token belongs is + * verified, provided the user has an email. + */ + email_verified?: boolean; + + /** + * The ID token's expiration time, in seconds since the Unix epoch. That is, the + * time at which this ID token expires and should no longer be considered valid. + * + * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new + * ID token with up to a one hour expiration. + */ + exp: number; + + /** + * Information about the sign in event, including which sign in provider was + * used and provider-specific identity details. + * + * This data is provided by the Firebase Authentication service and is a + * reserved claim in the ID token. + */ + firebase: { + + /** + * Provider-specific identity details corresponding + * to the provider used to sign in the user. + */ + identities: { + [key: string]: any; + }; + + /** + * The ID of the provider used to sign in the user. + * One of `"anonymous"`, `"password"`, `"facebook.com"`, `"github.com"`, + * `"google.com"`, `"twitter.com"`, `"apple.com"`, `"microsoft.com"`, + * "yahoo.com"`, `"phone"`, `"playgames.google.com"`, `"gc.apple.com"`, + * or `"custom"`. + * + * Additional Identity Platform provider IDs include `"linkedin.com"`, + * OIDC and SAML identity providers prefixed with `"saml."` and `"oidc."` + * respectively. + */ + sign_in_provider: string; + + /** + * The type identifier or `factorId` of the second factor, provided the + * ID token was obtained from a multi-factor authenticated user. + * For phone, this is `"phone"`. + */ + sign_in_second_factor?: string; + + /** + * The `uid` of the second factor used to sign in, provided the + * ID token was obtained from a multi-factor authenticated user. + */ + second_factor_identifier?: string; + + /** + * The ID of the tenant the user belongs to, if available. + */ + tenant?: string; + [key: string]: any; + }; + + /** + * The ID token's issued-at time, in seconds since the Unix epoch. That is, the + * time at which this ID token was issued and should start to be considered + * valid. + * + * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new + * ID token with a new issued-at time. If you want to get the time at which the + * user session corresponding to the ID token initially occurred, see the + * [`auth_time`](#auth_time) property. + */ + iat: number; + + /** + * The issuer identifier for the issuer of the response. + * + * This value is a URL with the format + * `https://securetoken.google.com/`, where `` is the + * same project ID specified in the [`aud`](#aud) property. + */ + iss: string; + + /** + * The phone number of the user to whom the ID token belongs, if available. + */ + phone_number?: string; + + /** + * The photo URL for the user to whom the ID token belongs, if available. + */ + picture?: string; + + /** + * The `uid` corresponding to the user who the ID token belonged to. + * + * As a convenience, this value is copied over to the [`uid`](#uid) property. + */ + sub: string; + + /** + * The `uid` corresponding to the user who the ID token belonged to. + * + * This value is not actually in the JWT token claims itself. It is added as a + * convenience, and is set as the value of the [`sub`](#sub) property. + */ + uid: string; + [key: string]: any; +} // Audience to use for Firebase Auth Custom tokens const FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit'; @@ -36,7 +182,11 @@ const CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/secur // URL containing the public keys for Firebase session cookies. This will be updated to a different URL soon. const SESSION_COOKIE_CERT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys'; -/** User facing token information related to the Firebase ID token. */ +/** + * User facing token information related to the Firebase ID token. + * + * @internal + */ export const ID_TOKEN_INFO: FirebaseTokenInfo = { url: 'https://firebase.google.com/docs/auth/admin/verify-id-tokens', verifyApiName: 'verifyIdToken()', @@ -45,7 +195,11 @@ export const ID_TOKEN_INFO: FirebaseTokenInfo = { expiredErrorCode: AuthClientErrorCode.ID_TOKEN_EXPIRED, }; -/** User facing token information related to the Firebase session cookie. */ +/** + * User facing token information related to the Firebase session cookie. + * + * @internal + */ export const SESSION_COOKIE_INFO: FirebaseTokenInfo = { url: 'https://firebase.google.com/docs/auth/admin/manage-cookies', verifyApiName: 'verifySessionCookie()', @@ -54,7 +208,11 @@ export const SESSION_COOKIE_INFO: FirebaseTokenInfo = { expiredErrorCode: AuthClientErrorCode.SESSION_COOKIE_EXPIRED, }; -/** Interface that defines token related user facing information. */ +/** + * Interface that defines token related user facing information. + * + * @internal + */ export interface FirebaseTokenInfo { /** Documentation URL. */ url: string; @@ -70,6 +228,8 @@ export interface FirebaseTokenInfo { /** * Class for verifying general purpose Firebase JWTs. This verifies ID tokens and session cookies. + * + * @internal */ export class FirebaseTokenVerifier { private publicKeys: {[key: string]: string}; @@ -78,7 +238,7 @@ export class FirebaseTokenVerifier { constructor(private clientCertUrl: string, private algorithm: jwt.Algorithm, private issuer: string, private tokenInfo: FirebaseTokenInfo, - private readonly app: FirebaseApp) { + private readonly app: App) { if (!validator.isURL(clientCertUrl)) { throw new FirebaseAuthError( @@ -342,8 +502,10 @@ export class FirebaseTokenVerifier { * * @param {FirebaseApp} app Firebase app instance. * @return {FirebaseTokenVerifier} + * + * @internal */ -export function createIdTokenVerifier(app: FirebaseApp): FirebaseTokenVerifier { +export function createIdTokenVerifier(app: App): FirebaseTokenVerifier { return new FirebaseTokenVerifier( CLIENT_CERT_URL, ALGORITHM_RS256, @@ -358,8 +520,10 @@ export function createIdTokenVerifier(app: FirebaseApp): FirebaseTokenVerifier { * * @param {FirebaseApp} app Firebase app instance. * @return {FirebaseTokenVerifier} + * + * @internal */ -export function createSessionCookieVerifier(app: FirebaseApp): FirebaseTokenVerifier { +export function createSessionCookieVerifier(app: App): FirebaseTokenVerifier { return new FirebaseTokenVerifier( SESSION_COOKIE_CERT_URL, ALGORITHM_RS256, diff --git a/src/auth/user-import-builder.ts b/src/auth/user-import-builder.ts index 92e84cc08c..72faa5304e 100644 --- a/src/auth/user-import-builder.ts +++ b/src/auth/user-import-builder.ts @@ -19,13 +19,241 @@ import * as utils from '../utils'; import * as validator from '../utils/validator'; import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; import { FirebaseArrayIndexError } from '../firebase-namespace-api'; -import { auth } from './index'; +import { + UpdateMultiFactorInfoRequest, UpdatePhoneMultiFactorInfoRequest, MultiFactorUpdateSettings +} from './auth-config'; -import UpdateMultiFactorInfoRequest = auth.UpdateMultiFactorInfoRequest; -import UpdatePhoneMultiFactorInfoRequest = auth.UpdatePhoneMultiFactorInfoRequest; -import UserImportRecord = auth.UserImportRecord; -import UserImportOptions = auth.UserImportOptions; -import UserImportResult = auth.UserImportResult; +export type HashAlgorithmType = 'SCRYPT' | 'STANDARD_SCRYPT' | 'HMAC_SHA512' | + 'HMAC_SHA256' | 'HMAC_SHA1' | 'HMAC_MD5' | 'MD5' | 'PBKDF_SHA1' | 'BCRYPT' | + 'PBKDF2_SHA256' | 'SHA512' | 'SHA256' | 'SHA1'; + +/** + * Interface representing the user import options needed for + * {@link auth.Auth.importUsers `importUsers()`} method. This is used to + * provide the password hashing algorithm information. + */ +export interface UserImportOptions { + + /** + * The password hashing information. + */ + hash: { + + /** + * The password hashing algorithm identifier. The following algorithm + * identifiers are supported: + * `SCRYPT`, `STANDARD_SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, `HMAC_SHA1`, + * `HMAC_MD5`, `MD5`, `PBKDF_SHA1`, `BCRYPT`, `PBKDF2_SHA256`, `SHA512`, + * `SHA256` and `SHA1`. + */ + algorithm: HashAlgorithmType; + + /** + * The signing key used in the hash algorithm in buffer bytes. + * Required by hashing algorithms `SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, + * `HAMC_SHA1` and `HMAC_MD5`. + */ + key?: Buffer; + + /** + * The salt separator in buffer bytes which is appended to salt when + * verifying a password. This is only used by the `SCRYPT` algorithm. + */ + saltSeparator?: Buffer; + + /** + * The number of rounds for hashing calculation. + * Required for `SCRYPT`, `MD5`, `SHA512`, `SHA256`, `SHA1`, `PBKDF_SHA1` and + * `PBKDF2_SHA256`. + */ + rounds?: number; + + /** + * The memory cost required for `SCRYPT` algorithm, or the CPU/memory cost. + * Required for `STANDARD_SCRYPT` algorithm. + */ + memoryCost?: number; + + /** + * The parallelization of the hashing algorithm. Required for the + * `STANDARD_SCRYPT` algorithm. + */ + parallelization?: number; + + /** + * The block size (normally 8) of the hashing algorithm. Required for the + * `STANDARD_SCRYPT` algorithm. + */ + blockSize?: number; + /** + * The derived key length of the hashing algorithm. Required for the + * `STANDARD_SCRYPT` algorithm. + */ + derivedKeyLength?: number; + }; +} + +/** + * Interface representing a user to import to Firebase Auth via the + * {@link auth.Auth.importUsers `importUsers()`} method. + */ +export interface UserImportRecord { + + /** + * The user's `uid`. + */ + uid: string; + + /** + * The user's primary email, if set. + */ + email?: string; + + /** + * Whether or not the user's primary email is verified. + */ + emailVerified?: boolean; + + /** + * The user's display name. + */ + displayName?: string; + + /** + * The user's primary phone number, if set. + */ + phoneNumber?: string; + + /** + * The user's photo URL. + */ + photoURL?: string; + + /** + * Whether or not the user is disabled: `true` for disabled; `false` for + * enabled. + */ + disabled?: boolean; + + /** + * Additional metadata about the user. + */ + metadata?: UserMetadataRequest; + + /** + * An array of providers (for example, Google, Facebook) linked to the user. + */ + providerData?: UserProviderRequest[]; + + /** + * The user's custom claims object if available, typically used to define + * user roles and propagated to an authenticated user's ID token. + */ + customClaims?: { [key: string]: any }; + + /** + * The buffer of bytes representing the user's hashed password. + * When a user is to be imported with a password hash, + * {@link auth.UserImportOptions `UserImportOptions`} are required to be + * specified to identify the hashing algorithm used to generate this hash. + */ + passwordHash?: Buffer; + + /** + * The buffer of bytes representing the user's password salt. + */ + passwordSalt?: Buffer; + + /** + * The identifier of the tenant where user is to be imported to. + * When not provided in an `admin.auth.Auth` context, the user is uploaded to + * the default parent project. + * When not provided in an `admin.auth.TenantAwareAuth` context, the user is uploaded + * to the tenant corresponding to that `TenantAwareAuth` instance's tenant ID. + */ + tenantId?: string; + + /** + * The user's multi-factor related properties. + */ + multiFactor?: MultiFactorUpdateSettings; +} + +/** + * User metadata to include when importing a user. + */ +export interface UserMetadataRequest { + + /** + * The date the user last signed in, formatted as a UTC string. + */ + lastSignInTime?: string; + + /** + * The date the user was created, formatted as a UTC string. + */ + creationTime?: string; +} + +/** + * User provider data to include when importing a user. + */ +export interface UserProviderRequest { + + /** + * The user identifier for the linked provider. + */ + uid: string; + + /** + * The display name for the linked provider. + */ + displayName?: string; + + /** + * The email for the linked provider. + */ + email?: string; + + /** + * The phone number for the linked provider. + */ + phoneNumber?: string; + + /** + * The photo URL for the linked provider. + */ + photoURL?: string; + + /** + * The linked provider ID (for example, "google.com" for the Google provider). + */ + providerId: string; +} + +/** + * Interface representing the response from the + * {@link auth.Auth.importUsers `importUsers()`} method for batch + * importing users to Firebase Auth. + */ +export interface UserImportResult { + + /** + * The number of user records that failed to import to Firebase Auth. + */ + failureCount: number; + + /** + * The number of user records that successfully imported to Firebase Auth. + */ + successCount: number; + + /** + * An array of errors corresponding to the provided users to import. The + * length of this array is equal to [`failureCount`](#failureCount). + */ + errors: FirebaseArrayIndexError[]; +} /** Interface representing an Auth second factor in Auth server format. */ export interface AuthFactorInfo { diff --git a/src/auth/user-record.ts b/src/auth/user-record.ts index 01092a25da..149b2f89e5 100644 --- a/src/auth/user-record.ts +++ b/src/auth/user-record.ts @@ -19,14 +19,6 @@ import { deepCopy } from '../utils/deep-copy'; import { isNonNullObject } from '../utils/validator'; import * as utils from '../utils'; import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; -import { auth } from './index'; - -import MultiFactorInfoInterface = auth.MultiFactorInfo; -import PhoneMultiFactorInfoInterface = auth.PhoneMultiFactorInfo; -import MultiFactorSettings = auth.MultiFactorSettings; -import UserMetadataInterface = auth.UserMetadata; -import UserInfoInterface = auth.UserInfo; -import UserRecordInterface = auth.UserRecord; /** * 'REDACTED', encoded as a base64 string. @@ -96,7 +88,7 @@ enum MultiFactorId { /** * Abstract class representing a multi-factor info interface. */ -export abstract class MultiFactorInfo implements MultiFactorInfoInterface { +export abstract class MultiFactorInfo { public readonly uid: string; public readonly displayName?: string; public readonly factorId: string; @@ -107,7 +99,7 @@ export abstract class MultiFactorInfo implements MultiFactorInfoInterface { * If no MultiFactorInfo is associated with the response, null is returned. * * @param response The server side response. - * @constructor + * @internal */ public static initMultiFactorInfo(response: MultiFactorInfoResponse): MultiFactorInfo | null { let multiFactorInfo: MultiFactorInfo | null = null; @@ -125,6 +117,7 @@ export abstract class MultiFactorInfo implements MultiFactorInfoInterface { * * @param response The server side response. * @constructor + * @internal */ constructor(response: MultiFactorInfoResponse) { this.initFromServerResponse(response); @@ -146,6 +139,8 @@ export abstract class MultiFactorInfo implements MultiFactorInfoInterface { * @param response The server side response. * @return The multi-factor ID associated with the provided response. If the response is * not associated with any known multi-factor ID, null is returned. + * + * @internal */ protected abstract getFactorId(response: MultiFactorInfoResponse): string | null; @@ -178,7 +173,7 @@ export abstract class MultiFactorInfo implements MultiFactorInfoInterface { } /** Class representing a phone MultiFactorInfo object. */ -export class PhoneMultiFactorInfo extends MultiFactorInfo implements PhoneMultiFactorInfoInterface { +export class PhoneMultiFactorInfo extends MultiFactorInfo { public readonly phoneNumber: string; /** @@ -186,6 +181,7 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo implements PhoneMultiF * * @param response The server side response. * @constructor + * @internal */ constructor(response: MultiFactorInfoResponse) { super(response); @@ -207,6 +203,8 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo implements PhoneMultiF * @param response The server side response. * @return The multi-factor ID associated with the provided response. If the response is * not associated with any known multi-factor ID, null is returned. + * + * @internal */ protected getFactorId(response: MultiFactorInfoResponse): string | null { return (response && response.phoneInfo) ? MultiFactorId.Phone : null; @@ -214,7 +212,7 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo implements PhoneMultiF } /** Class representing multi-factor related properties of a user. */ -export class MultiFactor implements MultiFactorSettings { +export class MultiFactorSettings { public enrolledFactors: MultiFactorInfo[]; /** @@ -222,6 +220,7 @@ export class MultiFactor implements MultiFactorSettings { * * @param response The server side response. * @constructor + * @internal */ constructor(response: GetAccountInfoUserResponse) { const parsedEnrolledFactors: MultiFactorInfo[] = []; @@ -253,12 +252,8 @@ export class MultiFactor implements MultiFactorSettings { /** * User metadata class that provides metadata information like user account creation * and last sign in time. - * - * @param response The server side response returned from the getAccountInfo - * endpoint. - * @constructor */ -export class UserMetadata implements UserMetadataInterface { +export class UserMetadata { public readonly creationTime: string; public readonly lastSignInTime: string; @@ -269,6 +264,12 @@ export class UserMetadata implements UserMetadataInterface { */ public readonly lastRefreshTime: string | null; + /** + * @param response The server side response returned from the getAccountInfo + * endpoint. + * @constructor + * @internal + */ constructor(response: GetAccountInfoUserResponse) { // Creation date should always be available but due to some backend bugs there // were cases in the past where users did not have creation date properly set. @@ -292,12 +293,8 @@ export class UserMetadata implements UserMetadataInterface { /** * User info class that provides provider user information for different * Firebase providers like google.com, facebook.com, password, etc. - * - * @param response The server side response returned from the getAccountInfo - * endpoint. - * @constructor */ -export class UserInfo implements UserInfoInterface { +export class UserInfo { public readonly uid: string; public readonly displayName: string; public readonly email: string; @@ -305,6 +302,13 @@ export class UserInfo implements UserInfoInterface { public readonly providerId: string; public readonly phoneNumber: string; + + /** + * @param response The server side response returned from the getAccountInfo + * endpoint. + * @constructor + * @internal + */ constructor(response: ProviderUserInfoResponse) { // Provider user id and provider id are required. if (!response.rawId || !response.providerId) { @@ -342,7 +346,7 @@ export class UserInfo implements UserInfoInterface { * endpoint. * @constructor */ -export class UserRecord implements UserRecordInterface { +export class UserRecord { public readonly uid: string; public readonly email: string; public readonly emailVerified: boolean; @@ -357,8 +361,14 @@ export class UserRecord implements UserRecordInterface { public readonly customClaims: {[key: string]: any}; public readonly tenantId?: string | null; public readonly tokensValidAfterTime?: string; - public readonly multiFactor?: MultiFactor; + public readonly multiFactor?: MultiFactorSettings; + /** + * @param response The server side response returned from the getAccountInfo + * endpoint. + * @constructor + * @internal + */ constructor(response: GetAccountInfoUserResponse) { // The Firebase user id is required. if (!response.localId) { @@ -404,7 +414,7 @@ export class UserRecord implements UserRecordInterface { } utils.addReadonlyGetter(this, 'tokensValidAfterTime', validAfterTime || undefined); utils.addReadonlyGetter(this, 'tenantId', response.tenantId); - const multiFactor = new MultiFactor(response); + const multiFactor = new MultiFactorSettings(response); if (multiFactor.enrolledFactors.length > 0) { utils.addReadonlyGetter(this, 'multiFactor', multiFactor); } diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 96b631356d..2eb4f75fe5 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -43,18 +43,11 @@ import { ActionCodeSettingsBuilder } from '../../../src/auth/action-code-setting import { SAMLConfigServerResponse } from '../../../src/auth/auth-config'; import { expectUserImportResult } from './user-import-builder.spec'; import { getSdkVersion } from '../../../src/utils/index'; -import { auth } from '../../../src/auth/index'; - -import UserImportRecord = auth.UserImportRecord; -import OIDCAuthProviderConfig = auth.OIDCAuthProviderConfig; -import SAMLAuthProviderConfig = auth.SAMLAuthProviderConfig; -import OIDCUpdateAuthProviderRequest = auth.OIDCUpdateAuthProviderRequest; -import SAMLUpdateAuthProviderRequest = auth.SAMLUpdateAuthProviderRequest; -import UserIdentifier = auth.UserIdentifier; -import UpdateRequest = auth.UpdateRequest; -import UpdateMultiFactorInfoRequest = auth.UpdateMultiFactorInfoRequest; -import CreateTenantRequest = auth.CreateTenantRequest; -import UpdateTenantRequest = auth.UpdateTenantRequest; +import { + UserImportRecord, OIDCAuthProviderConfig, SAMLAuthProviderConfig, OIDCUpdateAuthProviderRequest, + SAMLUpdateAuthProviderRequest, UserIdentifier, UpdateRequest, UpdateMultiFactorInfoRequest, + CreateTenantRequest, UpdateTenantRequest, +} from '../../../src/auth/index'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/auth/auth-config.spec.ts b/test/unit/auth/auth-config.spec.ts index ad81c5e62c..5e55bb6992 100644 --- a/test/unit/auth/auth-config.spec.ts +++ b/test/unit/auth/auth-config.spec.ts @@ -27,12 +27,10 @@ import { EmailSignInConfig, MultiFactorAuthConfig, validateTestPhoneNumbers, MAXIMUM_TEST_PHONE_NUMBERS, } from '../../../src/auth/auth-config'; -import { auth } from '../../../src/auth/index'; - -import SAMLUpdateAuthProviderRequest = auth.SAMLUpdateAuthProviderRequest; -import OIDCUpdateAuthProviderRequest = auth.OIDCUpdateAuthProviderRequest; -import SAMLAuthProviderConfig = auth.SAMLAuthProviderConfig; -import OIDCAuthProviderConfig = auth.OIDCAuthProviderConfig; +import { + SAMLUpdateAuthProviderRequest, OIDCUpdateAuthProviderRequest, + SAMLAuthProviderConfig, OIDCAuthProviderConfig, +} from '../../../src/auth/index'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 981624cf69..406d1c7ee7 100644 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -27,7 +27,7 @@ import * as chaiAsPromised from 'chai-as-promised'; import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; -import { Auth, TenantAwareAuth, BaseAuth } from '../../../src/auth/auth'; +import { Auth, TenantAwareAuth, BaseAuth } from '../../../src/auth/index'; import { UserRecord } from '../../../src/auth/user-record'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { @@ -44,11 +44,7 @@ import { deepCopy } from '../../../src/utils/deep-copy'; import { TenantManager } from '../../../src/auth/tenant-manager'; import { ServiceAccountCredential } from '../../../src/app/credential-internal'; import { HttpClient } from '../../../src/utils/api-request'; -import { auth } from '../../../src/auth/index'; - -import DecodedIdToken = auth.DecodedIdToken; -import UpdateRequest = auth.UpdateRequest; -import AuthProviderConfigFilter = auth.AuthProviderConfigFilter; +import { DecodedIdToken, UpdateRequest, AuthProviderConfigFilter } from '../../../src/auth/index'; chai.should(); chai.use(sinonChai); @@ -60,9 +56,9 @@ const expect = chai.expect; interface AuthTest { name: string; supportsTenantManagement: boolean; - Auth: new (...args: any[]) => BaseAuth; + Auth: new (...args: any[]) => BaseAuth; RequestHandler: new (...args: any[]) => AbstractAuthRequestHandler; - init(app: FirebaseApp): BaseAuth; + init(app: FirebaseApp): BaseAuth; } @@ -264,13 +260,13 @@ const AUTH_CONFIGS: AuthTest[] = [ ]; AUTH_CONFIGS.forEach((testConfig) => { describe(testConfig.name, () => { - let auth: BaseAuth; + let auth: BaseAuth; let mockApp: FirebaseApp; let getTokenStub: sinon.SinonStub; let oldProcessEnv: NodeJS.ProcessEnv; - let nullAccessTokenAuth: BaseAuth; - let malformedAccessTokenAuth: BaseAuth; - let rejectedPromiseAccessTokenAuth: BaseAuth; + let nullAccessTokenAuth: BaseAuth; + let malformedAccessTokenAuth: BaseAuth; + let rejectedPromiseAccessTokenAuth: BaseAuth; beforeEach(() => { mockApp = mocks.app(); diff --git a/test/unit/auth/tenant-manager.spec.ts b/test/unit/auth/tenant-manager.spec.ts index c00096da32..b769d236b0 100644 --- a/test/unit/auth/tenant-manager.spec.ts +++ b/test/unit/auth/tenant-manager.spec.ts @@ -28,11 +28,9 @@ import { AuthRequestHandler } from '../../../src/auth/auth-api-request'; import { Tenant, TenantServerResponse } from '../../../src/auth/tenant'; import { TenantManager } from '../../../src/auth/tenant-manager'; import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; -import { auth } from '../../../src/auth/index'; - -import CreateTenantRequest = auth.CreateTenantRequest; -import UpdateTenantRequest = auth.UpdateTenantRequest; -import ListTenantsResult = auth.ListTenantsResult; +import { + CreateTenantRequest, UpdateTenantRequest, ListTenantsResult, +} from '../../../src/auth/index'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/auth/tenant.spec.ts b/test/unit/auth/tenant.spec.ts index db090b3279..31370e17bc 100644 --- a/test/unit/auth/tenant.spec.ts +++ b/test/unit/auth/tenant.spec.ts @@ -22,11 +22,9 @@ import * as chaiAsPromised from 'chai-as-promised'; import { deepCopy } from '../../../src/utils/deep-copy'; import { EmailSignInConfig, MultiFactorAuthConfig } from '../../../src/auth/auth-config'; import { Tenant, TenantServerResponse } from '../../../src/auth/tenant'; -import { auth } from '../../../src/auth/index'; - -import EmailSignInProviderConfig = auth.EmailSignInProviderConfig; -import CreateTenantRequest = auth.CreateTenantRequest; -import UpdateTenantRequest = auth.UpdateTenantRequest; +import { + CreateTenantRequest, UpdateTenantRequest, EmailSignInProviderConfig, +} from '../../../src/auth/index'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/auth/user-import-builder.spec.ts b/test/unit/auth/user-import-builder.spec.ts index 14deeaeb64..859265a03a 100644 --- a/test/unit/auth/user-import-builder.spec.ts +++ b/test/unit/auth/user-import-builder.spec.ts @@ -24,11 +24,9 @@ import { } from '../../../src/auth/user-import-builder'; import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; import { toWebSafeBase64 } from '../../../src/utils'; -import { auth } from '../../../src/auth/index'; - -import UpdatePhoneMultiFactorInfoRequest = auth.UpdatePhoneMultiFactorInfoRequest; -import UserImportResult = auth.UserImportResult; -import UserImportRecord = auth.UserImportRecord; +import { + UpdatePhoneMultiFactorInfoRequest, UserImportResult, UserImportRecord, +} from '../../../src/auth'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/auth/user-record.spec.ts b/test/unit/auth/user-record.spec.ts index 0a42de92cb..2e1e3286c9 100644 --- a/test/unit/auth/user-record.spec.ts +++ b/test/unit/auth/user-record.spec.ts @@ -21,9 +21,11 @@ import * as chaiAsPromised from 'chai-as-promised'; import { deepCopy } from '../../../src/utils/deep-copy'; import { - UserInfo, UserMetadata, UserRecord, GetAccountInfoUserResponse, ProviderUserInfoResponse, - MultiFactor, PhoneMultiFactorInfo, MultiFactorInfo, MultiFactorInfoResponse, + GetAccountInfoUserResponse, ProviderUserInfoResponse, MultiFactorInfoResponse, } from '../../../src/auth/user-record'; +import { + UserInfo, UserMetadata, UserRecord, MultiFactorSettings, MultiFactorInfo, PhoneMultiFactorInfo, +} from '../../../src/auth/index'; chai.should(); @@ -395,7 +397,7 @@ describe('MultiFactorInfo', () => { }); }); -describe('MultiFactor', () => { +describe('MultiFactorSettings', () => { const serverResponse = { localId: 'uid123', mfaInfo: [ @@ -440,18 +442,18 @@ describe('MultiFactor', () => { describe('constructor', () => { it('should throw when a non object is provided', () => { expect(() => { - return new MultiFactor(undefined as any); + return new MultiFactorSettings(undefined as any); }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor response'); }); it('should populate an empty enrolledFactors array when given an empty object', () => { - const multiFactor = new MultiFactor({} as any); + const multiFactor = new MultiFactorSettings({} as any); expect(multiFactor.enrolledFactors.length).to.equal(0); }); it('should populate expected enrolledFactors', () => { - const multiFactor = new MultiFactor(serverResponse); + const multiFactor = new MultiFactorSettings(serverResponse); expect(multiFactor.enrolledFactors.length).to.equal(2); expect(multiFactor.enrolledFactors[0]).to.deep.equal(expectedMultiFactorInfo[0]); @@ -461,7 +463,7 @@ describe('MultiFactor', () => { describe('getter', () => { it('should throw when modifying readonly enrolledFactors property', () => { - const multiFactor = new MultiFactor(serverResponse); + const multiFactor = new MultiFactorSettings(serverResponse); expect(() => { (multiFactor as any).enrolledFactors = [ @@ -471,7 +473,7 @@ describe('MultiFactor', () => { }); it('should throw when modifying readonly enrolledFactors internals', () => { - const multiFactor = new MultiFactor(serverResponse); + const multiFactor = new MultiFactorSettings(serverResponse); expect(() => { (multiFactor.enrolledFactors as any)[0] = new PhoneMultiFactorInfo({ @@ -486,7 +488,7 @@ describe('MultiFactor', () => { describe('toJSON', () => { it('should return expected JSON object when given an empty response', () => { - const multiFactor = new MultiFactor({} as any); + const multiFactor = new MultiFactorSettings({} as any); expect(multiFactor.toJSON()).to.deep.equal({ enrolledFactors: [], @@ -494,7 +496,7 @@ describe('MultiFactor', () => { }); it('should return expected JSON object when given a populated response', () => { - const multiFactor = new MultiFactor(serverResponse); + const multiFactor = new MultiFactorSettings(serverResponse); expect(multiFactor.toJSON()).to.deep.equal({ enrolledFactors: [ @@ -971,7 +973,7 @@ describe('UserRecord', () => { }); it('should return expected multiFactor', () => { - const multiFactor = new MultiFactor({ + const multiFactor = new MultiFactorSettings({ localId: 'uid123', mfaInfo: [ { @@ -1001,7 +1003,7 @@ describe('UserRecord', () => { it('should throw when modifying readonly multiFactor property', () => { expect(() => { - (userRecord as any).multiFactor = new MultiFactor({ + (userRecord as any).multiFactor = new MultiFactorSettings({ localId: 'uid123', mfaInfo: [{ mfaEnrollmentId: 'enrollmentId3',