firebase-tools 11.1.0 → 11.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/lib/accountExporter.js +11 -4
  2. package/lib/accountImporter.js +5 -6
  3. package/lib/appdistribution/client.js +7 -9
  4. package/lib/auth.js +3 -5
  5. package/lib/checkValidTargetFilters.js +28 -18
  6. package/lib/commands/database-profile.js +2 -3
  7. package/lib/commands/database-push.js +2 -3
  8. package/lib/commands/database-remove.js +1 -2
  9. package/lib/commands/database-set.js +1 -2
  10. package/lib/commands/deploy.js +5 -6
  11. package/lib/commands/ext-dev-emulators-exec.js +1 -1
  12. package/lib/commands/ext-dev-emulators-start.js +1 -1
  13. package/lib/commands/ext-dev-list.js +6 -7
  14. package/lib/commands/ext-export.js +2 -0
  15. package/lib/commands/ext-info.js +12 -13
  16. package/lib/commands/ext.js +2 -3
  17. package/lib/commands/functions-delete.js +1 -7
  18. package/lib/commands/open.js +5 -6
  19. package/lib/commands/serve.js +3 -5
  20. package/lib/commands/use.js +2 -3
  21. package/lib/config.js +4 -3
  22. package/lib/deploy/database/prepare.js +2 -3
  23. package/lib/deploy/extensions/planner.js +1 -0
  24. package/lib/deploy/extensions/prepare.js +18 -1
  25. package/lib/deploy/extensions/release.js +4 -0
  26. package/lib/deploy/extensions/secrets.js +3 -3
  27. package/lib/deploy/functions/build.js +35 -53
  28. package/lib/deploy/functions/ensure.js +1 -11
  29. package/lib/deploy/functions/params.js +189 -0
  30. package/lib/deploy/functions/prepare.js +3 -13
  31. package/lib/deploy/functions/prepareFunctionsUpload.js +2 -3
  32. package/lib/deploy/functions/release/fabricator.js +0 -1
  33. package/lib/deploy/functions/release/index.js +1 -5
  34. package/lib/deploy/functions/runtimes/discovery/index.js +18 -3
  35. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +3 -2
  36. package/lib/deploy/functions/runtimes/golang/index.js +2 -22
  37. package/lib/deploy/functions/runtimes/node/index.js +3 -7
  38. package/lib/deploy/functions/runtimes/node/parseTriggers.js +2 -2
  39. package/lib/deploy/lifecycleHooks.js +8 -11
  40. package/lib/deploy/storage/prepare.js +2 -2
  41. package/lib/emulator/auth/index.js +1 -1
  42. package/lib/emulator/auth/operations.js +336 -64
  43. package/lib/emulator/auth/server.js +2 -2
  44. package/lib/emulator/auth/state.js +32 -7
  45. package/lib/emulator/commandUtils.js +1 -1
  46. package/lib/emulator/constants.js +1 -1
  47. package/lib/emulator/controller.js +6 -5
  48. package/lib/emulator/databaseEmulator.js +2 -2
  49. package/lib/emulator/download.js +1 -1
  50. package/lib/emulator/downloadableEmulators.js +6 -6
  51. package/lib/emulator/events/types.js +2 -3
  52. package/lib/emulator/firestoreEmulator.js +2 -2
  53. package/lib/emulator/functionsEmulator.js +36 -45
  54. package/lib/emulator/functionsEmulatorRuntime.js +12 -16
  55. package/lib/emulator/functionsEmulatorShared.js +7 -5
  56. package/lib/emulator/functionsRuntimeWorker.js +0 -6
  57. package/lib/emulator/hostingEmulator.js +1 -1
  58. package/lib/emulator/hub.js +1 -1
  59. package/lib/emulator/loggingEmulator.js +1 -1
  60. package/lib/emulator/pubsubEmulator.js +1 -1
  61. package/lib/emulator/storage/crc.js +4 -4
  62. package/lib/emulator/storage/index.js +1 -1
  63. package/lib/emulator/types.js +1 -1
  64. package/lib/extensions/askUserForConsent.js +1 -2
  65. package/lib/extensions/askUserForParam.js +15 -18
  66. package/lib/extensions/emulator/optionsHelper.js +4 -4
  67. package/lib/extensions/etags.js +28 -0
  68. package/lib/extensions/extensionsApi.js +1 -22
  69. package/lib/extensions/extensionsHelper.js +6 -6
  70. package/lib/extensions/listExtensions.js +9 -10
  71. package/lib/extensions/manifest.js +2 -2
  72. package/lib/extensions/resolveSource.js +11 -7
  73. package/lib/extensions/secretsUtils.js +3 -3
  74. package/lib/extensions/types.js +24 -0
  75. package/lib/extensions/updateHelper.js +1 -1
  76. package/lib/extensions/utils.js +1 -2
  77. package/lib/extensions/warnings.js +10 -4
  78. package/lib/firestore/encodeFirestoreValue.js +11 -8
  79. package/lib/fsAsync.js +3 -3
  80. package/lib/functions/env.js +5 -1
  81. package/lib/functionsConfig.js +18 -15
  82. package/lib/functionsConfigClone.js +10 -12
  83. package/lib/gcp/cloudfunctions.js +2 -15
  84. package/lib/gcp/rules.js +3 -4
  85. package/lib/gcp/runtimeconfig.js +2 -2
  86. package/lib/hosting/api.js +9 -11
  87. package/lib/hosting/expireUtils.js +2 -2
  88. package/lib/hosting/proxy.js +1 -1
  89. package/lib/init/features/hosting/github.js +1 -1
  90. package/lib/init/features/hosting/index.js +2 -2
  91. package/lib/localFunction.js +4 -4
  92. package/lib/management/projects.js +6 -7
  93. package/lib/previews.js +1 -1
  94. package/lib/profileReport.js +24 -22
  95. package/lib/prompt.js +1 -2
  96. package/lib/rc.js +12 -2
  97. package/lib/rulesDeploy.js +2 -2
  98. package/lib/serve/index.js +4 -5
  99. package/lib/utils.js +30 -6
  100. package/npm-shrinkwrap.json +2 -2
  101. package/package.json +10 -9
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setAccountInfoImpl = exports.resetPassword = exports.SESSION_COOKIE_MAX_VALID_DURATION = exports.CUSTOM_TOKEN_AUDIENCE = exports.authOperations = void 0;
3
+ exports.parseBlockingFunctionJwt = exports.setAccountInfoImpl = exports.resetPassword = exports.SESSION_COOKIE_MAX_VALID_DURATION = exports.CUSTOM_TOKEN_AUDIENCE = exports.authOperations = void 0;
4
4
  const url_1 = require("url");
5
5
  const jsonwebtoken_1 = require("jsonwebtoken");
6
+ const node_fetch_1 = require("node-fetch");
7
+ const abort_controller_1 = require("abort-controller");
6
8
  const utils_1 = require("./utils");
7
9
  const errors_1 = require("./errors");
8
10
  const types_1 = require("../types");
@@ -102,12 +104,13 @@ const MFA_INELIGIBLE_PROVIDER = new Set([
102
104
  state_1.PROVIDER_CUSTOM,
103
105
  state_1.PROVIDER_GAME_CENTER,
104
106
  ]);
105
- function signUp(state, reqBody, ctx) {
106
- var _a;
107
+ async function signUp(state, reqBody, ctx) {
108
+ var _a, _b, _c, _d;
107
109
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
108
110
  let provider;
109
- const updates = {
110
- lastLoginAt: Date.now().toString(),
111
+ const timestamp = new Date();
112
+ let updates = {
113
+ lastLoginAt: timestamp.getTime().toString(),
111
114
  };
112
115
  if ((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2) {
113
116
  if (reqBody.idToken) {
@@ -168,19 +171,31 @@ function signUp(state, reqBody, ctx) {
168
171
  if (reqBody.idToken) {
169
172
  ({ user } = parseIdToken(state, reqBody.idToken));
170
173
  }
174
+ let extraClaims;
171
175
  if (!user) {
172
- if (reqBody.localId) {
173
- user = state.createUserWithLocalId(reqBody.localId, updates);
174
- (0, errors_1.assert)(user, "DUPLICATE_LOCAL_ID");
176
+ updates.createdAt = timestamp.getTime().toString();
177
+ const localId = (_b = reqBody.localId) !== null && _b !== void 0 ? _b : state.generateLocalId();
178
+ if (reqBody.email && !((_c = ctx.security) === null || _c === void 0 ? void 0 : _c.Oauth2)) {
179
+ const userBeforeCreate = Object.assign({ localId }, updates);
180
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, { signInMethod: "password" });
181
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
175
182
  }
176
- else {
177
- user = state.createUser(updates);
183
+ user = state.createUserWithLocalId(localId, updates);
184
+ (0, errors_1.assert)(user, "DUPLICATE_LOCAL_ID");
185
+ if (reqBody.email && !((_d = ctx.security) === null || _d === void 0 ? void 0 : _d.Oauth2)) {
186
+ if (!user.disabled) {
187
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "password" });
188
+ updates = blockingResponse.updates;
189
+ extraClaims = blockingResponse.extraClaims;
190
+ user = state.updateUserByLocalId(user.localId, updates);
191
+ }
192
+ (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
178
193
  }
179
194
  }
180
195
  else {
181
196
  user = state.updateUserByLocalId(user.localId, updates);
182
197
  }
183
- return Object.assign({ kind: "identitytoolkit#SignupNewUserResponse", localId: user.localId, displayName: user.displayName, email: user.email }, (provider ? issueTokens(state, user, provider) : {}));
198
+ return Object.assign({ kind: "identitytoolkit#SignupNewUserResponse", localId: user.localId, displayName: user.displayName, email: user.email }, (provider ? issueTokens(state, user, provider, { extraClaims }) : {}));
184
199
  }
185
200
  function lookup(state, reqBody, ctx) {
186
201
  var _a, _b, _c, _d, _e;
@@ -479,7 +494,7 @@ function createAuthUri(state, reqBody) {
479
494
  }
480
495
  const SESSION_COOKIE_MIN_VALID_DURATION = 5 * 60;
481
496
  exports.SESSION_COOKIE_MAX_VALID_DURATION = 14 * 24 * 60 * 60;
482
- function createSessionCookie(state, reqBody, ctx) {
497
+ function createSessionCookie(state, reqBody) {
483
498
  (0, errors_1.assert)(reqBody.idToken, "MISSING_ID_TOKEN");
484
499
  const validDuration = Number(reqBody.validDuration) || exports.SESSION_COOKIE_MAX_VALID_DURATION;
485
500
  (0, errors_1.assert)(validDuration >= SESSION_COOKIE_MIN_VALID_DURATION &&
@@ -936,9 +951,10 @@ function signInWithCustomToken(state, reqBody) {
936
951
  }
937
952
  let user = state.getUserByLocalId(localId);
938
953
  const isNewUser = !user;
954
+ const timestamp = new Date();
939
955
  const updates = {
940
956
  customAuth: true,
941
- lastLoginAt: Date.now().toString(),
957
+ lastLoginAt: timestamp.getTime().toString(),
942
958
  tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined,
943
959
  };
944
960
  if (user) {
@@ -946,6 +962,7 @@ function signInWithCustomToken(state, reqBody) {
946
962
  user = state.updateUserByLocalId(localId, updates);
947
963
  }
948
964
  else {
965
+ updates.createdAt = timestamp.getTime().toString();
949
966
  user = state.createUserWithLocalId(localId, updates);
950
967
  if (!user) {
951
968
  throw new Error(`Internal assertion error: trying to create duplicate localId: ${localId}`);
@@ -953,8 +970,7 @@ function signInWithCustomToken(state, reqBody) {
953
970
  }
954
971
  return Object.assign({ kind: "identitytoolkit#VerifyCustomTokenResponse", isNewUser }, issueTokens(state, user, state_1.PROVIDER_CUSTOM, { extraClaims }));
955
972
  }
956
- function signInWithEmailLink(state, reqBody) {
957
- var _a;
973
+ async function signInWithEmailLink(state, reqBody) {
958
974
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
959
975
  (0, errors_1.assert)(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
960
976
  const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
@@ -965,7 +981,11 @@ function signInWithEmailLink(state, reqBody) {
965
981
  (0, errors_1.assert)(oob && oob.requestType === "EMAIL_SIGNIN", "INVALID_OOB_CODE");
966
982
  (0, errors_1.assert)(email === oob.email, "INVALID_EMAIL : The email provided does not match the sign-in email address.");
967
983
  state.deleteOobCode(reqBody.oobCode);
968
- const updates = {
984
+ const userFromEmail = state.getUserByEmail(email);
985
+ let user = userFromIdToken || userFromEmail;
986
+ const isNewUser = !user;
987
+ const timestamp = new Date();
988
+ let updates = {
969
989
  email,
970
990
  emailVerified: true,
971
991
  emailLinkSignin: true,
@@ -973,19 +993,31 @@ function signInWithEmailLink(state, reqBody) {
973
993
  if (state instanceof state_1.TenantProjectState) {
974
994
  updates.tenantId = state.tenantId;
975
995
  }
976
- let user = state.getUserByEmail(email);
977
- const isNewUser = !user && !userFromIdToken;
996
+ let extraClaims;
978
997
  if (!user) {
979
- if (userFromIdToken) {
980
- user = state.updateUserByLocalId(userFromIdToken.localId, updates);
981
- }
982
- else {
983
- user = state.createUser(updates);
998
+ updates.createdAt = timestamp.getTime().toString();
999
+ const localId = state.generateLocalId();
1000
+ const userBeforeCreate = Object.assign({ localId }, updates);
1001
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, { signInMethod: "emailLink" });
1002
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
1003
+ user = state.createUserWithLocalId(localId, updates);
1004
+ if (!user.disabled && !isMfaEnabled(state, user)) {
1005
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "emailLink" });
1006
+ updates = blockingResponse.updates;
1007
+ extraClaims = blockingResponse.extraClaims;
1008
+ user = state.updateUserByLocalId(user.localId, updates);
984
1009
  }
985
1010
  }
986
1011
  else {
987
1012
  (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
988
- (0, errors_1.assert)(!userFromIdToken || userFromIdToken.localId === user.localId, "EMAIL_EXISTS");
1013
+ if (userFromIdToken && userFromEmail) {
1014
+ (0, errors_1.assert)(userFromIdToken.localId === userFromEmail.localId, "EMAIL_EXISTS");
1015
+ }
1016
+ if (!user.disabled && !isMfaEnabled(state, user)) {
1017
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, Object.assign(Object.assign({}, user), updates), { signInMethod: "emailLink" });
1018
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
1019
+ extraClaims = blockingResponse.extraClaims;
1020
+ }
989
1021
  user = state.updateUserByLocalId(user.localId, updates);
990
1022
  }
991
1023
  const response = {
@@ -994,17 +1026,17 @@ function signInWithEmailLink(state, reqBody) {
994
1026
  localId: user.localId,
995
1027
  isNewUser,
996
1028
  };
997
- if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
998
- ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length)) {
1029
+ (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
1030
+ if (isMfaEnabled(state, user)) {
999
1031
  return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
1000
1032
  }
1001
1033
  else {
1002
1034
  user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
1003
- return Object.assign(Object.assign({}, response), issueTokens(state, user, state_1.PROVIDER_PASSWORD));
1035
+ return Object.assign(Object.assign({}, response), issueTokens(state, user, state_1.PROVIDER_PASSWORD, { extraClaims }));
1004
1036
  }
1005
1037
  }
1006
- function signInWithIdp(state, reqBody) {
1007
- var _a, _b, _c;
1038
+ async function signInWithIdp(state, reqBody) {
1039
+ var _a, _b;
1008
1040
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
1009
1041
  if (reqBody.returnRefreshToken) {
1010
1042
  throw new errors_1.NotImplementedError("returnRefreshToken is not implemented yet.");
@@ -1085,15 +1117,55 @@ function signInWithIdp(state, reqBody) {
1085
1117
  screenName: response.screenName,
1086
1118
  };
1087
1119
  let user;
1120
+ let extraClaims;
1121
+ const oauthTokens = {
1122
+ oauthIdToken: response.oauthIdToken,
1123
+ oauthAccessToken: response.oauthAccessToken,
1124
+ oauthRefreshToken: response.oauthRefreshToken,
1125
+ oauthTokenSecret: response.oauthTokenSecret,
1126
+ oauthExpiresIn: coercePrimitiveToString(response.oauthExpireIn),
1127
+ };
1088
1128
  if (response.isNewUser) {
1089
- user = state.createUser(Object.assign(Object.assign({}, accountUpdates.fields), { lastLoginAt: Date.now().toString(), providerUserInfo: [providerUserInfo], tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined }));
1129
+ let updates = Object.assign(Object.assign({}, accountUpdates.fields), { lastLoginAt: Date.now().toString(), providerUserInfo: [providerUserInfo], tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined });
1130
+ const localId = state.generateLocalId();
1131
+ const userBeforeCreate = Object.assign({ localId }, updates);
1132
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, {
1133
+ signInMethod: response.providerId,
1134
+ rawUserInfo: response.rawUserInfo,
1135
+ signInAttributes: JSON.stringify(signInAttributes),
1136
+ }, oauthTokens);
1137
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
1138
+ user = state.createUserWithLocalId(localId, updates);
1090
1139
  response.localId = user.localId;
1140
+ if (!user.disabled && !isMfaEnabled(state, user)) {
1141
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, {
1142
+ signInMethod: response.providerId,
1143
+ rawUserInfo: response.rawUserInfo,
1144
+ signInAttributes: JSON.stringify(signInAttributes),
1145
+ }, oauthTokens);
1146
+ updates = blockingResponse.updates;
1147
+ extraClaims = blockingResponse.extraClaims;
1148
+ user = state.updateUserByLocalId(user.localId, updates);
1149
+ }
1091
1150
  }
1092
1151
  else {
1093
1152
  if (!response.localId) {
1094
1153
  throw new Error("Internal assertion error: localId not set for exising user.");
1095
1154
  }
1096
- user = state.updateUserByLocalId(response.localId, Object.assign({}, accountUpdates.fields), {
1155
+ const maybeUser = state.getUserByLocalId(response.localId);
1156
+ (0, errors_1.assert)(maybeUser, "USER_NOT_FOUND");
1157
+ user = maybeUser;
1158
+ let updates = Object.assign({}, accountUpdates.fields);
1159
+ if (!user.disabled && !isMfaEnabled(state, user)) {
1160
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, Object.assign(Object.assign({}, user), updates), {
1161
+ signInMethod: response.providerId,
1162
+ rawUserInfo: response.rawUserInfo,
1163
+ signInAttributes: JSON.stringify(signInAttributes),
1164
+ }, oauthTokens);
1165
+ extraClaims = blockingResponse.extraClaims;
1166
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
1167
+ }
1168
+ user = state.updateUserByLocalId(response.localId, updates, {
1097
1169
  upsertProviders: [providerUserInfo],
1098
1170
  });
1099
1171
  }
@@ -1103,17 +1175,16 @@ function signInWithIdp(state, reqBody) {
1103
1175
  if (state instanceof state_1.TenantProjectState) {
1104
1176
  response.tenantId = state.tenantId;
1105
1177
  }
1106
- if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
1107
- ((_c = user.mfaInfo) === null || _c === void 0 ? void 0 : _c.length)) {
1178
+ if (isMfaEnabled(state, user)) {
1108
1179
  return Object.assign(Object.assign({}, response), mfaPending(state, user, providerId));
1109
1180
  }
1110
1181
  else {
1111
1182
  user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
1112
- return Object.assign(Object.assign({}, response), issueTokens(state, user, providerId, { signInAttributes }));
1183
+ (0, errors_1.assert)(!(user === null || user === void 0 ? void 0 : user.disabled), "USER_DISABLED");
1184
+ return Object.assign(Object.assign({}, response), issueTokens(state, user, providerId, { signInAttributes, extraClaims }));
1113
1185
  }
1114
1186
  }
1115
- function signInWithPassword(state, reqBody) {
1116
- var _a;
1187
+ async function signInWithPassword(state, reqBody) {
1117
1188
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
1118
1189
  (0, errors_1.assert)(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
1119
1190
  (0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
@@ -1136,16 +1207,17 @@ function signInWithPassword(state, reqBody) {
1136
1207
  localId: user.localId,
1137
1208
  email,
1138
1209
  };
1139
- if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
1140
- ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length)) {
1210
+ if (isMfaEnabled(state, user)) {
1141
1211
  return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
1142
1212
  }
1143
1213
  else {
1144
- user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
1145
- return Object.assign(Object.assign({}, response), issueTokens(state, user, state_1.PROVIDER_PASSWORD));
1214
+ const { updates, extraClaims } = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "password" });
1215
+ user = state.updateUserByLocalId(user.localId, Object.assign(Object.assign({}, updates), { lastLoginAt: Date.now().toString() }));
1216
+ (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
1217
+ return Object.assign(Object.assign({}, response), issueTokens(state, user, state_1.PROVIDER_PASSWORD, { extraClaims }));
1146
1218
  }
1147
1219
  }
1148
- function signInWithPhoneNumber(state, reqBody) {
1220
+ async function signInWithPhoneNumber(state, reqBody) {
1149
1221
  var _a;
1150
1222
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
1151
1223
  (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
@@ -1161,34 +1233,50 @@ function signInWithPhoneNumber(state, reqBody) {
1161
1233
  (0, errors_1.assert)(reqBody.code, "MISSING_CODE");
1162
1234
  phoneNumber = verifyPhoneNumber(state, reqBody.sessionInfo, reqBody.code);
1163
1235
  }
1164
- let user = state.getUserByPhoneNumber(phoneNumber);
1165
- let isNewUser = false;
1166
- const updates = {
1236
+ const userFromPhoneNumber = state.getUserByPhoneNumber(phoneNumber);
1237
+ const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
1238
+ if (userFromPhoneNumber && userFromIdToken) {
1239
+ if (userFromPhoneNumber.localId !== userFromIdToken.localId) {
1240
+ (0, errors_1.assert)(!reqBody.temporaryProof, "PHONE_NUMBER_EXISTS");
1241
+ return Object.assign({}, state.createTemporaryProof(phoneNumber));
1242
+ }
1243
+ }
1244
+ let user = userFromIdToken || userFromPhoneNumber;
1245
+ const isNewUser = !user;
1246
+ const timestamp = new Date();
1247
+ let updates = {
1167
1248
  phoneNumber,
1168
- lastLoginAt: Date.now().toString(),
1249
+ lastLoginAt: timestamp.getTime().toString(),
1169
1250
  };
1170
- const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
1251
+ let extraClaims;
1171
1252
  if (!user) {
1172
- if (userFromIdToken) {
1173
- (0, errors_1.assert)(!((_a = userFromIdToken.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.");
1174
- user = state.updateUserByLocalId(userFromIdToken.localId, updates);
1175
- }
1176
- else {
1177
- isNewUser = true;
1178
- user = state.createUser(updates);
1253
+ updates.createdAt = timestamp.getTime().toString();
1254
+ const localId = state.generateLocalId();
1255
+ const userBeforeCreate = Object.assign({ localId }, updates);
1256
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, { signInMethod: "phone" });
1257
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
1258
+ user = state.createUserWithLocalId(localId, updates);
1259
+ if (!user.disabled) {
1260
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: "phone" });
1261
+ updates = blockingResponse.updates;
1262
+ extraClaims = blockingResponse.extraClaims;
1263
+ user = state.updateUserByLocalId(user.localId, updates);
1179
1264
  }
1180
1265
  }
1181
1266
  else {
1182
1267
  (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
1183
- if (userFromIdToken && userFromIdToken.localId !== user.localId) {
1184
- if (!reqBody.temporaryProof) {
1185
- return Object.assign({}, state.createTemporaryProof(phoneNumber));
1186
- }
1187
- throw new errors_1.BadRequestError("PHONE_NUMBER_EXISTS");
1268
+ (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.");
1269
+ if (!user.disabled) {
1270
+ const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, Object.assign(Object.assign({}, user), updates), { signInMethod: "phone" });
1271
+ updates = Object.assign(Object.assign({}, updates), blockingResponse.updates);
1272
+ extraClaims = blockingResponse.extraClaims;
1188
1273
  }
1189
1274
  user = state.updateUserByLocalId(user.localId, updates);
1190
1275
  }
1191
- const tokens = issueTokens(state, user, state_1.PROVIDER_PHONE);
1276
+ (0, errors_1.assert)(!(user === null || user === void 0 ? void 0 : user.disabled), "USER_DISABLED");
1277
+ const tokens = issueTokens(state, user, state_1.PROVIDER_PHONE, {
1278
+ extraClaims,
1279
+ });
1192
1280
  return Object.assign({ isNewUser,
1193
1281
  phoneNumber, localId: user.localId }, tokens);
1194
1282
  }
@@ -1336,7 +1424,7 @@ function mfaSignInStart(state, reqBody) {
1336
1424
  },
1337
1425
  };
1338
1426
  }
1339
- function mfaSignInFinalize(state, reqBody) {
1427
+ async function mfaSignInFinalize(state, reqBody) {
1340
1428
  var _a, _b;
1341
1429
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
1342
1430
  (0, errors_1.assert)((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
@@ -1352,10 +1440,12 @@ function mfaSignInFinalize(state, reqBody) {
1352
1440
  const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
1353
1441
  let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
1354
1442
  const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber);
1443
+ const { updates, extraClaims } = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: signInProvider, signInSecondFactor: "phone" });
1444
+ user = state.updateUserByLocalId(user.localId, Object.assign(Object.assign({}, updates), { lastLoginAt: Date.now().toString() }));
1355
1445
  (0, errors_1.assert)(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
1356
- user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
1357
1446
  (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
1358
1447
  const { idToken, refreshToken } = issueTokens(state, user, signInProvider, {
1448
+ extraClaims,
1359
1449
  secondFactor: { identifier: enrollment.mfaEnrollmentId, provider: state_1.PROVIDER_PHONE },
1360
1450
  });
1361
1451
  return {
@@ -1363,7 +1453,7 @@ function mfaSignInFinalize(state, reqBody) {
1363
1453
  refreshToken,
1364
1454
  };
1365
1455
  }
1366
- function getConfig(state, reqBody, ctx) {
1456
+ function getConfig(state) {
1367
1457
  (0, errors_1.assert)(state instanceof state_1.AgentProjectState, "((Can only get top-level configurations on agent projects.))");
1368
1458
  return state.config;
1369
1459
  }
@@ -1844,12 +1934,12 @@ function listTenants(state, reqBody, ctx) {
1844
1934
  tenants,
1845
1935
  };
1846
1936
  }
1847
- function deleteTenant(state, reqBody, ctx) {
1937
+ function deleteTenant(state) {
1848
1938
  (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Can only delete tenant on tenant projects.))");
1849
1939
  state.delete();
1850
1940
  return {};
1851
1941
  }
1852
- function getTenant(state, reqBody, ctx) {
1942
+ function getTenant(state) {
1853
1943
  (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Can only get tenant on tenant projects.))");
1854
1944
  return state.tenantConfig;
1855
1945
  }
@@ -1857,3 +1947,185 @@ function updateTenant(state, reqBody, ctx) {
1857
1947
  (0, errors_1.assert)(state instanceof state_1.TenantProjectState, "((Can only update tenant on tenant projects.))");
1858
1948
  return state.updateTenant(reqBody, ctx.params.query.updateMask);
1859
1949
  }
1950
+ function isMfaEnabled(state, user) {
1951
+ var _a;
1952
+ return ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") &&
1953
+ ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length));
1954
+ }
1955
+ async function fetchBlockingFunction(state, event, user, options = {}, oauthTokens = {}, timeoutMs = 60000) {
1956
+ const url = state.getBlockingFunctionUri(event);
1957
+ if (!url) {
1958
+ return { updates: {} };
1959
+ }
1960
+ const jwt = generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options, oauthTokens);
1961
+ const reqBody = {
1962
+ data: {
1963
+ jwt,
1964
+ },
1965
+ };
1966
+ const controller = new abort_controller_1.default();
1967
+ const timeout = setTimeout(() => {
1968
+ controller.abort();
1969
+ }, timeoutMs);
1970
+ let response;
1971
+ try {
1972
+ const res = await (0, node_fetch_1.default)(url, {
1973
+ method: "POST",
1974
+ headers: { "Content-Type": "application/json" },
1975
+ body: JSON.stringify(reqBody),
1976
+ signal: controller.signal,
1977
+ });
1978
+ const text = await res.text();
1979
+ (0, errors_1.assert)(res.ok, `BLOCKING_FUNCTION_ERROR_RESPONSE: ((HTTP request to ${url} returned HTTP error${res.status}: ${text}))`);
1980
+ response = JSON.parse(text);
1981
+ }
1982
+ catch (thrown) {
1983
+ const err = thrown instanceof Error ? thrown : new Error(thrown);
1984
+ const isAbortError = err.name.includes("AbortError");
1985
+ if (isAbortError) {
1986
+ throw new errors_1.InternalError(`BLOCKING_FUNCTION_ERROR_RESPONSE: ((Deadline exceeded making request to ${url}.))`, err.message);
1987
+ }
1988
+ throw new errors_1.InternalError(`BLOCKING_FUNCTION_ERROR_RESPONSE: ((Failed to make request to ${url}.))`, err.message);
1989
+ }
1990
+ finally {
1991
+ clearTimeout(timeout);
1992
+ }
1993
+ return processBlockingFunctionResponse(event, response);
1994
+ }
1995
+ function processBlockingFunctionResponse(event, response) {
1996
+ let extraClaims;
1997
+ const updates = {};
1998
+ if (response.userRecord) {
1999
+ const userRecord = response.userRecord;
2000
+ (0, errors_1.assert)(userRecord.updateMask, "BLOCKING_FUNCTION_ERROR_RESPONSE: ((Response UserRecord is missing updateMask.))");
2001
+ const mask = userRecord.updateMask;
2002
+ const fields = mask.split(",");
2003
+ for (const field of fields) {
2004
+ switch (field) {
2005
+ case "displayName":
2006
+ case "photoUrl":
2007
+ updates[field] = coercePrimitiveToString(userRecord[field]);
2008
+ break;
2009
+ case "disabled":
2010
+ case "emailVerified":
2011
+ updates[field] = !!userRecord[field];
2012
+ break;
2013
+ case "customClaims":
2014
+ validateSerializedCustomClaims(userRecord.customClaims);
2015
+ updates.customAttributes = userRecord.customClaims;
2016
+ break;
2017
+ case "sessionClaims":
2018
+ if (event !== state_1.BlockingFunctionEvents.BEFORE_SIGN_IN) {
2019
+ break;
2020
+ }
2021
+ try {
2022
+ extraClaims = JSON.parse(userRecord.sessionClaims);
2023
+ }
2024
+ catch (_a) {
2025
+ throw new errors_1.BadRequestError("BLOCKING_FUNCTION_ERROR_RESPONSE: ((Response has malformed session claims.))");
2026
+ }
2027
+ break;
2028
+ default:
2029
+ break;
2030
+ }
2031
+ }
2032
+ }
2033
+ return { updates, extraClaims };
2034
+ }
2035
+ function generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options, oauthTokens) {
2036
+ const issuedAt = (0, utils_1.toUnixTimestamp)(new Date());
2037
+ const jwt = {
2038
+ iss: `https://securetoken.google.com/${state.projectId}`,
2039
+ aud: url,
2040
+ iat: issuedAt,
2041
+ exp: issuedAt + timeoutMs / 100,
2042
+ event_id: (0, utils_1.randomBase64UrlStr)(16),
2043
+ event_type: event,
2044
+ user_agent: "NotYetSupportedInFirebaseAuthEmulator",
2045
+ ip_address: "127.0.0.1",
2046
+ locale: "en",
2047
+ user_record: {
2048
+ uid: user.localId,
2049
+ email: user.email,
2050
+ email_verified: user.emailVerified,
2051
+ display_name: user.displayName,
2052
+ photo_url: user.photoUrl,
2053
+ disabled: user.disabled,
2054
+ phone_number: user.phoneNumber,
2055
+ custom_claims: user.customAttributes,
2056
+ },
2057
+ sub: user.localId,
2058
+ sign_in_method: options.signInMethod,
2059
+ sign_in_second_factor: options.signInSecondFactor,
2060
+ sign_in_attributes: options.signInAttributes,
2061
+ raw_user_info: options.rawUserInfo,
2062
+ };
2063
+ if (state instanceof state_1.TenantProjectState) {
2064
+ jwt.tenant_id = state.tenantId;
2065
+ jwt.user_record.tenant_id = state.tenantId;
2066
+ }
2067
+ const provider_data = [];
2068
+ if (user.providerUserInfo) {
2069
+ for (const providerUserInfo of user.providerUserInfo) {
2070
+ const provider = {
2071
+ provider_id: providerUserInfo.providerId,
2072
+ display_name: providerUserInfo.displayName,
2073
+ photo_url: providerUserInfo.photoUrl,
2074
+ email: providerUserInfo.email,
2075
+ uid: providerUserInfo.rawId,
2076
+ phone_number: providerUserInfo.phoneNumber,
2077
+ };
2078
+ provider_data.push(provider);
2079
+ }
2080
+ }
2081
+ jwt.user_record.provider_data = provider_data;
2082
+ if (user.mfaInfo) {
2083
+ const enrolled_factors = [];
2084
+ for (const mfaEnrollment of user.mfaInfo) {
2085
+ if (!mfaEnrollment.mfaEnrollmentId) {
2086
+ continue;
2087
+ }
2088
+ const enrolledFactor = {
2089
+ uid: mfaEnrollment.mfaEnrollmentId,
2090
+ display_name: mfaEnrollment.displayName,
2091
+ enrollment_time: mfaEnrollment.enrolledAt,
2092
+ phone_number: mfaEnrollment.phoneInfo,
2093
+ factor_id: state_1.PROVIDER_PHONE,
2094
+ };
2095
+ enrolled_factors.push(enrolledFactor);
2096
+ }
2097
+ jwt.user_record.multi_factor = {
2098
+ enrolled_factors,
2099
+ };
2100
+ }
2101
+ if (user.lastLoginAt || user.createdAt) {
2102
+ jwt.user_record.metadata = {
2103
+ last_sign_in_time: user.lastLoginAt,
2104
+ creation_time: user.createdAt,
2105
+ };
2106
+ }
2107
+ if (state.shouldForwardCredentialToBlockingFunction("accessToken")) {
2108
+ jwt.oauth_access_token = oauthTokens.oauthAccessToken;
2109
+ jwt.oauth_token_secret = oauthTokens.oauthTokenSecret;
2110
+ jwt.oauth_expires_in = oauthTokens.oauthExpiresIn;
2111
+ }
2112
+ if (state.shouldForwardCredentialToBlockingFunction("idToken")) {
2113
+ jwt.oauth_id_token = oauthTokens.oauthIdToken;
2114
+ }
2115
+ if (state.shouldForwardCredentialToBlockingFunction("refreshToken")) {
2116
+ jwt.oauth_refresh_token = oauthTokens.oauthRefreshToken;
2117
+ }
2118
+ const jwtStr = (0, jsonwebtoken_1.sign)(jwt, "", {
2119
+ algorithm: "none",
2120
+ });
2121
+ return jwtStr;
2122
+ }
2123
+ function parseBlockingFunctionJwt(jwt) {
2124
+ const decoded = (0, jsonwebtoken_1.decode)(jwt, { json: true });
2125
+ (0, errors_1.assert)(decoded, "((Invalid blocking function jwt.))");
2126
+ (0, errors_1.assert)(decoded.iss, "((Invalid blocking function jwt, missing `iss` claim.))");
2127
+ (0, errors_1.assert)(decoded.aud, "((Invalid blocking function jwt, missing `aud` claim.))");
2128
+ (0, errors_1.assert)(decoded.user_record, "((Invalid blocking function jwt, missing `user_record` claim.))");
2129
+ return decoded;
2130
+ }
2131
+ exports.parseBlockingFunctionJwt = parseBlockingFunctionJwt;
@@ -381,12 +381,12 @@ function wrapValidateBody(pluginContext) {
381
381
  if (op.validateBody && !op._authEmulatorValidateBodyWrapped) {
382
382
  const validateBody = op.validateBody.bind(op);
383
383
  op.validateBody = (body) => {
384
- return validateAndFixRestMappingRequestBody(validateBody, body, pluginContext.api);
384
+ return validateAndFixRestMappingRequestBody(validateBody, body);
385
385
  };
386
386
  op._authEmulatorValidateBodyWrapped = true;
387
387
  }
388
388
  }
389
- function validateAndFixRestMappingRequestBody(validate, body, api) {
389
+ function validateAndFixRestMappingRequestBody(validate, body) {
390
390
  var _a;
391
391
  body = convertKeysToCamelCase(body);
392
392
  let result;