From 35b8af013ea0dab81bad4e7499d59a9b6d61b6e4 Mon Sep 17 00:00:00 2001 From: bojeil-google Date: Mon, 6 Jan 2020 20:23:35 -0800 Subject: [PATCH 1/2] Adds backend multi-factor auth related errors and their client mappings. --- src/auth/user-import-builder.ts | 2 +- src/utils/error.ts | 40 ++++++++++++++++++++++ test/integration/auth.spec.ts | 8 ++--- test/unit/auth/auth-api-request.spec.ts | 6 ++-- test/unit/auth/user-import-builder.spec.ts | 2 +- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/auth/user-import-builder.ts b/src/auth/user-import-builder.ts index 145c373304..91cff27578 100755 --- a/src/auth/user-import-builder.ts +++ b/src/auth/user-import-builder.ts @@ -183,7 +183,7 @@ export function convertMultiFactorInfoToServerFormat(multiFactorInfo: SecondFact } else { // Unsupported second factor. throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(multiFactorInfo)}" provided.`); } } diff --git a/src/utils/error.ts b/src/utils/error.ts index 088c7ab33a..297447697c 100755 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -566,6 +566,11 @@ export class AuthClientErrorCode { code: 'missing-display-name', message: 'The resource being created or edited is missing a valid display name.', }; + public static MISSING_EMAIL = { + code: 'missing-email', + message: 'The email is required for the specified action. For example, a multi-factor user ' + + 'requires a verified email.', + }; public static MISSING_IOS_BUNDLE_ID = { code: 'missing-ios-bundle-id', message: 'The request is missing an iOS Bundle ID.', @@ -624,6 +629,14 @@ export class AuthClientErrorCode { code: 'quota-exceeded', message: 'The project quota for the specified operation has been exceeded.', }; + public static SECOND_FACTOR_LIMIT_EXCEEDED = { + code: 'second-factor-limit-exceeded', + message: 'The maximum number of allowed second factors on a user has been exceeded.', + }; + public static SECOND_FACTOR_UID_ALREADY_EXISTS = { + code: 'second-factor-uid-already-exists', + message: 'The specified second factor "uid" already exists.', + }; public static SESSION_COOKIE_EXPIRED = { code: 'session-cookie-expired', message: 'The Firebase session cookie is expired.', @@ -645,10 +658,23 @@ export class AuthClientErrorCode { message: 'The domain of the continue URL is not whitelisted. Whitelist the domain in the ' + 'Firebase console.', }; + public static UNSUPPORTED_FIRST_FACTOR = { + code: 'unsupported-first-factor', + message: 'A multi-factor user requires a supported first factor.', + }; + public static UNSUPPORTED_SECOND_FACTOR = { + code: 'unsupported-second-factor', + message: 'The request specified an unsupported type of second factor.', + }; public static UNSUPPORTED_TENANT_OPERATION = { code: 'unsupported-tenant-operation', message: 'This operation is not supported in a multi-tenant context.', }; + public static UNVERIFIED_EMAIL = { + code: 'unverified-email', + message: 'A verified email is required for the specified action. For example, a multi-factor user ' + + 'requires a verified email.', + }; public static USER_NOT_FOUND = { code: 'user-not-found', message: 'There is no user record corresponding to the provided identifier.', @@ -811,6 +837,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS', // uploadAccount provides a localId that already exists. DUPLICATE_LOCAL_ID: 'UID_ALREADY_EXISTS', + // Request specified a multi-factor enrollment ID that already exists. + DUPLICATE_MFA_ENROLLMENT_ID: 'SECOND_FACTOR_UID_ALREADY_EXISTS', // setAccountInfo email already exists. EMAIL_EXISTS: 'EMAIL_ALREADY_EXISTS', // Reserved claim name. @@ -849,6 +877,9 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { MISSING_CONFIG_ID: 'MISSING_PROVIDER_ID', // Missing tenant display name: This can be thrown on CreateTenant and UpdateTenant. MISSING_DISPLAY_NAME: 'MISSING_DISPLAY_NAME', + // Email is required for the specified action. For example a multi-factor user requires + // a verified email. + MISSING_EMAIL: 'MISSING_EMAIL', // Missing iOS bundle ID. MISSING_IOS_BUNDLE_ID: 'MISSING_IOS_BUNDLE_ID', // Missing OIDC issuer. @@ -873,6 +904,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { PROJECT_NOT_FOUND: 'PROJECT_NOT_FOUND', // In multi-tenancy context: project creation quota exceeded. QUOTA_EXCEEDED: 'QUOTA_EXCEEDED', + // Currently only 5 second factors can be set on the same user. + SECOND_FACTOR_LIMIT_EXCEEDED: 'SECOND_FACTOR_LIMIT_EXCEEDED', // Tenant not found. TENANT_NOT_FOUND: 'TENANT_NOT_FOUND', // Tenant ID mismatch. @@ -881,8 +914,15 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { TOKEN_EXPIRED: 'ID_TOKEN_EXPIRED', // Continue URL provided in ActionCodeSettings has a domain that is not whitelisted. UNAUTHORIZED_DOMAIN: 'UNAUTHORIZED_DOMAIN', + // A multi-factor user requires a supported first factor. + UNSUPPORTED_FIRST_FACTOR: 'UNSUPPORTED_FIRST_FACTOR', + // The request specified an unsupported type of second factor. + UNSUPPORTED_SECOND_FACTOR: 'UNSUPPORTED_SECOND_FACTOR', // Operation is not supported in a multi-tenant context. UNSUPPORTED_TENANT_OPERATION: 'UNSUPPORTED_TENANT_OPERATION', + // A verified email is required for the specified action. For example a multi-factor user + // requires a verified email. + UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL', // User on which action is to be performed is not found. USER_NOT_FOUND: 'USER_NOT_FOUND', // Password provided is too weak. diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 187471f64f..9796774866 100755 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -165,16 +165,16 @@ describe('admin.auth', () => { // Confirm expected email. expect(userRecord.email).to.equal(newUserData.email); // Confirm second factors added to user. - expect(userRecord.multiFactor.enrolledFactors.length).to.equal(2); + expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(2); // Confirm first enrolled second factor. - const firstMultiFactor = userRecord.multiFactor.enrolledFactors[0]; + const firstMultiFactor = userRecord.multiFactor!.enrolledFactors[0]; expect(firstMultiFactor.uid).not.to.be.undefined; expect(firstMultiFactor.enrollmentTime).not.to.be.undefined; expect(firstMultiFactor.phoneNumber).to.equal(enrolledFactors[0].phoneNumber); expect(firstMultiFactor.displayName).to.equal(enrolledFactors[0].displayName); expect(firstMultiFactor.factorId).to.equal(enrolledFactors[0].factorId); // Confirm second enrolled second factor. - const secondMultiFactor = userRecord.multiFactor.enrolledFactors[1]; + const secondMultiFactor = userRecord.multiFactor!.enrolledFactors[1]; expect(secondMultiFactor.uid).not.to.be.undefined; expect(secondMultiFactor.enrollmentTime).not.to.be.undefined; expect(secondMultiFactor.phoneNumber).to.equal(enrolledFactors[1].phoneNumber); @@ -413,7 +413,7 @@ describe('admin.auth', () => { }); }) .then((userRecord) => { - expect(userRecord.multiFactor.enrolledFactors.length).to.equal(1); + expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(1); const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); expect(actualUserRecord.multiFactor.enrolledFactors[0]).to.deep.equal(enrolledFactors[0]); // Remove all second factors. diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 4e492fa4db..85c7408236 100755 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -1530,7 +1530,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { index: 22, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(testUsers[22].multiFactor.enrolledFactors[0])}" provided.`, ), }, @@ -2032,7 +2032,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor type', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(unsupportedSecondFactor)}" provided.`), secondFactor: unsupportedSecondFactor, }, @@ -2512,7 +2512,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor type', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(unsupportedSecondFactor)}" provided.`), secondFactor: unsupportedSecondFactor, }, diff --git a/test/unit/auth/user-import-builder.spec.ts b/test/unit/auth/user-import-builder.spec.ts index 828bbadb33..453404b32a 100755 --- a/test/unit/auth/user-import-builder.spec.ts +++ b/test/unit/auth/user-import-builder.spec.ts @@ -854,7 +854,7 @@ describe('UserImportBuilder', () => { { index: 9, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(testUsers[9].multiFactor!.enrolledFactors[0])}" provided.`), }, ], From 0372ff36d3084f92dd88bb66f458eb41ce433050 Mon Sep 17 00:00:00 2001 From: bojeil-google Date: Tue, 7 Jan 2020 11:39:16 -0800 Subject: [PATCH 2/2] Trigger CI