123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.parseBlockingFunctionJwt = exports.setAccountInfoImpl = exports.resetPassword = exports.SESSION_COOKIE_MAX_VALID_DURATION = exports.CUSTOM_TOKEN_AUDIENCE = exports.authOperations = void 0;
- const url_1 = require("url");
- const jsonwebtoken_1 = require("jsonwebtoken");
- const node_fetch_1 = require("node-fetch");
- const abort_controller_1 = require("abort-controller");
- const utils_1 = require("./utils");
- const errors_1 = require("./errors");
- const types_1 = require("../types");
- const emulatorLogger_1 = require("../emulatorLogger");
- const state_1 = require("./state");
- exports.authOperations = {
- identitytoolkit: {
- getProjects,
- getRecaptchaParams,
- accounts: {
- createAuthUri,
- delete: deleteAccount,
- lookup,
- resetPassword,
- sendOobCode,
- sendVerificationCode,
- signInWithCustomToken,
- signInWithEmailLink,
- signInWithIdp,
- signInWithPassword,
- signInWithPhoneNumber,
- signUp,
- update: setAccountInfo,
- mfaEnrollment: {
- finalize: mfaEnrollmentFinalize,
- start: mfaEnrollmentStart,
- withdraw: mfaEnrollmentWithdraw,
- },
- mfaSignIn: {
- start: mfaSignInStart,
- finalize: mfaSignInFinalize,
- },
- },
- projects: {
- createSessionCookie,
- queryAccounts,
- getConfig,
- updateConfig,
- accounts: {
- _: signUp,
- delete: deleteAccount,
- lookup,
- query: queryAccounts,
- sendOobCode,
- update: setAccountInfo,
- batchCreate,
- batchDelete,
- batchGet,
- },
- tenants: {
- create: createTenant,
- delete: deleteTenant,
- get: getTenant,
- list: listTenants,
- patch: updateTenant,
- createSessionCookie,
- accounts: {
- _: signUp,
- batchCreate,
- batchDelete,
- batchGet,
- delete: deleteAccount,
- lookup,
- query: queryAccounts,
- sendOobCode,
- update: setAccountInfo,
- },
- },
- },
- },
- securetoken: {
- token: grantToken,
- },
- emulator: {
- projects: {
- accounts: {
- delete: deleteAllAccountsInProject,
- },
- config: {
- get: getEmulatorProjectConfig,
- update: updateEmulatorProjectConfig,
- },
- oobCodes: {
- list: listOobCodesInProject,
- },
- verificationCodes: {
- list: listVerificationCodesInProject,
- },
- },
- },
- };
- const PASSWORD_MIN_LENGTH = 6;
- exports.CUSTOM_TOKEN_AUDIENCE = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
- const MFA_INELIGIBLE_PROVIDER = new Set([
- state_1.PROVIDER_ANONYMOUS,
- state_1.PROVIDER_PHONE,
- state_1.PROVIDER_CUSTOM,
- state_1.PROVIDER_GAME_CENTER,
- ]);
- async function signUp(state, reqBody, ctx) {
- var _a, _b, _c, _d;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- let provider;
- const timestamp = new Date();
- let updates = {
- lastLoginAt: timestamp.getTime().toString(),
- };
- if ((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2) {
- if (reqBody.idToken) {
- (0, errors_1.assert)(!reqBody.localId, "UNEXPECTED_PARAMETER : User ID");
- }
- if (reqBody.localId) {
- (0, errors_1.assert)(!state.getUserByLocalId(reqBody.localId), "DUPLICATE_LOCAL_ID");
- }
- updates.displayName = reqBody.displayName;
- updates.photoUrl = reqBody.photoUrl;
- updates.emailVerified = reqBody.emailVerified || false;
- if (reqBody.phoneNumber) {
- (0, errors_1.assert)((0, utils_1.isValidPhoneNumber)(reqBody.phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
- (0, errors_1.assert)(!state.getUserByPhoneNumber(reqBody.phoneNumber), "PHONE_NUMBER_EXISTS");
- updates.phoneNumber = reqBody.phoneNumber;
- }
- if (reqBody.disabled) {
- updates.disabled = true;
- }
- }
- else {
- (0, errors_1.assert)(!reqBody.localId, "UNEXPECTED_PARAMETER : User ID");
- if (reqBody.idToken || reqBody.password || reqBody.email) {
- updates.displayName = reqBody.displayName;
- updates.emailVerified = false;
- (0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
- (0, errors_1.assert)(reqBody.password, "MISSING_PASSWORD");
- provider = state_1.PROVIDER_PASSWORD;
- (0, errors_1.assert)(state.allowPasswordSignup, "OPERATION_NOT_ALLOWED");
- }
- else {
- provider = state_1.PROVIDER_ANONYMOUS;
- (0, errors_1.assert)(state.enableAnonymousUser, "ADMIN_ONLY_OPERATION");
- }
- }
- if (reqBody.email) {
- (0, errors_1.assert)((0, utils_1.isValidEmailAddress)(reqBody.email), "INVALID_EMAIL");
- const email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- (0, errors_1.assert)(!state.getUserByEmail(email), "EMAIL_EXISTS");
- updates.email = email;
- }
- if (reqBody.password) {
- (0, errors_1.assert)(reqBody.password.length >= PASSWORD_MIN_LENGTH, `WEAK_PASSWORD : Password should be at least ${PASSWORD_MIN_LENGTH} characters`);
- updates.salt = "fakeSalt" + (0, utils_1.randomId)(20);
- updates.passwordHash = hashPassword(reqBody.password, updates.salt);
- updates.passwordUpdatedAt = Date.now();
- updates.validSince = (0, utils_1.toUnixTimestamp)(new Date()).toString();
- }
- if (reqBody.mfaInfo) {
- updates.mfaInfo = getMfaEnrollmentsFromRequest(state, reqBody.mfaInfo, {
- generateEnrollmentIds: true,
- });
- }
- if (state instanceof state_1.TenantProjectState) {
- updates.tenantId = state.tenantId;
- }
- let user;
- if (reqBody.idToken) {
- ({ user } = parseIdToken(state, reqBody.idToken));
- }
- let extraClaims;
- if (!user) {
- updates.createdAt = timestamp.getTime().toString();
- const localId = (_b = reqBody.localId) !== null && _b !== void 0 ? _b : state.generateLocalId();
- if (reqBody.email && !((_c = ctx.security) === null || _c === void 0 ? void 0 : _c.Oauth2)) {
- const userBeforeCreate = Object.assign({ localId }, updates);
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, { signInMethod: "password" });
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- }
- user = state.createUserWithLocalId(localId, updates);
- (0, errors_1.assert)(user, "DUPLICATE_LOCAL_ID");
- if (reqBody.email && !((_d = ctx.security) === null || _d === void 0 ? void 0 : _d.Oauth2)) {
- if (!user.disabled) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "password" });
- updates = blockingResponse.updates;
- extraClaims = blockingResponse.extraClaims;
- user = state.updateUserByLocalId(user.localId, updates);
- }
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- }
- }
- else {
- user = state.updateUserByLocalId(user.localId, updates);
- }
- return Object.assign({ kind: "identitytoolkit#SignupNewUserResponse", localId: user.localId, displayName: user.displayName, email: user.email }, (provider ? issueTokens(state, user, provider, { extraClaims }) : {}));
- }
- function lookup(state, reqBody, ctx) {
- var _a, _b, _c, _d, _e;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- const seenLocalIds = new Set();
- const users = [];
- function tryAddUser(maybeUser) {
- if (maybeUser && !seenLocalIds.has(maybeUser.localId)) {
- users.push(maybeUser);
- seenLocalIds.add(maybeUser.localId);
- }
- }
- if ((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2) {
- if (reqBody.initialEmail) {
- throw new errors_1.NotImplementedError("Lookup by initialEmail is not implemented.");
- }
- for (const localId of (_b = reqBody.localId) !== null && _b !== void 0 ? _b : []) {
- tryAddUser(state.getUserByLocalId(localId));
- }
- for (const email of (_c = reqBody.email) !== null && _c !== void 0 ? _c : []) {
- tryAddUser(state.getUserByEmail(email));
- }
- for (const phoneNumber of (_d = reqBody.phoneNumber) !== null && _d !== void 0 ? _d : []) {
- tryAddUser(state.getUserByPhoneNumber(phoneNumber));
- }
- for (const { providerId, rawId } of (_e = reqBody.federatedUserId) !== null && _e !== void 0 ? _e : []) {
- if (!providerId || !rawId) {
- continue;
- }
- tryAddUser(state.getUserByProviderRawId(providerId, rawId));
- }
- }
- else {
- (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
- const { user } = parseIdToken(state, reqBody.idToken);
- users.push(redactPasswordHash(user));
- }
- return {
- kind: "identitytoolkit#GetAccountInfoResponse",
- users: users.length ? users : undefined,
- };
- }
- function batchCreate(state, reqBody) {
- var _a, _b;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)((_a = reqBody.users) === null || _a === void 0 ? void 0 : _a.length, "MISSING_USER_ACCOUNT");
- if (reqBody.sanityCheck) {
- if (state.oneAccountPerEmail) {
- const existingEmails = new Set();
- for (const userInfo of reqBody.users) {
- if (userInfo.email) {
- (0, errors_1.assert)(!existingEmails.has(userInfo.email), `DUPLICATE_EMAIL : ${userInfo.email}`);
- existingEmails.add(userInfo.email);
- }
- }
- }
- const existingProviderAccounts = new Set();
- for (const userInfo of reqBody.users) {
- for (const { providerId, rawId } of (_b = userInfo.providerUserInfo) !== null && _b !== void 0 ? _b : []) {
- const key = `${providerId}:${rawId}`;
- (0, errors_1.assert)(!existingProviderAccounts.has(key), `DUPLICATE_RAW_ID : Provider id(${providerId}), Raw id(${rawId})`);
- existingProviderAccounts.add(key);
- }
- }
- }
- if (!reqBody.allowOverwrite) {
- const existingLocalIds = new Set();
- for (const userInfo of reqBody.users) {
- const localId = userInfo.localId || "";
- (0, errors_1.assert)(!existingLocalIds.has(localId), `DUPLICATE_LOCAL_ID : ${localId}`);
- existingLocalIds.add(localId);
- }
- }
- const errors = [];
- for (let index = 0; index < reqBody.users.length; index++) {
- const userInfo = reqBody.users[index];
- try {
- (0, errors_1.assert)(userInfo.localId, "localId is missing");
- const uploadTime = new Date();
- const fields = {
- displayName: userInfo.displayName,
- photoUrl: userInfo.photoUrl,
- lastLoginAt: userInfo.lastLoginAt,
- };
- if (userInfo.tenantId) {
- (0, errors_1.assert)(state instanceof state_1.TenantProjectState && state.tenantId === userInfo.tenantId, "Tenant id in userInfo does not match the tenant id in request.");
- }
- if (state instanceof state_1.TenantProjectState) {
- fields.tenantId = state.tenantId;
- }
- if (userInfo.passwordHash) {
- fields.passwordHash = userInfo.passwordHash;
- fields.salt = userInfo.salt;
- fields.passwordUpdatedAt = uploadTime.getTime();
- }
- else if (userInfo.rawPassword) {
- fields.salt = userInfo.salt || "fakeSalt" + (0, utils_1.randomId)(20);
- fields.passwordHash = hashPassword(userInfo.rawPassword, fields.salt);
- fields.passwordUpdatedAt = uploadTime.getTime();
- }
- if (userInfo.customAttributes) {
- validateSerializedCustomClaims(userInfo.customAttributes);
- fields.customAttributes = userInfo.customAttributes;
- }
- if (userInfo.providerUserInfo) {
- fields.providerUserInfo = [];
- for (const providerUserInfo of userInfo.providerUserInfo) {
- const { providerId, rawId, federatedId } = providerUserInfo;
- if (providerId === state_1.PROVIDER_PASSWORD || providerId === state_1.PROVIDER_PHONE) {
- continue;
- }
- if (!rawId || !providerId) {
- if (!federatedId) {
- (0, errors_1.assert)(false, "federatedId or (providerId & rawId) is required");
- }
- else {
- (0, errors_1.assert)(false, "((Parsing federatedId is not implemented in Auth Emulator; please specify providerId AND rawId as a workaround.))");
- }
- }
- const existingUserWithRawId = state.getUserByProviderRawId(providerId, rawId);
- (0, errors_1.assert)(!existingUserWithRawId || existingUserWithRawId.localId === userInfo.localId, "raw id exists in other account in database");
- fields.providerUserInfo.push(Object.assign(Object.assign({}, providerUserInfo), { providerId, rawId }));
- }
- }
- if (userInfo.phoneNumber) {
- (0, errors_1.assert)((0, utils_1.isValidPhoneNumber)(userInfo.phoneNumber), "phone number format is invalid");
- fields.phoneNumber = userInfo.phoneNumber;
- }
- fields.validSince = (0, utils_1.toUnixTimestamp)(uploadTime).toString();
- fields.createdAt = uploadTime.getTime().toString();
- if (fields.createdAt && !isNaN(Number(userInfo.createdAt))) {
- fields.createdAt = userInfo.createdAt;
- }
- if (userInfo.email) {
- const email = userInfo.email;
- (0, errors_1.assert)((0, utils_1.isValidEmailAddress)(email), "email is invalid");
- const existingUserWithEmail = state.getUserByEmail(email);
- (0, errors_1.assert)(!existingUserWithEmail || existingUserWithEmail.localId === userInfo.localId, reqBody.sanityCheck && state.oneAccountPerEmail
- ? "email exists in other account in database"
- : `((Auth Emulator does not support importing duplicate email: ${email}))`);
- fields.email = (0, utils_1.canonicalizeEmailAddress)(email);
- }
- fields.emailVerified = !!userInfo.emailVerified;
- fields.disabled = !!userInfo.disabled;
- if (userInfo.mfaInfo && userInfo.mfaInfo.length > 0) {
- fields.mfaInfo = [];
- (0, errors_1.assert)(fields.email, "Second factor account requires email to be presented.");
- (0, errors_1.assert)(fields.emailVerified, "Second factor account requires email to be verified.");
- const existingIds = new Set();
- for (const enrollment of userInfo.mfaInfo) {
- if (enrollment.mfaEnrollmentId) {
- (0, errors_1.assert)(!existingIds.has(enrollment.mfaEnrollmentId), "Enrollment id already exists.");
- existingIds.add(enrollment.mfaEnrollmentId);
- }
- }
- for (const enrollment of userInfo.mfaInfo) {
- enrollment.mfaEnrollmentId = enrollment.mfaEnrollmentId || newRandomId(28, existingIds);
- enrollment.enrolledAt = enrollment.enrolledAt || new Date().toISOString();
- (0, errors_1.assert)(enrollment.phoneInfo, "Second factor not supported.");
- (0, errors_1.assert)((0, utils_1.isValidPhoneNumber)(enrollment.phoneInfo), "Phone number format is invalid");
- enrollment.unobfuscatedPhoneInfo = enrollment.phoneInfo;
- fields.mfaInfo.push(enrollment);
- }
- }
- if (state.getUserByLocalId(userInfo.localId)) {
- (0, errors_1.assert)(reqBody.allowOverwrite, "localId belongs to an existing account - can not overwrite.");
- }
- state.overwriteUserWithLocalId(userInfo.localId, fields);
- }
- catch (e) {
- if (e instanceof errors_1.BadRequestError) {
- let message = e.message;
- if (message === "INVALID_CLAIMS") {
- message = "Invalid custom claims provided.";
- }
- else if (message === "CLAIMS_TOO_LARGE") {
- message = "Custom claims provided are too large.";
- }
- else if (message.startsWith("FORBIDDEN_CLAIM")) {
- message = "Custom claims provided include a reserved claim.";
- }
- errors.push({
- index,
- message,
- });
- }
- else {
- throw e;
- }
- }
- }
- return {
- kind: "identitytoolkit#UploadAccountResponse",
- error: errors,
- };
- }
- function batchDelete(state, reqBody) {
- var _a;
- const errors = [];
- const localIds = (_a = reqBody.localIds) !== null && _a !== void 0 ? _a : [];
- (0, errors_1.assert)(localIds.length > 0 && localIds.length <= 1000, "LOCAL_ID_LIST_EXCEEDS_LIMIT");
- for (let index = 0; index < localIds.length; index++) {
- const localId = localIds[index];
- const user = state.getUserByLocalId(localId);
- if (!user) {
- continue;
- }
- else if (!user.disabled && !reqBody.force) {
- errors.push({
- index,
- localId,
- message: "NOT_DISABLED : Disable the account before batch deletion.",
- });
- }
- else {
- state.deleteUser(user);
- }
- }
- return { errors: errors.length ? errors : undefined };
- }
- function batchGet(state, reqBody, ctx) {
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- const maxResults = Math.min(Math.floor(ctx.params.query.maxResults) || 20, 1000);
- const users = state.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
- let newPageToken = undefined;
- if (maxResults >= 0 && users.length >= maxResults) {
- users.length = maxResults;
- if (users.length) {
- newPageToken = users[users.length - 1].localId;
- }
- }
- return {
- kind: "identitytoolkit#DownloadAccountResponse",
- users,
- nextPageToken: newPageToken,
- };
- }
- function createAuthUri(state, reqBody) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- const sessionId = reqBody.sessionId || (0, utils_1.randomId)(27);
- if (reqBody.providerId) {
- throw new errors_1.NotImplementedError("Sign-in with IDP is not yet supported.");
- }
- (0, errors_1.assert)(reqBody.identifier, "MISSING_IDENTIFIER");
- (0, errors_1.assert)(reqBody.continueUri, "MISSING_CONTINUE_URI");
- (0, errors_1.assert)((0, utils_1.isValidEmailAddress)(reqBody.identifier), "INVALID_IDENTIFIER");
- const email = (0, utils_1.canonicalizeEmailAddress)(reqBody.identifier);
- (0, errors_1.assert)((0, utils_1.parseAbsoluteUri)(reqBody.continueUri), "INVALID_CONTINUE_URI");
- const allProviders = [];
- const signinMethods = [];
- let registered = false;
- const users = state.getUsersByEmailOrProviderEmail(email);
- if (state.oneAccountPerEmail) {
- if (users.length) {
- registered = true;
- (_a = users[0].providerUserInfo) === null || _a === void 0 ? void 0 : _a.forEach(({ providerId }) => {
- if (providerId === state_1.PROVIDER_PASSWORD) {
- allProviders.push(providerId);
- if (users[0].passwordHash) {
- signinMethods.push(state_1.PROVIDER_PASSWORD);
- }
- if (users[0].emailLinkSignin) {
- signinMethods.push(state_1.SIGNIN_METHOD_EMAIL_LINK);
- }
- }
- else if (providerId !== state_1.PROVIDER_PHONE) {
- allProviders.push(providerId);
- signinMethods.push(providerId);
- }
- });
- }
- }
- else {
- const user = users.find((u) => u.email);
- if (user) {
- registered = true;
- if (user.passwordHash || user.emailLinkSignin) {
- allProviders.push(state_1.PROVIDER_PASSWORD);
- if (users[0].passwordHash) {
- signinMethods.push(state_1.PROVIDER_PASSWORD);
- }
- if (users[0].emailLinkSignin) {
- signinMethods.push(state_1.SIGNIN_METHOD_EMAIL_LINK);
- }
- }
- }
- }
- return {
- kind: "identitytoolkit#CreateAuthUriResponse",
- registered,
- allProviders,
- sessionId,
- signinMethods,
- };
- }
- const SESSION_COOKIE_MIN_VALID_DURATION = 5 * 60;
- exports.SESSION_COOKIE_MAX_VALID_DURATION = 14 * 24 * 60 * 60;
- function createSessionCookie(state, reqBody) {
- (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
- const validDuration = Number(reqBody.validDuration) || exports.SESSION_COOKIE_MAX_VALID_DURATION;
- (0, errors_1.assert)(validDuration >= SESSION_COOKIE_MIN_VALID_DURATION &&
- validDuration <= exports.SESSION_COOKIE_MAX_VALID_DURATION, "INVALID_DURATION");
- const { payload } = parseIdToken(state, reqBody.idToken);
- const issuedAt = (0, utils_1.toUnixTimestamp)(new Date());
- const expiresAt = issuedAt + validDuration;
- const sessionCookie = (0, jsonwebtoken_1.sign)(Object.assign(Object.assign({}, payload), { iat: issuedAt, exp: expiresAt, iss: `https://session.firebase.google.com/${payload.aud}` }), "", {
- algorithm: "none",
- });
- return { sessionCookie };
- }
- function deleteAccount(state, reqBody, ctx) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- let user;
- if ((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2) {
- (0, errors_1.assert)(reqBody.localId, "MISSING_LOCAL_ID");
- const maybeUser = state.getUserByLocalId(reqBody.localId);
- (0, errors_1.assert)(maybeUser, "USER_NOT_FOUND");
- user = maybeUser;
- }
- else {
- (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
- user = parseIdToken(state, reqBody.idToken).user;
- }
- state.deleteUser(user);
- return {
- kind: "identitytoolkit#DeleteAccountResponse",
- };
- }
- function getProjects(state) {
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
- return {
- projectId: state.projectNumber,
- authorizedDomains: [
- "localhost",
- ],
- };
- }
- function getRecaptchaParams(state) {
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- return {
- kind: "identitytoolkit#GetRecaptchaParamResponse",
- recaptchaStoken: "This-is-a-fake-token__Dont-send-this-to-the-Recaptcha-service__The-Auth-Emulator-does-not-support-Recaptcha",
- recaptchaSiteKey: "Fake-key__Do-not-send-this-to-Recaptcha_",
- };
- }
- function queryAccounts(state, reqBody) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- if ((_a = reqBody.expression) === null || _a === void 0 ? void 0 : _a.length) {
- throw new errors_1.NotImplementedError("expression is not implemented.");
- }
- if (reqBody.returnUserInfo === false) {
- return {
- recordsCount: state.getUserCount().toString(),
- };
- }
- if (reqBody.limit) {
- throw new errors_1.NotImplementedError("limit is not implemented.");
- }
- reqBody.offset = reqBody.offset || "0";
- if (reqBody.offset !== "0") {
- throw new errors_1.NotImplementedError("offset is not implemented.");
- }
- if (!reqBody.order || reqBody.order === "ORDER_UNSPECIFIED") {
- reqBody.order = "ASC";
- }
- if (!reqBody.sortBy || reqBody.sortBy === "SORT_BY_FIELD_UNSPECIFIED") {
- reqBody.sortBy = "USER_ID";
- }
- let sortByField;
- if (reqBody.sortBy === "USER_ID") {
- sortByField = "localId";
- }
- else {
- throw new errors_1.NotImplementedError("Only sorting by USER_ID is implemented.");
- }
- const users = state.queryUsers({}, { order: reqBody.order, sortByField });
- return {
- recordsCount: users.length.toString(),
- userInfo: users,
- };
- }
- function resetPassword(state, reqBody) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
- (0, errors_1.assert)(reqBody.oobCode, "MISSING_OOB_CODE");
- const oob = state.validateOobCode(reqBody.oobCode);
- (0, errors_1.assert)(oob, "INVALID_OOB_CODE");
- if (reqBody.newPassword) {
- (0, errors_1.assert)(oob.requestType === "PASSWORD_RESET", "INVALID_OOB_CODE");
- (0, errors_1.assert)(reqBody.newPassword.length >= PASSWORD_MIN_LENGTH, `WEAK_PASSWORD : Password should be at least ${PASSWORD_MIN_LENGTH} characters`);
- state.deleteOobCode(reqBody.oobCode);
- let user = state.getUserByEmail(oob.email);
- (0, errors_1.assert)(user, "INVALID_OOB_CODE");
- const salt = "fakeSalt" + (0, utils_1.randomId)(20);
- const passwordHash = hashPassword(reqBody.newPassword, salt);
- user = state.updateUserByLocalId(user.localId, {
- emailVerified: true,
- passwordHash,
- salt,
- passwordUpdatedAt: Date.now(),
- validSince: (0, utils_1.toUnixTimestamp)(new Date()).toString(),
- }, { deleteProviders: (_a = user.providerUserInfo) === null || _a === void 0 ? void 0 : _a.map((info) => info.providerId) });
- }
- return {
- kind: "identitytoolkit#ResetPasswordResponse",
- requestType: oob.requestType,
- email: oob.requestType === "EMAIL_SIGNIN" ? undefined : oob.email,
- };
- }
- exports.resetPassword = resetPassword;
- function sendOobCode(state, reqBody, ctx) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(reqBody.requestType && reqBody.requestType !== "OOB_REQ_TYPE_UNSPECIFIED", "MISSING_REQ_TYPE");
- if (reqBody.returnOobLink) {
- (0, errors_1.assert)((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2, "INSUFFICIENT_PERMISSION");
- }
- if (reqBody.continueUrl) {
- (0, errors_1.assert)((0, utils_1.parseAbsoluteUri)(reqBody.continueUrl), "INVALID_CONTINUE_URI : ((expected an absolute URI with valid scheme and host))");
- }
- let email;
- let mode;
- switch (reqBody.requestType) {
- case "EMAIL_SIGNIN":
- (0, errors_1.assert)(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
- mode = "signIn";
- (0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
- email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- break;
- case "PASSWORD_RESET":
- mode = "resetPassword";
- (0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
- email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- (0, errors_1.assert)(state.getUserByEmail(email), "EMAIL_NOT_FOUND");
- break;
- case "VERIFY_EMAIL":
- mode = "verifyEmail";
- if (reqBody.returnOobLink && !reqBody.idToken) {
- (0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
- email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- const maybeUser = state.getUserByEmail(email);
- (0, errors_1.assert)(maybeUser, "USER_NOT_FOUND");
- }
- else {
- const user = parseIdToken(state, reqBody.idToken || "").user;
- (0, errors_1.assert)(user.email, "MISSING_EMAIL");
- email = user.email;
- }
- break;
- default:
- throw new errors_1.NotImplementedError(reqBody.requestType);
- }
- if (reqBody.canHandleCodeInApp) {
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", "canHandleCodeInApp is unsupported in Auth Emulator. All OOB operations will complete via web.");
- }
- const url = (0, utils_1.authEmulatorUrl)(ctx.req);
- const oobRecord = createOobRecord(state, email, url, {
- requestType: reqBody.requestType,
- mode,
- continueUrl: reqBody.continueUrl,
- });
- if (reqBody.returnOobLink) {
- return {
- kind: "identitytoolkit#GetOobConfirmationCodeResponse",
- email,
- oobCode: oobRecord.oobCode,
- oobLink: oobRecord.oobLink,
- };
- }
- else {
- logOobMessage(oobRecord);
- return {
- kind: "identitytoolkit#GetOobConfirmationCodeResponse",
- email,
- };
- }
- }
- function sendVerificationCode(state, reqBody) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
- (0, errors_1.assert)(reqBody.phoneNumber && (0, utils_1.isValidPhoneNumber)(reqBody.phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
- const user = state.getUserByPhoneNumber(reqBody.phoneNumber);
- (0, errors_1.assert)(!((_a = user === null || user === void 0 ? void 0 : user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length), "UNSUPPORTED_FIRST_FACTOR : A phone number cannot be set as a first factor on an SMS based MFA user.");
- const { sessionInfo, phoneNumber, code } = state.createVerificationCode(reqBody.phoneNumber);
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("BULLET", `To verify the phone number ${phoneNumber}, use the code ${code}.`);
- return {
- sessionInfo,
- };
- }
- function setAccountInfo(state, reqBody, ctx) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- const url = (0, utils_1.authEmulatorUrl)(ctx.req);
- return setAccountInfoImpl(state, reqBody, {
- privileged: !!((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2),
- emulatorUrl: url,
- });
- }
- function setAccountInfoImpl(state, reqBody, { privileged = false, emulatorUrl = undefined } = {}) {
- var _a, _b;
- const unimplementedFields = [
- "provider",
- "upgradeToFederatedLogin",
- "linkProviderUserInfo",
- ];
- for (const field of unimplementedFields) {
- if (field in reqBody) {
- throw new errors_1.NotImplementedError(`${field} is not implemented yet.`);
- }
- }
- if (!privileged) {
- (0, errors_1.assert)(reqBody.idToken || reqBody.oobCode, "INVALID_REQ_TYPE : Unsupported request parameters.");
- (0, errors_1.assert)(reqBody.customAttributes == null, "INSUFFICIENT_PERMISSION");
- }
- else {
- (0, errors_1.assert)(reqBody.localId, "MISSING_LOCAL_ID");
- }
- if (reqBody.customAttributes) {
- validateSerializedCustomClaims(reqBody.customAttributes);
- }
- reqBody.deleteAttribute = reqBody.deleteAttribute || [];
- for (const attr of reqBody.deleteAttribute) {
- if (attr === "PROVIDER" || attr === "RAW_USER_INFO") {
- throw new errors_1.NotImplementedError(`deleteAttribute: ${attr}`);
- }
- }
- const updates = {};
- let user;
- let signInProvider;
- let isEmailUpdate = false;
- if (reqBody.oobCode) {
- const oob = state.validateOobCode(reqBody.oobCode);
- (0, errors_1.assert)(oob, "INVALID_OOB_CODE");
- switch (oob.requestType) {
- case "VERIFY_EMAIL": {
- state.deleteOobCode(reqBody.oobCode);
- signInProvider = state_1.PROVIDER_PASSWORD;
- const maybeUser = state.getUserByEmail(oob.email);
- (0, errors_1.assert)(maybeUser, "INVALID_OOB_CODE");
- user = maybeUser;
- updates.emailVerified = true;
- if (oob.email !== user.email) {
- updates.email = oob.email;
- }
- break;
- }
- case "RECOVER_EMAIL": {
- state.deleteOobCode(reqBody.oobCode);
- const maybeUser = state.getUserByInitialEmail(oob.email);
- (0, errors_1.assert)(maybeUser, "INVALID_OOB_CODE");
- (0, errors_1.assert)(!state.getUserByEmail(oob.email), "EMAIL_EXISTS");
- user = maybeUser;
- if (oob.email !== user.email) {
- updates.email = oob.email;
- updates.emailVerified = true;
- }
- break;
- }
- default:
- throw new errors_1.NotImplementedError(oob.requestType);
- }
- }
- else {
- if (reqBody.idToken) {
- ({ user, signInProvider } = parseIdToken(state, reqBody.idToken));
- (0, errors_1.assert)(reqBody.disableUser == null, "OPERATION_NOT_ALLOWED");
- }
- else {
- (0, errors_1.assert)(reqBody.localId, "MISSING_LOCAL_ID");
- const maybeUser = state.getUserByLocalId(reqBody.localId);
- (0, errors_1.assert)(maybeUser, "USER_NOT_FOUND");
- user = maybeUser;
- }
- if (reqBody.email) {
- (0, errors_1.assert)((0, utils_1.isValidEmailAddress)(reqBody.email), "INVALID_EMAIL");
- const newEmail = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- if (newEmail !== user.email) {
- (0, errors_1.assert)(!state.getUserByEmail(newEmail), "EMAIL_EXISTS");
- updates.email = newEmail;
- updates.emailVerified = false;
- isEmailUpdate = true;
- if (signInProvider !== state_1.PROVIDER_ANONYMOUS && user.email && !user.initialEmail) {
- updates.initialEmail = user.email;
- }
- }
- }
- if (reqBody.password) {
- (0, errors_1.assert)(reqBody.password.length >= PASSWORD_MIN_LENGTH, `WEAK_PASSWORD : Password should be at least ${PASSWORD_MIN_LENGTH} characters`);
- updates.salt = "fakeSalt" + (0, utils_1.randomId)(20);
- updates.passwordHash = hashPassword(reqBody.password, updates.salt);
- updates.passwordUpdatedAt = Date.now();
- signInProvider = state_1.PROVIDER_PASSWORD;
- }
- if (reqBody.password || reqBody.validSince || updates.email) {
- updates.validSince = (0, utils_1.toUnixTimestamp)(new Date()).toString();
- }
- if (reqBody.mfa) {
- if (reqBody.mfa.enrollments && reqBody.mfa.enrollments.length > 0) {
- updates.mfaInfo = getMfaEnrollmentsFromRequest(state, reqBody.mfa.enrollments);
- }
- else {
- updates.mfaInfo = undefined;
- }
- }
- const fieldsToCopy = [
- "displayName",
- "photoUrl",
- ];
- if (privileged) {
- if (reqBody.disableUser != null) {
- updates.disabled = reqBody.disableUser;
- }
- if (reqBody.phoneNumber && reqBody.phoneNumber !== user.phoneNumber) {
- (0, errors_1.assert)((0, utils_1.isValidPhoneNumber)(reqBody.phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
- (0, errors_1.assert)(!state.getUserByPhoneNumber(reqBody.phoneNumber), "PHONE_NUMBER_EXISTS");
- updates.phoneNumber = reqBody.phoneNumber;
- }
- fieldsToCopy.push("emailVerified", "customAttributes", "createdAt", "lastLoginAt", "validSince");
- }
- for (const field of fieldsToCopy) {
- if (reqBody[field] != null) {
- (0, utils_1.mirrorFieldTo)(updates, field, reqBody);
- }
- }
- for (const attr of reqBody.deleteAttribute) {
- switch (attr) {
- case "USER_ATTRIBUTE_NAME_UNSPECIFIED":
- continue;
- case "DISPLAY_NAME":
- updates.displayName = undefined;
- break;
- case "PHOTO_URL":
- updates.photoUrl = undefined;
- break;
- case "PASSWORD":
- updates.passwordHash = undefined;
- updates.salt = undefined;
- break;
- case "EMAIL":
- updates.email = undefined;
- updates.emailVerified = undefined;
- updates.emailLinkSignin = undefined;
- break;
- }
- }
- if ((_a = reqBody.deleteProvider) === null || _a === void 0 ? void 0 : _a.includes(state_1.PROVIDER_PASSWORD)) {
- updates.email = undefined;
- updates.emailVerified = undefined;
- updates.emailLinkSignin = undefined;
- updates.passwordHash = undefined;
- updates.salt = undefined;
- }
- if ((_b = reqBody.deleteProvider) === null || _b === void 0 ? void 0 : _b.includes(state_1.PROVIDER_PHONE)) {
- updates.phoneNumber = undefined;
- }
- }
- user = state.updateUserByLocalId(user.localId, updates, {
- deleteProviders: reqBody.deleteProvider,
- });
- if (signInProvider !== state_1.PROVIDER_ANONYMOUS && user.initialEmail && isEmailUpdate) {
- if (!emulatorUrl) {
- throw new Error("Internal assertion error: missing emulatorUrl param");
- }
- sendOobForEmailReset(state, user.initialEmail, emulatorUrl);
- }
- return redactPasswordHash(Object.assign({ kind: "identitytoolkit#SetAccountInfoResponse", localId: user.localId, emailVerified: user.emailVerified, providerUserInfo: user.providerUserInfo, email: user.email, displayName: user.displayName, photoUrl: user.photoUrl, passwordHash: user.passwordHash }, (updates.validSince && signInProvider ? issueTokens(state, user, signInProvider) : {})));
- }
- exports.setAccountInfoImpl = setAccountInfoImpl;
- function sendOobForEmailReset(state, initialEmail, url) {
- const oobRecord = createOobRecord(state, initialEmail, url, {
- requestType: "RECOVER_EMAIL",
- mode: "recoverEmail",
- });
- logOobMessage(oobRecord);
- }
- function createOobRecord(state, email, url, params) {
- const oobRecord = state.createOob(email, params.requestType, (oobCode) => {
- url.pathname = "/emulator/action";
- url.searchParams.set("mode", params.mode);
- url.searchParams.set("lang", "en");
- url.searchParams.set("oobCode", oobCode);
- url.searchParams.set("apiKey", "fake-api-key");
- if (params.continueUrl) {
- url.searchParams.set("continueUrl", params.continueUrl);
- }
- if (state instanceof state_1.TenantProjectState) {
- url.searchParams.set("tenantId", state.tenantId);
- }
- return url.toString();
- });
- return oobRecord;
- }
- function logOobMessage(oobRecord) {
- const oobLink = oobRecord.oobLink;
- const email = oobRecord.email;
- let maybeMessage;
- switch (oobRecord.requestType) {
- case "EMAIL_SIGNIN":
- maybeMessage = `To sign in as ${email}, follow this link: ${oobLink}`;
- break;
- case "PASSWORD_RESET":
- maybeMessage = `To reset the password for ${email}, follow this link: ${oobLink}&newPassword=NEW_PASSWORD_HERE`;
- break;
- case "VERIFY_EMAIL":
- maybeMessage = `To verify the email address ${email}, follow this link: ${oobLink}`;
- break;
- case "RECOVER_EMAIL":
- maybeMessage = `To reset your email address to ${email}, follow this link: ${oobLink}`;
- break;
- }
- if (maybeMessage) {
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("BULLET", maybeMessage);
- }
- }
- function signInWithCustomToken(state, reqBody) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(reqBody.token, "MISSING_CUSTOM_TOKEN");
- let payload;
- if (reqBody.token.startsWith("{")) {
- try {
- payload = JSON.parse(reqBody.token);
- }
- catch (_b) {
- throw new errors_1.BadRequestError("INVALID_CUSTOM_TOKEN : ((Auth Emulator only accepts strict JSON or JWTs as fake custom tokens.))");
- }
- }
- else {
- const decoded = (0, jsonwebtoken_1.decode)(reqBody.token, { complete: true });
- if (state instanceof state_1.TenantProjectState) {
- (0, errors_1.assert)((decoded === null || decoded === void 0 ? void 0 : decoded.payload.tenant_id) === state.tenantId, "TENANT_ID_MISMATCH");
- }
- (0, errors_1.assert)(decoded, "INVALID_CUSTOM_TOKEN : Invalid assertion format");
- if (decoded.header.alg !== "none") {
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", "Received a signed custom token. Auth Emulator does not validate JWTs and IS NOT SECURE");
- }
- (0, errors_1.assert)(decoded.payload.aud === exports.CUSTOM_TOKEN_AUDIENCE, `INVALID_CUSTOM_TOKEN : ((Invalid aud (audience): ${decoded.payload.aud} ` +
- "Note: Firebase ID Tokens / third-party tokens cannot be used with signInWithCustomToken.))");
- payload = decoded.payload;
- }
- const localId = (_a = coercePrimitiveToString(payload.uid)) !== null && _a !== void 0 ? _a : coercePrimitiveToString(payload.user_id);
- (0, errors_1.assert)(localId, "MISSING_IDENTIFIER");
- let extraClaims = {};
- if ("claims" in payload) {
- validateCustomClaims(payload.claims);
- extraClaims = payload.claims;
- }
- let user = state.getUserByLocalId(localId);
- const isNewUser = !user;
- const timestamp = new Date();
- const updates = {
- customAuth: true,
- lastLoginAt: timestamp.getTime().toString(),
- tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined,
- };
- if (user) {
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- user = state.updateUserByLocalId(localId, updates);
- }
- else {
- updates.createdAt = timestamp.getTime().toString();
- user = state.createUserWithLocalId(localId, updates);
- if (!user) {
- throw new Error(`Internal assertion error: trying to create duplicate localId: ${localId}`);
- }
- }
- return Object.assign({ kind: "identitytoolkit#VerifyCustomTokenResponse", isNewUser }, issueTokens(state, user, state_1.PROVIDER_CUSTOM, { extraClaims }));
- }
- async function signInWithEmailLink(state, reqBody) {
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
- const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
- (0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
- const email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- (0, errors_1.assert)(reqBody.oobCode, "MISSING_OOB_CODE");
- const oob = state.validateOobCode(reqBody.oobCode);
- (0, errors_1.assert)(oob && oob.requestType === "EMAIL_SIGNIN", "INVALID_OOB_CODE");
- (0, errors_1.assert)(email === oob.email, "INVALID_EMAIL : The email provided does not match the sign-in email address.");
- state.deleteOobCode(reqBody.oobCode);
- const userFromEmail = state.getUserByEmail(email);
- let user = userFromIdToken || userFromEmail;
- const isNewUser = !user;
- const timestamp = new Date();
- let updates = {
- email,
- emailVerified: true,
- emailLinkSignin: true,
- };
- if (state instanceof state_1.TenantProjectState) {
- updates.tenantId = state.tenantId;
- }
- let extraClaims;
- if (!user) {
- updates.createdAt = timestamp.getTime().toString();
- const localId = state.generateLocalId();
- const userBeforeCreate = Object.assign({ localId }, updates);
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, { signInMethod: "emailLink" });
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- user = state.createUserWithLocalId(localId, updates);
- if (!user.disabled && !isMfaEnabled(state, user)) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "emailLink" });
- updates = blockingResponse.updates;
- extraClaims = blockingResponse.extraClaims;
- user = state.updateUserByLocalId(user.localId, updates);
- }
- }
- else {
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- if (userFromIdToken && userFromEmail) {
- (0, errors_1.assert)(userFromIdToken.localId === userFromEmail.localId, "EMAIL_EXISTS");
- }
- if (!user.disabled && !isMfaEnabled(state, user)) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, Object.assign(Object.assign({}, user), updates), { signInMethod: "emailLink" });
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- extraClaims = blockingResponse.extraClaims;
- }
- user = state.updateUserByLocalId(user.localId, updates);
- }
- const response = {
- kind: "identitytoolkit#EmailLinkSigninResponse",
- email,
- localId: user.localId,
- isNewUser,
- };
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- if (isMfaEnabled(state, user)) {
- return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
- }
- else {
- user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
- return Object.assign(Object.assign({}, response), issueTokens(state, user, state_1.PROVIDER_PASSWORD, { extraClaims }));
- }
- }
- async function signInWithIdp(state, reqBody) {
- var _a, _b;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- if (reqBody.returnRefreshToken) {
- throw new errors_1.NotImplementedError("returnRefreshToken is not implemented yet.");
- }
- if (reqBody.pendingIdToken) {
- throw new errors_1.NotImplementedError("pendingIdToken is not implemented yet.");
- }
- const normalizedUri = getNormalizedUri(reqBody);
- const providerId = (_a = normalizedUri.searchParams.get("providerId")) === null || _a === void 0 ? void 0 : _a.toLowerCase();
- (0, errors_1.assert)(providerId, `INVALID_CREDENTIAL_OR_PROVIDER_ID : Invalid IdP response/credential: ${normalizedUri.toString()}`);
- const oauthIdToken = normalizedUri.searchParams.get("id_token") || undefined;
- const oauthAccessToken = normalizedUri.searchParams.get("access_token") || undefined;
- const claims = parseClaims(oauthIdToken) || parseClaims(oauthAccessToken);
- if (!claims) {
- if (oauthIdToken) {
- throw new errors_1.BadRequestError(`INVALID_IDP_RESPONSE : Unable to parse id_token: ${oauthIdToken} ((Auth Emulator only accepts strict JSON or JWTs as fake id_tokens.))`);
- }
- else if (oauthAccessToken) {
- if (providerId === "google.com" || providerId === "apple.com") {
- throw new errors_1.NotImplementedError(`The Auth Emulator only support sign-in with ${providerId} using id_token, not access_token. Please update your code to use id_token.`);
- }
- else {
- throw new errors_1.NotImplementedError(`The Auth Emulator does not support ${providerId} sign-in with credentials.`);
- }
- }
- else {
- throw new errors_1.NotImplementedError("The Auth Emulator only supports sign-in with credentials (id_token required).");
- }
- }
- let samlResponse;
- let signInAttributes = undefined;
- if (normalizedUri.searchParams.get("SAMLResponse")) {
- samlResponse = JSON.parse(normalizedUri.searchParams.get("SAMLResponse"));
- signInAttributes = (_b = samlResponse.assertion) === null || _b === void 0 ? void 0 : _b.attributeStatements;
- (0, errors_1.assert)(samlResponse.assertion, "INVALID_IDP_RESPONSE ((Missing assertion in SAMLResponse.))");
- (0, errors_1.assert)(samlResponse.assertion.subject, "INVALID_IDP_RESPONSE ((Missing assertion.subject in SAMLResponse.))");
- (0, errors_1.assert)(samlResponse.assertion.subject.nameId, "INVALID_IDP_RESPONSE ((Missing assertion.subject.nameId in SAMLResponse.))");
- }
- let { response, rawId } = fakeFetchUserInfoFromIdp(providerId, claims, samlResponse);
- response.oauthAccessToken =
- oauthAccessToken || `FirebaseAuthEmulatorFakeAccessToken_${providerId}`;
- response.oauthIdToken = oauthIdToken;
- const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
- const userMatchingProvider = state.getUserByProviderRawId(providerId, rawId);
- let accountUpdates;
- try {
- if (userFromIdToken) {
- (0, errors_1.assert)(!userMatchingProvider, "FEDERATED_USER_ID_ALREADY_LINKED");
- ({ accountUpdates, response } = handleLinkIdp(state, response, userFromIdToken));
- }
- else if (state.oneAccountPerEmail) {
- const userMatchingEmail = response.email ? state.getUserByEmail(response.email) : undefined;
- ({ accountUpdates, response } = handleIdpSigninEmailRequired(response, rawId, userMatchingProvider, userMatchingEmail));
- }
- else {
- ({ accountUpdates, response } = handleIdpSigninEmailNotRequired(response, userMatchingProvider));
- }
- }
- catch (err) {
- if (reqBody.returnIdpCredential && err instanceof errors_1.BadRequestError) {
- response.errorMessage = err.message;
- return response;
- }
- else {
- throw err;
- }
- }
- if (response.needConfirmation) {
- return response;
- }
- const providerUserInfo = {
- providerId,
- rawId,
- federatedId: rawId,
- displayName: response.displayName,
- photoUrl: response.photoUrl,
- email: response.email,
- screenName: response.screenName,
- };
- let user;
- let extraClaims;
- const oauthTokens = {
- oauthIdToken: response.oauthIdToken,
- oauthAccessToken: response.oauthAccessToken,
- oauthRefreshToken: response.oauthRefreshToken,
- oauthTokenSecret: response.oauthTokenSecret,
- oauthExpiresIn: coercePrimitiveToString(response.oauthExpireIn),
- };
- if (response.isNewUser) {
- const timestamp = new Date();
- let updates = Object.assign(Object.assign({}, accountUpdates.fields), { createdAt: timestamp.getTime().toString(), lastLoginAt: timestamp.getTime().toString(), providerUserInfo: [providerUserInfo], tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined });
- const localId = state.generateLocalId();
- const userBeforeCreate = Object.assign({ localId }, updates);
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, {
- signInMethod: response.providerId,
- rawUserInfo: response.rawUserInfo,
- signInAttributes: JSON.stringify(signInAttributes),
- }, oauthTokens);
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- user = state.createUserWithLocalId(localId, updates);
- response.localId = user.localId;
- if (!user.disabled && !isMfaEnabled(state, user)) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, {
- signInMethod: response.providerId,
- rawUserInfo: response.rawUserInfo,
- signInAttributes: JSON.stringify(signInAttributes),
- }, oauthTokens);
- updates = blockingResponse.updates;
- extraClaims = blockingResponse.extraClaims;
- user = state.updateUserByLocalId(user.localId, updates);
- }
- }
- else {
- if (!response.localId) {
- throw new Error("Internal assertion error: localId not set for exising user.");
- }
- const maybeUser = state.getUserByLocalId(response.localId);
- (0, errors_1.assert)(maybeUser, "USER_NOT_FOUND");
- user = maybeUser;
- let updates = Object.assign({}, accountUpdates.fields);
- if (!user.disabled && !isMfaEnabled(state, user)) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, Object.assign(Object.assign({}, user), updates), {
- signInMethod: response.providerId,
- rawUserInfo: response.rawUserInfo,
- signInAttributes: JSON.stringify(signInAttributes),
- }, oauthTokens);
- extraClaims = blockingResponse.extraClaims;
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- }
- user = state.updateUserByLocalId(response.localId, updates, {
- upsertProviders: [providerUserInfo],
- });
- }
- if (user.email === response.email) {
- response.emailVerified = user.emailVerified;
- }
- if (state instanceof state_1.TenantProjectState) {
- response.tenantId = state.tenantId;
- }
- if (isMfaEnabled(state, user)) {
- return Object.assign(Object.assign({}, response), mfaPending(state, user, providerId));
- }
- else {
- user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
- (0, errors_1.assert)(!(user === null || user === void 0 ? void 0 : user.disabled), "USER_DISABLED");
- return Object.assign(Object.assign({}, response), issueTokens(state, user, providerId, { signInAttributes, extraClaims }));
- }
- }
- async function signInWithPassword(state, reqBody) {
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
- (0, errors_1.assert)(reqBody.email !== undefined, "MISSING_EMAIL");
- (0, errors_1.assert)((0, utils_1.isValidEmailAddress)(reqBody.email), "INVALID_EMAIL");
- (0, errors_1.assert)(reqBody.password, "MISSING_PASSWORD");
- if (reqBody.captchaResponse || reqBody.captchaChallenge) {
- throw new errors_1.NotImplementedError("captcha unimplemented");
- }
- if (reqBody.idToken || reqBody.pendingIdToken) {
- throw new errors_1.NotImplementedError("idToken / pendingIdToken is no longer in use and unsupported by the Auth Emulator.");
- }
- const email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
- let user = state.getUserByEmail(email);
- (0, errors_1.assert)(user, "EMAIL_NOT_FOUND");
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- (0, errors_1.assert)(user.passwordHash && user.salt, "INVALID_PASSWORD");
- (0, errors_1.assert)(user.passwordHash === hashPassword(reqBody.password, user.salt), "INVALID_PASSWORD");
- const response = {
- kind: "identitytoolkit#VerifyPasswordResponse",
- registered: true,
- localId: user.localId,
- email,
- };
- if (isMfaEnabled(state, user)) {
- return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
- }
- else {
- const { updates, extraClaims } = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "password" });
- user = state.updateUserByLocalId(user.localId, Object.assign(Object.assign({}, updates), { lastLoginAt: Date.now().toString() }));
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- return Object.assign(Object.assign({}, response), issueTokens(state, user, state_1.PROVIDER_PASSWORD, { extraClaims }));
- }
- }
- async function signInWithPhoneNumber(state, reqBody) {
- var _a;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
- let phoneNumber;
- if (reqBody.temporaryProof) {
- (0, errors_1.assert)(reqBody.phoneNumber, "MISSING_PHONE_NUMBER");
- const proof = state.validateTemporaryProof(reqBody.temporaryProof, reqBody.phoneNumber);
- (0, errors_1.assert)(proof, "INVALID_TEMPORARY_PROOF");
- ({ phoneNumber } = proof);
- }
- else {
- (0, errors_1.assert)(reqBody.sessionInfo, "MISSING_SESSION_INFO");
- (0, errors_1.assert)(reqBody.code, "MISSING_CODE");
- phoneNumber = verifyPhoneNumber(state, reqBody.sessionInfo, reqBody.code);
- }
- const userFromPhoneNumber = state.getUserByPhoneNumber(phoneNumber);
- const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
- if (userFromPhoneNumber && userFromIdToken) {
- if (userFromPhoneNumber.localId !== userFromIdToken.localId) {
- (0, errors_1.assert)(!reqBody.temporaryProof, "PHONE_NUMBER_EXISTS");
- return Object.assign({}, state.createTemporaryProof(phoneNumber));
- }
- }
- let user = userFromIdToken || userFromPhoneNumber;
- const isNewUser = !user;
- const timestamp = new Date();
- let updates = {
- phoneNumber,
- lastLoginAt: timestamp.getTime().toString(),
- };
- let extraClaims;
- if (!user) {
- updates.createdAt = timestamp.getTime().toString();
- const localId = state.generateLocalId();
- const userBeforeCreate = Object.assign({ localId }, updates);
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, { signInMethod: "phone" });
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- user = state.createUserWithLocalId(localId, updates);
- if (!user.disabled) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "phone" });
- updates = blockingResponse.updates;
- extraClaims = blockingResponse.extraClaims;
- user = state.updateUserByLocalId(user.localId, updates);
- }
- }
- else {
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- (0, errors_1.assert)(!((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length), "UNSUPPORTED_FIRST_FACTOR : A phone number cannot be set as a first factor on an SMS based MFA user.");
- if (!user.disabled) {
- const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, Object.assign(Object.assign({}, user), updates), { signInMethod: "phone" });
- updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
- extraClaims = blockingResponse.extraClaims;
- }
- user = state.updateUserByLocalId(user.localId, updates);
- }
- (0, errors_1.assert)(!(user === null || user === void 0 ? void 0 : user.disabled), "USER_DISABLED");
- const tokens = issueTokens(state, user, state_1.PROVIDER_PHONE, {
- extraClaims,
- });
- return Object.assign({ isNewUser,
- phoneNumber, localId: user.localId }, tokens);
- }
- function grantToken(state, reqBody) {
- (0, errors_1.assert)(reqBody.grantType, "MISSING_GRANT_TYPE");
- (0, errors_1.assert)(reqBody.grantType === "refresh_token", "INVALID_GRANT_TYPE");
- (0, errors_1.assert)(reqBody.refreshToken, "MISSING_REFRESH_TOKEN");
- const refreshTokenRecord = state.validateRefreshToken(reqBody.refreshToken);
- (0, errors_1.assert)(!refreshTokenRecord.user.disabled, "USER_DISABLED");
- const tokens = issueTokens(state, refreshTokenRecord.user, refreshTokenRecord.provider, {
- extraClaims: refreshTokenRecord.extraClaims,
- secondFactor: refreshTokenRecord.secondFactor,
- });
- return {
- id_token: tokens.idToken,
- access_token: tokens.idToken,
- expires_in: tokens.expiresIn,
- refresh_token: tokens.refreshToken,
- token_type: "Bearer",
- user_id: refreshTokenRecord.user.localId,
- project_id: state.projectNumber,
- };
- }
- function deleteAllAccountsInProject(state) {
- state.deleteAllAccounts();
- return {};
- }
- function getEmulatorProjectConfig(state) {
- return {
- signIn: {
- allowDuplicateEmails: !state.oneAccountPerEmail,
- },
- };
- }
- function updateEmulatorProjectConfig(state, reqBody, ctx) {
- var _a;
- const updateMask = [];
- if (((_a = reqBody.signIn) === null || _a === void 0 ? void 0 : _a.allowDuplicateEmails) != null) {
- updateMask.push("signIn.allowDuplicateEmails");
- }
- ctx.params.query.updateMask = updateMask.join();
- updateConfig(state, reqBody, ctx);
- return getEmulatorProjectConfig(state);
- }
- function listOobCodesInProject(state) {
- return {
- oobCodes: [...state.listOobCodes()],
- };
- }
- function listVerificationCodesInProject(state) {
- return {
- verificationCodes: [...state.listVerificationCodes()],
- };
- }
- function mfaEnrollmentStart(state, reqBody) {
- var _a, _b;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
- ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
- (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
- const { user, signInProvider } = parseIdToken(state, reqBody.idToken);
- (0, errors_1.assert)(!MFA_INELIGIBLE_PROVIDER.has(signInProvider), "UNSUPPORTED_FIRST_FACTOR : MFA is not available for the given first factor.");
- (0, errors_1.assert)(user.emailVerified, "UNVERIFIED_EMAIL : Need to verify email first before enrolling second factors.");
- (0, errors_1.assert)(reqBody.phoneEnrollmentInfo, "INVALID_ARGUMENT : ((Missing phoneEnrollmentInfo.))");
- const phoneNumber = reqBody.phoneEnrollmentInfo.phoneNumber;
- (0, errors_1.assert)(phoneNumber && (0, utils_1.isValidPhoneNumber)(phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
- (0, errors_1.assert)(!((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.some((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber)), "SECOND_FACTOR_EXISTS : Phone number already enrolled as second factor for this account.");
- const { sessionInfo, code } = state.createVerificationCode(phoneNumber);
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("BULLET", `To enroll MFA with ${phoneNumber}, use the code ${code}.`);
- return {
- phoneSessionInfo: {
- sessionInfo,
- },
- };
- }
- function mfaEnrollmentFinalize(state, reqBody) {
- var _a, _b;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
- ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
- (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
- let { user, signInProvider } = parseIdToken(state, reqBody.idToken);
- (0, errors_1.assert)(!MFA_INELIGIBLE_PROVIDER.has(signInProvider), "UNSUPPORTED_FIRST_FACTOR : MFA is not available for the given first factor.");
- (0, errors_1.assert)(reqBody.phoneVerificationInfo, "INVALID_ARGUMENT : ((Missing phoneVerificationInfo.))");
- if (reqBody.phoneVerificationInfo.androidVerificationProof) {
- throw new errors_1.NotImplementedError("androidVerificationProof is unsupported!");
- }
- const { code, sessionInfo } = reqBody.phoneVerificationInfo;
- (0, errors_1.assert)(code, "MISSING_CODE");
- (0, errors_1.assert)(sessionInfo, "MISSING_SESSION_INFO");
- const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
- (0, errors_1.assert)(!((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.some((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber)), "SECOND_FACTOR_EXISTS : Phone number already enrolled as second factor for this account.");
- const existingFactors = user.mfaInfo || [];
- const existingIds = new Set();
- for (const { mfaEnrollmentId } of existingFactors) {
- if (mfaEnrollmentId) {
- existingIds.add(mfaEnrollmentId);
- }
- }
- const enrollment = {
- displayName: reqBody.displayName,
- enrolledAt: new Date().toISOString(),
- mfaEnrollmentId: newRandomId(28, existingIds),
- phoneInfo: phoneNumber,
- unobfuscatedPhoneInfo: phoneNumber,
- };
- user = state.updateUserByLocalId(user.localId, {
- mfaInfo: [...existingFactors, enrollment],
- });
- const { idToken, refreshToken } = issueTokens(state, user, signInProvider, {
- secondFactor: { identifier: enrollment.mfaEnrollmentId, provider: state_1.PROVIDER_PHONE },
- });
- return {
- idToken,
- refreshToken,
- };
- }
- function mfaEnrollmentWithdraw(state, reqBody) {
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
- let { user, signInProvider } = parseIdToken(state, reqBody.idToken);
- (0, errors_1.assert)(user.mfaInfo, "MFA_ENROLLMENT_NOT_FOUND");
- const updatedList = user.mfaInfo.filter((enrollment) => enrollment.mfaEnrollmentId !== reqBody.mfaEnrollmentId);
- (0, errors_1.assert)(updatedList.length < user.mfaInfo.length, "MFA_ENROLLMENT_NOT_FOUND");
- user = state.updateUserByLocalId(user.localId, { mfaInfo: updatedList });
- return Object.assign({}, issueTokens(state, user, signInProvider));
- }
- function mfaSignInStart(state, reqBody) {
- var _a, _b;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
- ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
- (0, errors_1.assert)(reqBody.mfaPendingCredential, "MISSING_MFA_PENDING_CREDENTIAL : Request does not have MFA pending credential.");
- (0, errors_1.assert)(reqBody.mfaEnrollmentId, "MISSING_MFA_ENROLLMENT_ID : No second factor identifier is provided.");
- const { user } = parsePendingCredential(state, reqBody.mfaPendingCredential);
- const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((factor) => factor.mfaEnrollmentId === reqBody.mfaEnrollmentId);
- (0, errors_1.assert)(enrollment, "MFA_ENROLLMENT_NOT_FOUND");
- const phoneNumber = enrollment.unobfuscatedPhoneInfo;
- (0, errors_1.assert)(phoneNumber, "INVALID_ARGUMENT : MFA provider not supported!");
- const { sessionInfo, code } = state.createVerificationCode(phoneNumber);
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("BULLET", `To sign in with MFA using ${phoneNumber}, use the code ${code}.`);
- return {
- phoneResponseInfo: {
- sessionInfo,
- },
- };
- }
- async function mfaSignInFinalize(state, reqBody) {
- var _a, _b;
- (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
- (0, errors_1.assert)((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
- ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
- (0, errors_1.assert)(reqBody.mfaPendingCredential, "MISSING_CREDENTIAL : Please set MFA Pending Credential.");
- (0, errors_1.assert)(reqBody.phoneVerificationInfo, "INVALID_ARGUMENT : MFA provider not supported!");
- if (reqBody.phoneVerificationInfo.androidVerificationProof) {
- throw new errors_1.NotImplementedError("androidVerificationProof is unsupported!");
- }
- const { code, sessionInfo } = reqBody.phoneVerificationInfo;
- (0, errors_1.assert)(code, "MISSING_CODE");
- (0, errors_1.assert)(sessionInfo, "MISSING_SESSION_INFO");
- const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
- let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
- const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber);
- const { updates, extraClaims } = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: signInProvider, signInSecondFactor: "phone" });
- user = state.updateUserByLocalId(user.localId, Object.assign(Object.assign({}, updates), { lastLoginAt: Date.now().toString() }));
- (0, errors_1.assert)(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- const { idToken, refreshToken } = issueTokens(state, user, signInProvider, {
- extraClaims,
- secondFactor: { identifier: enrollment.mfaEnrollmentId, provider: state_1.PROVIDER_PHONE },
- });
- return {
- idToken,
- refreshToken,
- };
- }
- function getConfig(state) {
- (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "((Can only get top-level configurations on agent projects.))");
- return state.config;
- }
- function updateConfig(state, reqBody, ctx) {
- var _a;
- (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "((Can only update top-level configurations on agent projects.))");
- for (const event in (_a = reqBody.blockingFunctions) === null || _a === void 0 ? void 0 : _a.triggers) {
- if (Object.prototype.hasOwnProperty.call(reqBody.blockingFunctions.triggers, event)) {
- (0, errors_1.assert)(Object.values(state_1.BlockingFunctionEvents).includes(event), "INVALID_BLOCKING_FUNCTION : ((Event type is invalid.))");
- (0, errors_1.assert)((0, utils_1.parseAbsoluteUri)(reqBody.blockingFunctions.triggers[event].functionUri), "INVALID_BLOCKING_FUNCTION : ((Expected an absolute URI with valid scheme and host.))");
- }
- }
- return state.updateConfig(reqBody, ctx.params.query.updateMask);
- }
- function coercePrimitiveToString(value) {
- switch (typeof value) {
- case "string":
- return value;
- case "number":
- case "boolean":
- return value.toString();
- default:
- return undefined;
- }
- }
- function redactPasswordHash(user) {
- return user;
- }
- function hashPassword(password, salt) {
- return `fakeHash:salt=${salt}:password=${password}`;
- }
- function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, signInAttributes, } = {}) {
- user = state.updateUserByLocalId(user.localId, { lastRefreshAt: new Date().toISOString() });
- const tenantId = state instanceof state_1.TenantProjectState ? state.tenantId : undefined;
- const expiresInSeconds = 60 * 60;
- const idToken = generateJwt(user, {
- projectId: state.projectId,
- signInProvider,
- expiresInSeconds,
- extraClaims,
- secondFactor,
- tenantId,
- signInAttributes,
- });
- const refreshToken = state.createRefreshTokenFor(user, signInProvider, {
- extraClaims,
- secondFactor,
- });
- return {
- idToken,
- refreshToken,
- expiresIn: expiresInSeconds.toString(),
- };
- }
- function parseIdToken(state, idToken) {
- const decoded = (0, jsonwebtoken_1.decode)(idToken, { complete: true });
- (0, errors_1.assert)(decoded, "INVALID_ID_TOKEN");
- if (decoded.header.alg !== "none") {
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", "Received a signed JWT. Auth Emulator does not validate JWTs and IS NOT SECURE");
- }
- if (decoded.payload.firebase.tenant) {
- (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Parsed token that belongs to tenant in a non-tenant project.))");
- (0, errors_1.assert)(decoded.payload.firebase.tenant === state.tenantId, "TENANT_ID_MISMATCH");
- }
- const user = state.getUserByLocalId(decoded.payload.user_id);
- (0, errors_1.assert)(user, "USER_NOT_FOUND");
- (0, errors_1.assert)(!user.validSince || decoded.payload.iat >= Number(user.validSince), "TOKEN_EXPIRED");
- (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
- const signInProvider = decoded.payload.firebase.sign_in_provider;
- return { user, signInProvider, payload: decoded.payload };
- }
- function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, tenantId, signInAttributes, }) {
- const identities = {};
- if (user.email) {
- identities["email"] = [user.email];
- }
- if (user.providerUserInfo) {
- for (const providerInfo of user.providerUserInfo) {
- if (providerInfo.providerId &&
- providerInfo.providerId !== state_1.PROVIDER_PASSWORD &&
- providerInfo.rawId) {
- const ids = identities[providerInfo.providerId] || [];
- ids.push(providerInfo.rawId);
- identities[providerInfo.providerId] = ids;
- }
- }
- }
- const customAttributes = JSON.parse(user.customAttributes || "{}");
- const customPayloadFields = Object.assign(Object.assign(Object.assign({ name: user.displayName, picture: user.photoUrl }, customAttributes), extraClaims), { email: user.email, email_verified: user.emailVerified, phone_number: user.phoneNumber, provider_id: signInProvider === "anonymous" ? signInProvider : undefined, auth_time: (0, utils_1.toUnixTimestamp)(getAuthTime(user)), user_id: user.localId, firebase: {
- identities,
- sign_in_provider: signInProvider,
- second_factor_identifier: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.identifier,
- sign_in_second_factor: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.provider,
- tenant: tenantId,
- sign_in_attributes: signInAttributes,
- } });
- const jwtStr = (0, jsonwebtoken_1.sign)(customPayloadFields, "", {
- algorithm: "none",
- expiresIn: expiresInSeconds,
- subject: user.localId,
- issuer: `https://securetoken.google.com/${projectId}`,
- audience: projectId,
- });
- return jwtStr;
- }
- function getAuthTime(user) {
- if (user.lastLoginAt != null) {
- const millisSinceEpoch = parseInt(user.lastLoginAt, 10);
- const authTime = new Date(millisSinceEpoch);
- if (isNaN(authTime.getTime())) {
- throw new Error(`Internal assertion error: invalid user.lastLoginAt = ${user.lastLoginAt}`);
- }
- return authTime;
- }
- else if (user.lastRefreshAt != null) {
- const authTime = new Date(user.lastRefreshAt);
- if (isNaN(authTime.getTime())) {
- throw new Error(`Internal assertion error: invalid user.lastRefreshAt = ${user.lastRefreshAt}`);
- }
- return authTime;
- }
- else {
- throw new Error(`Internal assertion error: Missing user.lastLoginAt and user.lastRefreshAt`);
- }
- }
- function verifyPhoneNumber(state, sessionInfo, code) {
- const verification = state.getVerificationCodeBySessionInfo(sessionInfo);
- (0, errors_1.assert)(verification, "INVALID_SESSION_INFO");
- (0, errors_1.assert)(verification.code === code, "INVALID_CODE");
- state.deleteVerificationCodeBySessionInfo(sessionInfo);
- return verification.phoneNumber;
- }
- const CUSTOM_ATTRIBUTES_MAX_LENGTH = 1000;
- function validateSerializedCustomClaims(claims) {
- (0, errors_1.assert)(claims.length <= CUSTOM_ATTRIBUTES_MAX_LENGTH, "CLAIMS_TOO_LARGE");
- let parsed;
- try {
- parsed = JSON.parse(claims);
- }
- catch (_a) {
- throw new errors_1.BadRequestError("INVALID_CLAIMS");
- }
- validateCustomClaims(parsed);
- }
- const FORBIDDEN_CUSTOM_CLAIMS = [
- "iss",
- "aud",
- "sub",
- "iat",
- "exp",
- "nbf",
- "jti",
- "nonce",
- "azp",
- "acr",
- "amr",
- "cnf",
- "auth_time",
- "firebase",
- "at_hash",
- "c_hash",
- ];
- function validateCustomClaims(claims) {
- (0, errors_1.assert)(typeof claims === "object" && claims != null && !Array.isArray(claims), "INVALID_CLAIMS");
- for (const reservedField of FORBIDDEN_CUSTOM_CLAIMS) {
- (0, errors_1.assert)(!(reservedField in claims), `FORBIDDEN_CLAIM : ${reservedField}`);
- }
- }
- function newRandomId(length, existingIds) {
- for (let i = 0; i < 10; i++) {
- const id = (0, utils_1.randomId)(length);
- if (!(existingIds === null || existingIds === void 0 ? void 0 : existingIds.has(id))) {
- return id;
- }
- }
- throw new errors_1.InternalError("INTERNAL_ERROR : Failed to generate a random ID after 10 attempts", "INTERNAL");
- }
- function getMfaEnrollmentsFromRequest(state, request, options) {
- const enrollments = [];
- const phoneNumbers = new Set();
- const enrollmentIds = new Set();
- for (const enrollment of request) {
- (0, errors_1.assert)(enrollment.phoneInfo && (0, utils_1.isValidPhoneNumber)(enrollment.phoneInfo), "INVALID_MFA_PHONE_NUMBER : Invalid format.");
- if (!phoneNumbers.has(enrollment.phoneInfo)) {
- const mfaEnrollmentId = (options === null || options === void 0 ? void 0 : options.generateEnrollmentIds)
- ? newRandomId(28, enrollmentIds)
- : enrollment.mfaEnrollmentId;
- (0, errors_1.assert)(mfaEnrollmentId, "INVALID_MFA_ENROLLMENT_ID : mfaEnrollmentId must be defined.");
- (0, errors_1.assert)(!enrollmentIds.has(mfaEnrollmentId), "DUPLICATE_MFA_ENROLLMENT_ID");
- enrollments.push(Object.assign(Object.assign({}, enrollment), { mfaEnrollmentId, unobfuscatedPhoneInfo: enrollment.phoneInfo }));
- phoneNumbers.add(enrollment.phoneInfo);
- enrollmentIds.add(mfaEnrollmentId);
- }
- }
- return state.validateMfaEnrollments(enrollments);
- }
- function getNormalizedUri(reqBody) {
- (0, errors_1.assert)(reqBody.requestUri, "MISSING_REQUEST_URI");
- const normalizedUri = (0, utils_1.parseAbsoluteUri)(reqBody.requestUri);
- (0, errors_1.assert)(normalizedUri, "INVALID_REQUEST_URI");
- if (reqBody.postBody) {
- const postBodyParams = new url_1.URLSearchParams(reqBody.postBody);
- for (const key of postBodyParams.keys()) {
- normalizedUri.searchParams.set(key, postBodyParams.get(key));
- }
- }
- const fragment = normalizedUri.hash.replace(/^#/, "");
- if (fragment) {
- const fragmentParams = new url_1.URLSearchParams(fragment);
- for (const key of fragmentParams.keys()) {
- normalizedUri.searchParams.set(key, fragmentParams.get(key));
- }
- normalizedUri.hash = "";
- }
- return normalizedUri;
- }
- function parseClaims(idTokenOrJsonClaims) {
- if (!idTokenOrJsonClaims) {
- return undefined;
- }
- let claims;
- if (idTokenOrJsonClaims.startsWith("{")) {
- try {
- claims = JSON.parse(idTokenOrJsonClaims);
- }
- catch (_a) {
- throw new errors_1.BadRequestError(`INVALID_IDP_RESPONSE : Unable to parse id_token: ${idTokenOrJsonClaims} ((Auth Emulator failed to parse fake id_token as strict JSON.))`);
- }
- }
- else {
- const decoded = (0, jsonwebtoken_1.decode)(idTokenOrJsonClaims, { json: true });
- if (!decoded) {
- return undefined;
- }
- claims = decoded;
- }
- (0, errors_1.assert)(claims.sub, 'INVALID_IDP_RESPONSE : Invalid Idp Response: id_token missing required fields. ((Missing "sub" field. This field is required and must be a unique identifier.))');
- (0, errors_1.assert)(typeof claims.sub === "string", 'INVALID_IDP_RESPONSE : ((The "sub" field must be a string.))');
- return claims;
- }
- function fakeFetchUserInfoFromIdp(providerId, claims, samlResponse) {
- var _a, _b, _c, _d, _e;
- const rawId = claims.sub;
- const email = claims.email ? (0, utils_1.canonicalizeEmailAddress)(claims.email) : undefined;
- const emailVerified = !!claims.email_verified;
- const displayName = claims.name;
- const photoUrl = claims.picture;
- const response = {
- kind: "identitytoolkit#VerifyAssertionResponse",
- context: "",
- providerId,
- displayName,
- fullName: displayName,
- screenName: claims.screen_name,
- email,
- emailVerified,
- photoUrl,
- };
- let federatedId = rawId;
- switch (providerId) {
- case "google.com": {
- federatedId = `https://accounts.google.com/${rawId}`;
- let grantedScopes = "openid https://www.googleapis.com/auth/userinfo.profile";
- if (email) {
- grantedScopes += " https://www.googleapis.com/auth/userinfo.email";
- }
- response.firstName = claims.given_name;
- response.lastName = claims.family_name;
- response.rawUserInfo = JSON.stringify({
- granted_scopes: grantedScopes,
- id: rawId,
- name: displayName,
- given_name: claims.given_name,
- family_name: claims.family_name,
- verified_email: emailVerified,
- locale: "en",
- email,
- picture: photoUrl,
- });
- break;
- }
- case (_a = providerId.match(/^saml\./)) === null || _a === void 0 ? void 0 : _a.input:
- const nameId = (_c = (_b = samlResponse === null || samlResponse === void 0 ? void 0 : samlResponse.assertion) === null || _b === void 0 ? void 0 : _b.subject) === null || _c === void 0 ? void 0 : _c.nameId;
- response.email = nameId && (0, utils_1.isValidEmailAddress)(nameId) ? nameId : response.email;
- response.emailVerified = true;
- response.rawUserInfo = JSON.stringify((_d = samlResponse === null || samlResponse === void 0 ? void 0 : samlResponse.assertion) === null || _d === void 0 ? void 0 : _d.attributeStatements);
- break;
- case (_e = providerId.match(/^oidc\./)) === null || _e === void 0 ? void 0 : _e.input:
- default:
- response.rawUserInfo = JSON.stringify(claims);
- break;
- }
- response.federatedId = federatedId;
- return { response, rawId };
- }
- function handleLinkIdp(state, response, userFromIdToken) {
- if (state.oneAccountPerEmail && response.email) {
- const userMatchingEmail = state.getUserByEmail(response.email);
- (0, errors_1.assert)(!userMatchingEmail || userMatchingEmail.localId === userFromIdToken.localId, "EMAIL_EXISTS");
- }
- response.localId = userFromIdToken.localId;
- const fields = {};
- if (state.oneAccountPerEmail && response.email && !userFromIdToken.email) {
- fields.email = response.email;
- fields.emailVerified = response.emailVerified;
- }
- if (response.email &&
- response.emailVerified &&
- (fields.email || userFromIdToken.email) === response.email) {
- fields.emailVerified = true;
- }
- return { accountUpdates: { fields }, response };
- }
- function handleIdpSigninEmailNotRequired(response, userMatchingProvider) {
- if (userMatchingProvider) {
- return {
- response: Object.assign(Object.assign({}, response), { localId: userMatchingProvider.localId }),
- accountUpdates: {},
- };
- }
- else {
- return handleIdpSignUp(response, { emailRequired: false });
- }
- }
- function handleIdpSigninEmailRequired(response, rawId, userMatchingProvider, userMatchingEmail) {
- var _a, _b, _c;
- if (userMatchingProvider) {
- return {
- response: Object.assign(Object.assign({}, response), { localId: userMatchingProvider.localId }),
- accountUpdates: {},
- };
- }
- else if (userMatchingEmail) {
- if (response.emailVerified) {
- if ((_a = userMatchingEmail.providerUserInfo) === null || _a === void 0 ? void 0 : _a.some((info) => info.providerId === response.providerId && info.rawId !== rawId)) {
- response.emailRecycled = true;
- }
- response.localId = userMatchingEmail.localId;
- const accountUpdates = {
- fields: {},
- };
- if (!userMatchingEmail.emailVerified) {
- accountUpdates.fields.passwordHash = undefined;
- accountUpdates.fields.phoneNumber = undefined;
- accountUpdates.fields.validSince = (0, utils_1.toUnixTimestamp)(new Date()).toString();
- accountUpdates.deleteProviders = (_b = userMatchingEmail.providerUserInfo) === null || _b === void 0 ? void 0 : _b.map((info) => info.providerId);
- }
- accountUpdates.fields.dateOfBirth = response.dateOfBirth;
- accountUpdates.fields.displayName = response.displayName;
- accountUpdates.fields.language = response.language;
- accountUpdates.fields.photoUrl = response.photoUrl;
- accountUpdates.fields.screenName = response.screenName;
- accountUpdates.fields.emailVerified = true;
- return { response, accountUpdates };
- }
- else {
- response.needConfirmation = true;
- response.localId = userMatchingEmail.localId;
- response.verifiedProvider = (_c = userMatchingEmail.providerUserInfo) === null || _c === void 0 ? void 0 : _c.map((info) => info.providerId).filter((id) => id !== state_1.PROVIDER_PASSWORD && id !== state_1.PROVIDER_PHONE);
- return { response, accountUpdates: {} };
- }
- }
- else {
- return handleIdpSignUp(response, { emailRequired: true });
- }
- }
- function handleIdpSignUp(response, options) {
- const accountUpdates = {
- fields: {
- dateOfBirth: response.dateOfBirth,
- displayName: response.displayName,
- language: response.language,
- photoUrl: response.photoUrl,
- screenName: response.screenName,
- },
- };
- if (options.emailRequired && response.email) {
- accountUpdates.fields.email = response.email;
- accountUpdates.fields.emailVerified = response.emailVerified;
- }
- return {
- response: Object.assign(Object.assign({}, response), { isNewUser: true }),
- accountUpdates,
- };
- }
- function mfaPending(state, user, signInProvider) {
- if (!user.mfaInfo) {
- throw new Error("Internal assertion error: mfaPending called on user without MFA.");
- }
- const pendingCredentialPayload = {
- _AuthEmulatorMfaPendingCredential: "DO NOT MODIFY",
- localId: user.localId,
- signInProvider,
- projectId: state.projectId,
- };
- if (state instanceof state_1.TenantProjectState) {
- pendingCredentialPayload.tenantId = state.tenantId;
- }
- const mfaPendingCredential = Buffer.from(JSON.stringify(pendingCredentialPayload), "utf8").toString("base64");
- return { mfaPendingCredential, mfaInfo: user.mfaInfo.map(redactMfaInfo) };
- }
- function redactMfaInfo(mfaInfo) {
- return {
- displayName: mfaInfo.displayName,
- enrolledAt: mfaInfo.enrolledAt,
- mfaEnrollmentId: mfaInfo.mfaEnrollmentId,
- phoneInfo: mfaInfo.unobfuscatedPhoneInfo
- ? obfuscatePhoneNumber(mfaInfo.unobfuscatedPhoneInfo)
- : undefined,
- };
- }
- function obfuscatePhoneNumber(phoneNumber) {
- const split = phoneNumber.split("");
- let digitsEncountered = 0;
- for (let i = split.length - 1; i >= 0; i--) {
- if (/[0-9]/.test(split[i])) {
- digitsEncountered++;
- if (digitsEncountered > 4) {
- split[i] = "*";
- }
- }
- }
- return split.join("");
- }
- function parsePendingCredential(state, pendingCredential) {
- let pendingCredentialPayload;
- try {
- const json = Buffer.from(pendingCredential, "base64").toString("utf8");
- pendingCredentialPayload = JSON.parse(json);
- }
- catch (_a) {
- (0, errors_1.assert)(false, "((Invalid phoneVerificationInfo.mfaPendingCredential.))");
- }
- (0, errors_1.assert)(pendingCredentialPayload._AuthEmulatorMfaPendingCredential, "((Invalid phoneVerificationInfo.mfaPendingCredential.))");
- (0, errors_1.assert)(pendingCredentialPayload.projectId === state.projectId, "INVALID_PROJECT_ID : Project ID does not match MFA pending credential.");
- if (state instanceof state_1.TenantProjectState) {
- (0, errors_1.assert)(pendingCredentialPayload.tenantId === state.tenantId, "INVALID_PROJECT_ID : Project ID does not match MFA pending credential.");
- }
- const { localId, signInProvider } = pendingCredentialPayload;
- const user = state.getUserByLocalId(localId);
- (0, errors_1.assert)(user, "((User in pendingCredentialPayload does not exist.))");
- return { user, signInProvider };
- }
- function createTenant(state, reqBody) {
- var _a, _b, _c, _d, _e;
- if (!(state instanceof state_1.AgentProjectState)) {
- throw new errors_1.InternalError("INTERNAL_ERROR : Can only create tenant in agent project", "INTERNAL");
- }
- const mfaConfig = (_a = reqBody.mfaConfig) !== null && _a !== void 0 ? _a : {};
- if (!("state" in mfaConfig)) {
- mfaConfig.state = "DISABLED";
- }
- if (!("enabledProviders" in mfaConfig)) {
- mfaConfig.enabledProviders = [];
- }
- const tenant = {
- displayName: reqBody.displayName,
- allowPasswordSignup: (_b = reqBody.allowPasswordSignup) !== null && _b !== void 0 ? _b : false,
- enableEmailLinkSignin: (_c = reqBody.enableEmailLinkSignin) !== null && _c !== void 0 ? _c : false,
- enableAnonymousUser: (_d = reqBody.enableAnonymousUser) !== null && _d !== void 0 ? _d : false,
- disableAuth: (_e = reqBody.disableAuth) !== null && _e !== void 0 ? _e : false,
- mfaConfig: mfaConfig,
- tenantId: "",
- };
- return state.createTenant(tenant);
- }
- function listTenants(state, reqBody, ctx) {
- (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "((Can only list tenants in agent project.))");
- const pageSize = Math.min(Math.floor(ctx.params.query.pageSize) || 20, 1000);
- const tenants = state.listTenants(ctx.params.query.pageToken);
- let nextPageToken = undefined;
- if (pageSize > 0 && tenants.length >= pageSize) {
- tenants.length = pageSize;
- nextPageToken = tenants[tenants.length - 1].tenantId;
- }
- return {
- nextPageToken,
- tenants,
- };
- }
- function deleteTenant(state) {
- (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Can only delete tenant on tenant projects.))");
- state.delete();
- return {};
- }
- function getTenant(state) {
- (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Can only get tenant on tenant projects.))");
- return state.tenantConfig;
- }
- function updateTenant(state, reqBody, ctx) {
- (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Can only update tenant on tenant projects.))");
- return state.updateTenant(reqBody, ctx.params.query.updateMask);
- }
- function isMfaEnabled(state, user) {
- var _a;
- return ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
- ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length));
- }
- async function fetchBlockingFunction(state, event, user, options = {}, oauthTokens = {}, timeoutMs = 60000) {
- const url = state.getBlockingFunctionUri(event);
- if (!url) {
- return { updates: {} };
- }
- const jwt = generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options, oauthTokens);
- const reqBody = {
- data: {
- jwt,
- },
- };
- const controller = new abort_controller_1.default();
- const timeout = setTimeout(() => {
- controller.abort();
- }, timeoutMs);
- let response;
- let ok;
- let status;
- let text;
- try {
- const res = await (0, node_fetch_1.default)(url, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(reqBody),
- signal: controller.signal,
- });
- ok = res.ok;
- status = res.status;
- text = await res.text();
- }
- catch (thrown) {
- const err = thrown instanceof Error ? thrown : new Error(thrown);
- const isAbortError = err.name.includes("AbortError");
- if (isAbortError) {
- throw new errors_1.InternalError(`BLOCKING_FUNCTION_ERROR_RESPONSE : ((Deadline exceeded making request to ${url}.))`, err.message);
- }
- throw new errors_1.InternalError(`BLOCKING_FUNCTION_ERROR_RESPONSE : ((Failed to make request to ${url}.))`, err.message);
- }
- finally {
- clearTimeout(timeout);
- }
- (0, errors_1.assert)(ok, `BLOCKING_FUNCTION_ERROR_RESPONSE : ((HTTP request to ${url} returned HTTP error ${status}: ${text}))`);
- try {
- response = JSON.parse(text);
- }
- catch (thrown) {
- const err = thrown instanceof Error ? thrown : new Error(thrown);
- throw new errors_1.InternalError(`BLOCKING_FUNCTION_ERROR_RESPONSE : ((Response body is not valid JSON.))`, err.message);
- }
- return processBlockingFunctionResponse(event, response);
- }
- function processBlockingFunctionResponse(event, response) {
- let extraClaims;
- const updates = {};
- if (response.userRecord) {
- const userRecord = response.userRecord;
- (0, errors_1.assert)(userRecord.updateMask, "BLOCKING_FUNCTION_ERROR_RESPONSE : ((Response UserRecord is missing updateMask.))");
- const mask = userRecord.updateMask;
- const fields = mask.split(",");
- for (const field of fields) {
- switch (field) {
- case "displayName":
- case "photoUrl":
- updates[field] = coercePrimitiveToString(userRecord[field]);
- break;
- case "disabled":
- case "emailVerified":
- updates[field] = !!userRecord[field];
- break;
- case "customClaims":
- const customClaims = JSON.stringify(userRecord.customClaims);
- validateSerializedCustomClaims(customClaims);
- updates.customAttributes = customClaims;
- break;
- case "sessionClaims":
- if (event !== state_1.BlockingFunctionEvents.BEFORE_SIGN_IN) {
- break;
- }
- try {
- extraClaims = userRecord.sessionClaims;
- }
- catch (_a) {
- throw new errors_1.BadRequestError("BLOCKING_FUNCTION_ERROR_RESPONSE : ((Response has malformed session claims.))");
- }
- break;
- default:
- break;
- }
- }
- }
- return { updates, extraClaims };
- }
- function generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options, oauthTokens) {
- const issuedAt = (0, utils_1.toUnixTimestamp)(new Date());
- const jwt = {
- iss: `https://securetoken.google.com/${state.projectId}`,
- aud: url,
- iat: issuedAt,
- exp: issuedAt + timeoutMs / 100,
- event_id: (0, utils_1.randomBase64UrlStr)(16),
- event_type: event,
- user_agent: "NotYetSupportedInFirebaseAuthEmulator",
- ip_address: "127.0.0.1",
- locale: "en",
- user_record: {
- uid: user.localId,
- email: user.email,
- email_verified: user.emailVerified,
- display_name: user.displayName,
- photo_url: user.photoUrl,
- disabled: user.disabled,
- phone_number: user.phoneNumber,
- custom_claims: JSON.parse(user.customAttributes || "{}"),
- },
- sub: user.localId,
- sign_in_method: options.signInMethod,
- sign_in_second_factor: options.signInSecondFactor,
- sign_in_attributes: options.signInAttributes,
- raw_user_info: options.rawUserInfo,
- };
- if (state instanceof state_1.TenantProjectState) {
- jwt.tenant_id = state.tenantId;
- jwt.user_record.tenant_id = state.tenantId;
- }
- const providerData = [];
- if (user.providerUserInfo) {
- for (const providerUserInfo of user.providerUserInfo) {
- const provider = {
- provider_id: providerUserInfo.providerId,
- display_name: providerUserInfo.displayName,
- photo_url: providerUserInfo.photoUrl,
- email: providerUserInfo.email,
- uid: providerUserInfo.rawId,
- phone_number: providerUserInfo.phoneNumber,
- };
- providerData.push(provider);
- }
- }
- jwt.user_record.provider_data = providerData;
- if (user.mfaInfo) {
- const enrolledFactors = [];
- for (const mfaEnrollment of user.mfaInfo) {
- if (!mfaEnrollment.mfaEnrollmentId) {
- continue;
- }
- const enrolledFactor = {
- uid: mfaEnrollment.mfaEnrollmentId,
- display_name: mfaEnrollment.displayName,
- enrollment_time: mfaEnrollment.enrolledAt,
- phone_number: mfaEnrollment.phoneInfo,
- factor_id: state_1.PROVIDER_PHONE,
- };
- enrolledFactors.push(enrolledFactor);
- }
- jwt.user_record.multi_factor = {
- enrolled_factors: enrolledFactors,
- };
- }
- if (user.lastLoginAt || user.createdAt) {
- jwt.user_record.metadata = {
- last_sign_in_time: user.lastLoginAt,
- creation_time: user.createdAt,
- };
- }
- if (state.shouldForwardCredentialToBlockingFunction("accessToken")) {
- jwt.oauth_access_token = oauthTokens.oauthAccessToken;
- jwt.oauth_token_secret = oauthTokens.oauthTokenSecret;
- jwt.oauth_expires_in = oauthTokens.oauthExpiresIn;
- }
- if (state.shouldForwardCredentialToBlockingFunction("idToken")) {
- jwt.oauth_id_token = oauthTokens.oauthIdToken;
- }
- if (state.shouldForwardCredentialToBlockingFunction("refreshToken")) {
- jwt.oauth_refresh_token = oauthTokens.oauthRefreshToken;
- }
- const jwtStr = (0, jsonwebtoken_1.sign)(jwt, "", {
- algorithm: "none",
- });
- return jwtStr;
- }
- function parseBlockingFunctionJwt(jwt) {
- const decoded = (0, jsonwebtoken_1.decode)(jwt, { json: true });
- (0, errors_1.assert)(decoded, "((Invalid blocking function jwt.))");
- (0, errors_1.assert)(decoded.iss, "((Invalid blocking function jwt, missing `iss` claim.))");
- (0, errors_1.assert)(decoded.aud, "((Invalid blocking function jwt, missing `aud` claim.))");
- (0, errors_1.assert)(decoded.user_record, "((Invalid blocking function jwt, missing `user_record` claim.))");
- return decoded;
- }
- exports.parseBlockingFunctionJwt = parseBlockingFunctionJwt;
|