firebase-tools 9.19.0 → 9.20.0
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.
- package/CHANGELOG.md +1 -3
- package/lib/api.js +1 -0
- package/lib/commands/crashlytics-symbols-upload.js +146 -0
- package/lib/commands/ext-configure.js +2 -0
- package/lib/commands/ext-install.js +23 -2
- package/lib/commands/ext-uninstall.js +6 -0
- package/lib/commands/ext-update.js +9 -2
- package/lib/commands/functions-config-export.js +115 -0
- package/lib/commands/functions-delete.js +44 -35
- package/lib/commands/functions-list.js +1 -1
- package/lib/commands/index.js +8 -0
- package/lib/deploy/functions/backend.js +13 -4
- package/lib/deploy/functions/prepare.js +3 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +13 -1
- package/lib/deploy/functions/triggerRegionHelper.js +32 -0
- package/lib/downloadUtils.js +37 -0
- package/lib/emulator/auth/apiSpec.js +513 -1
- package/lib/emulator/auth/handlers.js +4 -3
- package/lib/emulator/auth/operations.js +144 -14
- package/lib/emulator/auth/server.js +10 -13
- package/lib/emulator/auth/state.js +132 -13
- package/lib/emulator/download.js +2 -31
- package/lib/emulator/functionsEmulatorRuntime.js +29 -7
- package/lib/extensions/askUserForConsent.js +14 -1
- package/lib/extensions/askUserForParam.js +72 -3
- package/lib/extensions/extensionsApi.js +1 -0
- package/lib/extensions/extensionsHelper.js +1 -0
- package/lib/extensions/paramHelper.js +3 -2
- package/lib/extensions/secretsUtils.js +58 -0
- package/lib/functional.js +8 -1
- package/lib/functions/env.js +10 -4
- package/lib/functions/runtimeConfigExport.js +137 -0
- package/lib/gcp/cloudfunctions.js +4 -2
- package/lib/gcp/cloudfunctionsv2.js +6 -0
- package/lib/gcp/secretManager.js +111 -0
- package/lib/gcp/storage.js +16 -0
- package/lib/previews.js +1 -1
- package/lib/requireInteractive.js +12 -0
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ const errors_1 = require("./errors");
|
|
|
7
7
|
const widget_ui_1 = require("./widget_ui");
|
|
8
8
|
function registerHandlers(app, getProjectStateByApiKey) {
|
|
9
9
|
app.get(`/emulator/action`, (req, res) => {
|
|
10
|
-
const { mode, oobCode, continueUrl, apiKey } = req.query;
|
|
10
|
+
const { mode, oobCode, continueUrl, apiKey, tenantId } = req.query;
|
|
11
11
|
if (!apiKey) {
|
|
12
12
|
return res.status(400).json({
|
|
13
13
|
authEmulator: {
|
|
@@ -24,7 +24,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
|
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
const state = getProjectStateByApiKey(apiKey);
|
|
27
|
+
const state = getProjectStateByApiKey(apiKey, tenantId);
|
|
28
28
|
switch (mode) {
|
|
29
29
|
case "recoverEmail": {
|
|
30
30
|
const oob = state.validateOobCode(oobCode);
|
|
@@ -159,6 +159,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
|
|
|
159
159
|
res.set("Content-Type", "text/html; charset=utf-8");
|
|
160
160
|
const apiKey = req.query.apiKey;
|
|
161
161
|
const providerId = req.query.providerId;
|
|
162
|
+
const tenantId = req.query.tenantId;
|
|
162
163
|
if (!apiKey || !providerId) {
|
|
163
164
|
return res.status(400).json({
|
|
164
165
|
authEmulator: {
|
|
@@ -166,7 +167,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
|
|
|
166
167
|
},
|
|
167
168
|
});
|
|
168
169
|
}
|
|
169
|
-
const state = getProjectStateByApiKey(apiKey);
|
|
170
|
+
const state = getProjectStateByApiKey(apiKey, tenantId);
|
|
170
171
|
const providerInfos = state.listProviderInfosByProviderId(providerId);
|
|
171
172
|
const options = providerInfos
|
|
172
173
|
.map((info) => `<li class="js-reuse-account mdc-list-item mdc-ripple-upgraded" tabindex="0" data-id-token="${encodeURIComponent(createFakeClaims(info))}">
|
|
@@ -50,6 +50,25 @@ exports.authOperations = {
|
|
|
50
50
|
batchDelete,
|
|
51
51
|
batchGet,
|
|
52
52
|
},
|
|
53
|
+
tenants: {
|
|
54
|
+
create: createTenant,
|
|
55
|
+
delete: deleteTenant,
|
|
56
|
+
get: getTenant,
|
|
57
|
+
list: listTenants,
|
|
58
|
+
patch: updateTenant,
|
|
59
|
+
createSessionCookie,
|
|
60
|
+
accounts: {
|
|
61
|
+
_: signUp,
|
|
62
|
+
batchCreate,
|
|
63
|
+
batchDelete,
|
|
64
|
+
batchGet,
|
|
65
|
+
delete: deleteAccount,
|
|
66
|
+
lookup,
|
|
67
|
+
query: queryAccounts,
|
|
68
|
+
sendOobCode,
|
|
69
|
+
update: setAccountInfo,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
53
72
|
},
|
|
54
73
|
},
|
|
55
74
|
securetoken: {
|
|
@@ -83,6 +102,7 @@ const MFA_INELIGIBLE_PROVIDER = new Set([
|
|
|
83
102
|
]);
|
|
84
103
|
function signUp(state, reqBody, ctx) {
|
|
85
104
|
var _a;
|
|
105
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
86
106
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
87
107
|
let provider;
|
|
88
108
|
const updates = {
|
|
@@ -115,9 +135,11 @@ function signUp(state, reqBody, ctx) {
|
|
|
115
135
|
errors_1.assert(reqBody.email, "MISSING_EMAIL");
|
|
116
136
|
errors_1.assert(reqBody.password, "MISSING_PASSWORD");
|
|
117
137
|
provider = state_1.PROVIDER_PASSWORD;
|
|
138
|
+
errors_1.assert(state.allowPasswordSignup, "OPERATION_NOT_ALLOWED");
|
|
118
139
|
}
|
|
119
140
|
else {
|
|
120
141
|
provider = state_1.PROVIDER_ANONYMOUS;
|
|
142
|
+
errors_1.assert(state.enableAnonymousUser, "ADMIN_ONLY_OPERATION");
|
|
121
143
|
}
|
|
122
144
|
}
|
|
123
145
|
if (reqBody.email) {
|
|
@@ -138,6 +160,9 @@ function signUp(state, reqBody, ctx) {
|
|
|
138
160
|
generateEnrollmentIds: true,
|
|
139
161
|
});
|
|
140
162
|
}
|
|
163
|
+
if (reqBody.tenantId) {
|
|
164
|
+
updates.tenantId = reqBody.tenantId;
|
|
165
|
+
}
|
|
141
166
|
let user;
|
|
142
167
|
if (reqBody.idToken) {
|
|
143
168
|
({ user } = parseIdToken(state, reqBody.idToken));
|
|
@@ -158,6 +183,7 @@ function signUp(state, reqBody, ctx) {
|
|
|
158
183
|
}
|
|
159
184
|
function lookup(state, reqBody, ctx) {
|
|
160
185
|
var _a, _b, _c, _d, _e;
|
|
186
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
161
187
|
const seenLocalIds = new Set();
|
|
162
188
|
const users = [];
|
|
163
189
|
function tryAddUser(maybeUser) {
|
|
@@ -198,6 +224,7 @@ function lookup(state, reqBody, ctx) {
|
|
|
198
224
|
}
|
|
199
225
|
function batchCreate(state, reqBody) {
|
|
200
226
|
var _a, _b;
|
|
227
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
201
228
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
202
229
|
errors_1.assert((_a = reqBody.users) === null || _a === void 0 ? void 0 : _a.length, "MISSING_USER_ACCOUNT");
|
|
203
230
|
if (reqBody.sanityCheck) {
|
|
@@ -238,6 +265,10 @@ function batchCreate(state, reqBody) {
|
|
|
238
265
|
photoUrl: userInfo.photoUrl,
|
|
239
266
|
lastLoginAt: userInfo.lastLoginAt,
|
|
240
267
|
};
|
|
268
|
+
if (userInfo.tenantId) {
|
|
269
|
+
errors_1.assert(state instanceof state_1.TenantProjectState && state.tenantId === userInfo.tenantId, "Tenant id in userInfo does not match the tenant id in request.");
|
|
270
|
+
fields.tenantId = state.tenantId;
|
|
271
|
+
}
|
|
241
272
|
if (userInfo.passwordHash) {
|
|
242
273
|
fields.passwordHash = userInfo.passwordHash;
|
|
243
274
|
fields.salt = userInfo.salt;
|
|
@@ -369,6 +400,7 @@ function batchDelete(state, reqBody) {
|
|
|
369
400
|
return { errors: errors.length ? errors : undefined };
|
|
370
401
|
}
|
|
371
402
|
function batchGet(state, reqBody, ctx) {
|
|
403
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
372
404
|
const maxResults = Math.min(Math.floor(ctx.params.query.maxResults) || 20, 1000);
|
|
373
405
|
const users = state.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
|
|
374
406
|
let newPageToken = undefined;
|
|
@@ -386,6 +418,7 @@ function batchGet(state, reqBody, ctx) {
|
|
|
386
418
|
}
|
|
387
419
|
function createAuthUri(state, reqBody) {
|
|
388
420
|
var _a;
|
|
421
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
389
422
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
390
423
|
const sessionId = reqBody.sessionId || utils_1.randomId(27);
|
|
391
424
|
if (reqBody.providerId) {
|
|
@@ -461,6 +494,7 @@ function createSessionCookie(state, reqBody, ctx) {
|
|
|
461
494
|
}
|
|
462
495
|
function deleteAccount(state, reqBody, ctx) {
|
|
463
496
|
var _a;
|
|
497
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
464
498
|
let user;
|
|
465
499
|
if ((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2) {
|
|
466
500
|
errors_1.assert(reqBody.localId, "MISSING_LOCAL_ID");
|
|
@@ -478,6 +512,8 @@ function deleteAccount(state, reqBody, ctx) {
|
|
|
478
512
|
};
|
|
479
513
|
}
|
|
480
514
|
function getProjects(state) {
|
|
515
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
516
|
+
errors_1.assert(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
|
|
481
517
|
return {
|
|
482
518
|
projectId: state.projectNumber,
|
|
483
519
|
authorizedDomains: [
|
|
@@ -485,7 +521,8 @@ function getProjects(state) {
|
|
|
485
521
|
],
|
|
486
522
|
};
|
|
487
523
|
}
|
|
488
|
-
function getRecaptchaParams() {
|
|
524
|
+
function getRecaptchaParams(state) {
|
|
525
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
489
526
|
return {
|
|
490
527
|
kind: "identitytoolkit#GetRecaptchaParamResponse",
|
|
491
528
|
recaptchaStoken: "This-is-a-fake-token__Dont-send-this-to-the-Recaptcha-service__The-Auth-Emulator-does-not-support-Recaptcha",
|
|
@@ -494,6 +531,7 @@ function getRecaptchaParams() {
|
|
|
494
531
|
}
|
|
495
532
|
function queryAccounts(state, reqBody) {
|
|
496
533
|
var _a;
|
|
534
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
497
535
|
if ((_a = reqBody.expression) === null || _a === void 0 ? void 0 : _a.length) {
|
|
498
536
|
throw new errors_1.NotImplementedError("expression is not implemented.");
|
|
499
537
|
}
|
|
@@ -530,7 +568,9 @@ function queryAccounts(state, reqBody) {
|
|
|
530
568
|
}
|
|
531
569
|
function resetPassword(state, reqBody) {
|
|
532
570
|
var _a;
|
|
571
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
533
572
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
573
|
+
errors_1.assert(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
|
|
534
574
|
errors_1.assert(reqBody.oobCode, "MISSING_OOB_CODE");
|
|
535
575
|
const oob = state.validateOobCode(reqBody.oobCode);
|
|
536
576
|
errors_1.assert(oob, "INVALID_OOB_CODE");
|
|
@@ -559,6 +599,7 @@ function resetPassword(state, reqBody) {
|
|
|
559
599
|
exports.resetPassword = resetPassword;
|
|
560
600
|
function sendOobCode(state, reqBody, ctx) {
|
|
561
601
|
var _a;
|
|
602
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
562
603
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
563
604
|
errors_1.assert(reqBody.requestType && reqBody.requestType !== "OOB_REQ_TYPE_UNSPECIFIED", "MISSING_REQ_TYPE");
|
|
564
605
|
if (reqBody.returnOobLink) {
|
|
@@ -571,6 +612,7 @@ function sendOobCode(state, reqBody, ctx) {
|
|
|
571
612
|
let mode;
|
|
572
613
|
switch (reqBody.requestType) {
|
|
573
614
|
case "EMAIL_SIGNIN":
|
|
615
|
+
errors_1.assert(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
|
|
574
616
|
mode = "signIn";
|
|
575
617
|
errors_1.assert(reqBody.email, "MISSING_EMAIL");
|
|
576
618
|
email = utils_1.canonicalizeEmailAddress(reqBody.email);
|
|
@@ -625,6 +667,8 @@ function sendOobCode(state, reqBody, ctx) {
|
|
|
625
667
|
}
|
|
626
668
|
function sendVerificationCode(state, reqBody) {
|
|
627
669
|
var _a;
|
|
670
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
671
|
+
errors_1.assert(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
|
|
628
672
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
629
673
|
errors_1.assert(reqBody.phoneNumber && utils_1.isValidPhoneNumber(reqBody.phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
|
|
630
674
|
const user = state.getUserByPhoneNumber(reqBody.phoneNumber);
|
|
@@ -637,6 +681,7 @@ function sendVerificationCode(state, reqBody) {
|
|
|
637
681
|
}
|
|
638
682
|
function setAccountInfo(state, reqBody, ctx) {
|
|
639
683
|
var _a;
|
|
684
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
640
685
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
641
686
|
const url = utils_1.authEmulatorUrl(ctx.req);
|
|
642
687
|
return setAccountInfoImpl(state, reqBody, {
|
|
@@ -831,6 +876,9 @@ function createOobRecord(state, email, url, params) {
|
|
|
831
876
|
if (params.continueUrl) {
|
|
832
877
|
url.searchParams.set("continueUrl", params.continueUrl);
|
|
833
878
|
}
|
|
879
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
880
|
+
url.searchParams.set("tenantId", state.tenantId);
|
|
881
|
+
}
|
|
834
882
|
return url.toString();
|
|
835
883
|
});
|
|
836
884
|
return oobRecord;
|
|
@@ -859,6 +907,7 @@ function logOobMessage(oobRecord) {
|
|
|
859
907
|
}
|
|
860
908
|
function signInWithCustomToken(state, reqBody) {
|
|
861
909
|
var _a;
|
|
910
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
862
911
|
errors_1.assert(reqBody.token, "MISSING_CUSTOM_TOKEN");
|
|
863
912
|
let payload;
|
|
864
913
|
if (reqBody.token.startsWith("{")) {
|
|
@@ -871,6 +920,9 @@ function signInWithCustomToken(state, reqBody) {
|
|
|
871
920
|
}
|
|
872
921
|
else {
|
|
873
922
|
const decoded = jsonwebtoken_1.decode(reqBody.token, { complete: true });
|
|
923
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
924
|
+
errors_1.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.tenantId) === state.tenantId, "TENANT_ID_MISMATCH");
|
|
925
|
+
}
|
|
874
926
|
errors_1.assert(decoded, "INVALID_CUSTOM_TOKEN : Invalid assertion format");
|
|
875
927
|
if (decoded.header.alg !== "none") {
|
|
876
928
|
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");
|
|
@@ -891,6 +943,7 @@ function signInWithCustomToken(state, reqBody) {
|
|
|
891
943
|
const updates = {
|
|
892
944
|
customAuth: true,
|
|
893
945
|
lastLoginAt: Date.now().toString(),
|
|
946
|
+
tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined,
|
|
894
947
|
};
|
|
895
948
|
if (user) {
|
|
896
949
|
errors_1.assert(!user.disabled, "USER_DISABLED");
|
|
@@ -906,6 +959,8 @@ function signInWithCustomToken(state, reqBody) {
|
|
|
906
959
|
}
|
|
907
960
|
function signInWithEmailLink(state, reqBody) {
|
|
908
961
|
var _a;
|
|
962
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
963
|
+
errors_1.assert(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
|
|
909
964
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
910
965
|
const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
|
|
911
966
|
errors_1.assert(reqBody.email, "MISSING_EMAIL");
|
|
@@ -920,6 +975,9 @@ function signInWithEmailLink(state, reqBody) {
|
|
|
920
975
|
emailVerified: true,
|
|
921
976
|
emailLinkSignin: true,
|
|
922
977
|
};
|
|
978
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
979
|
+
updates.tenantId = state.tenantId;
|
|
980
|
+
}
|
|
923
981
|
let user = state.getUserByEmail(email);
|
|
924
982
|
const isNewUser = !user && !userFromIdToken;
|
|
925
983
|
if (!user) {
|
|
@@ -941,7 +999,7 @@ function signInWithEmailLink(state, reqBody) {
|
|
|
941
999
|
localId: user.localId,
|
|
942
1000
|
isNewUser,
|
|
943
1001
|
};
|
|
944
|
-
if ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length) {
|
|
1002
|
+
if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
945
1003
|
return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
|
|
946
1004
|
}
|
|
947
1005
|
else {
|
|
@@ -951,6 +1009,7 @@ function signInWithEmailLink(state, reqBody) {
|
|
|
951
1009
|
}
|
|
952
1010
|
function signInWithIdp(state, reqBody) {
|
|
953
1011
|
var _a, _b;
|
|
1012
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
954
1013
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
955
1014
|
if (reqBody.returnRefreshToken) {
|
|
956
1015
|
throw new errors_1.NotImplementedError("returnRefreshToken is not implemented yet.");
|
|
@@ -1023,7 +1082,7 @@ function signInWithIdp(state, reqBody) {
|
|
|
1023
1082
|
};
|
|
1024
1083
|
let user;
|
|
1025
1084
|
if (response.isNewUser) {
|
|
1026
|
-
user = state.createUser(Object.assign(Object.assign({}, accountUpdates.fields), { lastLoginAt: Date.now().toString(), providerUserInfo: [providerUserInfo] }));
|
|
1085
|
+
user = state.createUser(Object.assign(Object.assign({}, accountUpdates.fields), { lastLoginAt: Date.now().toString(), providerUserInfo: [providerUserInfo], tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined }));
|
|
1027
1086
|
response.localId = user.localId;
|
|
1028
1087
|
}
|
|
1029
1088
|
else {
|
|
@@ -1037,7 +1096,10 @@ function signInWithIdp(state, reqBody) {
|
|
|
1037
1096
|
if (user.email === response.email) {
|
|
1038
1097
|
response.emailVerified = user.emailVerified;
|
|
1039
1098
|
}
|
|
1040
|
-
if (
|
|
1099
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
1100
|
+
response.tenantId = state.tenantId;
|
|
1101
|
+
}
|
|
1102
|
+
if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
1041
1103
|
return Object.assign(Object.assign({}, response), mfaPending(state, user, providerId));
|
|
1042
1104
|
}
|
|
1043
1105
|
else {
|
|
@@ -1047,6 +1109,8 @@ function signInWithIdp(state, reqBody) {
|
|
|
1047
1109
|
}
|
|
1048
1110
|
function signInWithPassword(state, reqBody) {
|
|
1049
1111
|
var _a;
|
|
1112
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1113
|
+
errors_1.assert(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
|
|
1050
1114
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
1051
1115
|
errors_1.assert(reqBody.email, "MISSING_EMAIL");
|
|
1052
1116
|
errors_1.assert(reqBody.password, "MISSING_PASSWORD");
|
|
@@ -1068,7 +1132,7 @@ function signInWithPassword(state, reqBody) {
|
|
|
1068
1132
|
localId: user.localId,
|
|
1069
1133
|
email,
|
|
1070
1134
|
};
|
|
1071
|
-
if ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length) {
|
|
1135
|
+
if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
1072
1136
|
return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
|
|
1073
1137
|
}
|
|
1074
1138
|
else {
|
|
@@ -1078,6 +1142,8 @@ function signInWithPassword(state, reqBody) {
|
|
|
1078
1142
|
}
|
|
1079
1143
|
function signInWithPhoneNumber(state, reqBody) {
|
|
1080
1144
|
var _a;
|
|
1145
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1146
|
+
errors_1.assert(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
|
|
1081
1147
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
1082
1148
|
let phoneNumber;
|
|
1083
1149
|
if (reqBody.temporaryProof) {
|
|
@@ -1191,7 +1257,9 @@ function listVerificationCodesInProject(state) {
|
|
|
1191
1257
|
};
|
|
1192
1258
|
}
|
|
1193
1259
|
function mfaEnrollmentStart(state, reqBody) {
|
|
1194
|
-
var _a;
|
|
1260
|
+
var _a, _b;
|
|
1261
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1262
|
+
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.");
|
|
1195
1263
|
errors_1.assert(reqBody.idToken, "MISSING_ID_TOKEN");
|
|
1196
1264
|
const { user, signInProvider } = parseIdToken(state, reqBody.idToken);
|
|
1197
1265
|
errors_1.assert(!MFA_INELIGIBLE_PROVIDER.has(signInProvider), "UNSUPPORTED_FIRST_FACTOR : MFA is not available for the given first factor.");
|
|
@@ -1199,7 +1267,7 @@ function mfaEnrollmentStart(state, reqBody) {
|
|
|
1199
1267
|
errors_1.assert(reqBody.phoneEnrollmentInfo, "INVALID_ARGUMENT : ((Missing phoneEnrollmentInfo.))");
|
|
1200
1268
|
const phoneNumber = reqBody.phoneEnrollmentInfo.phoneNumber;
|
|
1201
1269
|
errors_1.assert(phoneNumber && utils_1.isValidPhoneNumber(phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
|
|
1202
|
-
errors_1.assert(!((
|
|
1270
|
+
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.");
|
|
1203
1271
|
const { sessionInfo, code } = state.createVerificationCode(phoneNumber);
|
|
1204
1272
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("BULLET", `To enroll MFA with ${phoneNumber}, use the code ${code}.`);
|
|
1205
1273
|
return {
|
|
@@ -1209,7 +1277,9 @@ function mfaEnrollmentStart(state, reqBody) {
|
|
|
1209
1277
|
};
|
|
1210
1278
|
}
|
|
1211
1279
|
function mfaEnrollmentFinalize(state, reqBody) {
|
|
1212
|
-
var _a;
|
|
1280
|
+
var _a, _b;
|
|
1281
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1282
|
+
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.");
|
|
1213
1283
|
errors_1.assert(reqBody.idToken, "MISSING_ID_TOKEN");
|
|
1214
1284
|
let { user, signInProvider } = parseIdToken(state, reqBody.idToken);
|
|
1215
1285
|
errors_1.assert(!MFA_INELIGIBLE_PROVIDER.has(signInProvider), "UNSUPPORTED_FIRST_FACTOR : MFA is not available for the given first factor.");
|
|
@@ -1221,7 +1291,7 @@ function mfaEnrollmentFinalize(state, reqBody) {
|
|
|
1221
1291
|
errors_1.assert(code, "MISSING_CODE");
|
|
1222
1292
|
errors_1.assert(sessionInfo, "MISSING_SESSION_INFO");
|
|
1223
1293
|
const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
|
|
1224
|
-
errors_1.assert(!((
|
|
1294
|
+
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.");
|
|
1225
1295
|
const existingFactors = user.mfaInfo || [];
|
|
1226
1296
|
const existingIds = new Set();
|
|
1227
1297
|
for (const { mfaEnrollmentId } of existingFactors) {
|
|
@@ -1248,6 +1318,7 @@ function mfaEnrollmentFinalize(state, reqBody) {
|
|
|
1248
1318
|
};
|
|
1249
1319
|
}
|
|
1250
1320
|
function mfaEnrollmentWithdraw(state, reqBody) {
|
|
1321
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1251
1322
|
errors_1.assert(reqBody.idToken, "MISSING_ID_TOKEN");
|
|
1252
1323
|
let { user, signInProvider } = parseIdToken(state, reqBody.idToken);
|
|
1253
1324
|
errors_1.assert(user.mfaInfo, "MFA_ENROLLMENT_NOT_FOUND");
|
|
@@ -1257,11 +1328,13 @@ function mfaEnrollmentWithdraw(state, reqBody) {
|
|
|
1257
1328
|
return Object.assign({}, issueTokens(state, user, signInProvider));
|
|
1258
1329
|
}
|
|
1259
1330
|
function mfaSignInStart(state, reqBody) {
|
|
1260
|
-
var _a;
|
|
1331
|
+
var _a, _b;
|
|
1332
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1333
|
+
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.");
|
|
1261
1334
|
errors_1.assert(reqBody.mfaPendingCredential, "MISSING_MFA_PENDING_CREDENTIAL : Request does not have MFA pending credential.");
|
|
1262
1335
|
errors_1.assert(reqBody.mfaEnrollmentId, "MISSING_MFA_ENROLLMENT_ID : No second factor identifier is provided.");
|
|
1263
1336
|
const { user } = parsePendingCredential(state, reqBody.mfaPendingCredential);
|
|
1264
|
-
const enrollment = (
|
|
1337
|
+
const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((factor) => factor.mfaEnrollmentId === reqBody.mfaEnrollmentId);
|
|
1265
1338
|
errors_1.assert(enrollment, "MFA_ENROLLMENT_NOT_FOUND");
|
|
1266
1339
|
const phoneNumber = enrollment.unobfuscatedPhoneInfo;
|
|
1267
1340
|
errors_1.assert(phoneNumber, "INVALID_ARGUMENT : MFA provider not supported!");
|
|
@@ -1274,7 +1347,9 @@ function mfaSignInStart(state, reqBody) {
|
|
|
1274
1347
|
};
|
|
1275
1348
|
}
|
|
1276
1349
|
function mfaSignInFinalize(state, reqBody) {
|
|
1277
|
-
var _a;
|
|
1350
|
+
var _a, _b;
|
|
1351
|
+
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1352
|
+
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.");
|
|
1278
1353
|
errors_1.assert(reqBody.mfaPendingCredential, "MISSING_CREDENTIAL : Please set MFA Pending Credential.");
|
|
1279
1354
|
errors_1.assert(reqBody.phoneVerificationInfo, "INVALID_ARGUMENT : MFA provider not supported!");
|
|
1280
1355
|
if (reqBody.phoneVerificationInfo.androidVerificationProof) {
|
|
@@ -1285,7 +1360,7 @@ function mfaSignInFinalize(state, reqBody) {
|
|
|
1285
1360
|
errors_1.assert(sessionInfo, "MISSING_SESSION_INFO");
|
|
1286
1361
|
const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
|
|
1287
1362
|
let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
|
|
1288
|
-
const enrollment = (
|
|
1363
|
+
const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo == phoneNumber);
|
|
1289
1364
|
errors_1.assert(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
|
|
1290
1365
|
user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
|
|
1291
1366
|
errors_1.assert(!user.disabled, "USER_DISABLED");
|
|
@@ -1317,6 +1392,7 @@ function hashPassword(password, salt) {
|
|
|
1317
1392
|
function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, } = {}) {
|
|
1318
1393
|
user = state.updateUserByLocalId(user.localId, { lastRefreshAt: new Date().toISOString() });
|
|
1319
1394
|
const usageMode = state.usageMode === state_1.UsageMode.PASSTHROUGH ? "passthrough" : undefined;
|
|
1395
|
+
const tenantId = state instanceof state_1.TenantProjectState ? state.tenantId : undefined;
|
|
1320
1396
|
const expiresInSeconds = 60 * 60;
|
|
1321
1397
|
const idToken = generateJwt(user, {
|
|
1322
1398
|
projectId: state.projectId,
|
|
@@ -1325,6 +1401,7 @@ function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, }
|
|
|
1325
1401
|
extraClaims,
|
|
1326
1402
|
secondFactor,
|
|
1327
1403
|
usageMode,
|
|
1404
|
+
tenantId,
|
|
1328
1405
|
});
|
|
1329
1406
|
const refreshToken = state.usageMode === state_1.UsageMode.DEFAULT
|
|
1330
1407
|
? state.createRefreshTokenFor(user, signInProvider, {
|
|
@@ -1345,6 +1422,10 @@ function parseIdToken(state, idToken) {
|
|
|
1345
1422
|
if (decoded.header.alg !== "none") {
|
|
1346
1423
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", "Received a signed JWT. Auth Emulator does not validate JWTs and IS NOT SECURE");
|
|
1347
1424
|
}
|
|
1425
|
+
if (decoded.payload.firebase.tenant) {
|
|
1426
|
+
errors_1.assert(state instanceof state_1.TenantProjectState, "((Parsed token that belongs to tenant in a non-tenant project.))");
|
|
1427
|
+
errors_1.assert(decoded.payload.firebase.tenant === state.tenantId, "TENANT_ID_MISMATCH");
|
|
1428
|
+
}
|
|
1348
1429
|
const user = state.getUserByLocalId(decoded.payload.user_id);
|
|
1349
1430
|
errors_1.assert(user, "USER_NOT_FOUND");
|
|
1350
1431
|
errors_1.assert(!user.validSince || decoded.payload.iat >= Number(user.validSince), "TOKEN_EXPIRED");
|
|
@@ -1352,7 +1433,7 @@ function parseIdToken(state, idToken) {
|
|
|
1352
1433
|
const signInProvider = decoded.payload.firebase.sign_in_provider;
|
|
1353
1434
|
return { user, signInProvider, payload: decoded.payload };
|
|
1354
1435
|
}
|
|
1355
|
-
function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, usageMode, }) {
|
|
1436
|
+
function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, usageMode, tenantId, }) {
|
|
1356
1437
|
const identities = {};
|
|
1357
1438
|
if (user.email) {
|
|
1358
1439
|
identities["email"] = [user.email];
|
|
@@ -1375,6 +1456,7 @@ function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraC
|
|
|
1375
1456
|
second_factor_identifier: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.identifier,
|
|
1376
1457
|
sign_in_second_factor: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.provider,
|
|
1377
1458
|
usage_mode: usageMode,
|
|
1459
|
+
tenant: tenantId,
|
|
1378
1460
|
} });
|
|
1379
1461
|
const jwtStr = jsonwebtoken_1.sign(customPayloadFields, "", {
|
|
1380
1462
|
algorithm: "none",
|
|
@@ -1667,6 +1749,9 @@ function mfaPending(state, user, signInProvider) {
|
|
|
1667
1749
|
signInProvider,
|
|
1668
1750
|
projectId: state.projectId,
|
|
1669
1751
|
};
|
|
1752
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
1753
|
+
pendingCredentialPayload.tenantId = state.tenantId;
|
|
1754
|
+
}
|
|
1670
1755
|
const mfaPendingCredential = Buffer.from(JSON.stringify(pendingCredentialPayload), "utf8").toString("base64");
|
|
1671
1756
|
return { mfaPendingCredential, mfaInfo: user.mfaInfo.map(redactMfaInfo) };
|
|
1672
1757
|
}
|
|
@@ -1704,8 +1789,53 @@ function parsePendingCredential(state, pendingCredential) {
|
|
|
1704
1789
|
}
|
|
1705
1790
|
errors_1.assert(pendingCredentialPayload._AuthEmulatorMfaPendingCredential, "((Invalid phoneVerificationInfo.mfaPendingCredential.))");
|
|
1706
1791
|
errors_1.assert(pendingCredentialPayload.projectId === state.projectId, "INVALID_PROJECT_ID : Project ID does not match MFA pending credential.");
|
|
1792
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
1793
|
+
errors_1.assert(pendingCredentialPayload.tenantId === state.tenantId, "INVALID_PROJECT_ID : Project ID does not match MFA pending credential.");
|
|
1794
|
+
}
|
|
1707
1795
|
const { localId, signInProvider } = pendingCredentialPayload;
|
|
1708
1796
|
const user = state.getUserByLocalId(localId);
|
|
1709
1797
|
errors_1.assert(user, "((User in pendingCredentialPayload does not exist.))");
|
|
1710
1798
|
return { user, signInProvider };
|
|
1711
1799
|
}
|
|
1800
|
+
function createTenant(state, reqBody) {
|
|
1801
|
+
if (!(state instanceof state_1.AgentProjectState)) {
|
|
1802
|
+
throw new errors_1.InternalError("INTERNAL_ERROR: Can only create tenant in agent project", "INTERNAL");
|
|
1803
|
+
}
|
|
1804
|
+
const tenant = {
|
|
1805
|
+
displayName: reqBody.displayName,
|
|
1806
|
+
allowPasswordSignup: reqBody.allowPasswordSignup,
|
|
1807
|
+
enableEmailLinkSignin: reqBody.enableEmailLinkSignin,
|
|
1808
|
+
enableAnonymousUser: reqBody.enableAnonymousUser,
|
|
1809
|
+
disableAuth: reqBody.disableAuth,
|
|
1810
|
+
mfaConfig: reqBody.mfaConfig,
|
|
1811
|
+
tenantId: "",
|
|
1812
|
+
};
|
|
1813
|
+
return state.createTenant(tenant);
|
|
1814
|
+
}
|
|
1815
|
+
function listTenants(state, reqBody, ctx) {
|
|
1816
|
+
errors_1.assert(state instanceof state_1.AgentProjectState, "((Can only list tenants in agent project.))");
|
|
1817
|
+
const pageSize = Math.min(Math.floor(ctx.params.query.pageSize) || 20, 1000);
|
|
1818
|
+
const tenants = state.listTenants(ctx.params.query.pageToken);
|
|
1819
|
+
let nextPageToken = undefined;
|
|
1820
|
+
if (pageSize > 0 && tenants.length >= pageSize) {
|
|
1821
|
+
tenants.length = pageSize;
|
|
1822
|
+
nextPageToken = tenants[tenants.length - 1].tenantId;
|
|
1823
|
+
}
|
|
1824
|
+
return {
|
|
1825
|
+
nextPageToken,
|
|
1826
|
+
tenants,
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
function deleteTenant(state, reqBody, ctx) {
|
|
1830
|
+
errors_1.assert(state instanceof state_1.TenantProjectState, "((Can only delete tenant on tenant projects.))");
|
|
1831
|
+
state.delete();
|
|
1832
|
+
return {};
|
|
1833
|
+
}
|
|
1834
|
+
function getTenant(state, reqBody, ctx) {
|
|
1835
|
+
errors_1.assert(state instanceof state_1.TenantProjectState, "((Can only get tenant on tenant projects.))");
|
|
1836
|
+
return state.tenantConfig;
|
|
1837
|
+
}
|
|
1838
|
+
function updateTenant(state, reqBody, ctx) {
|
|
1839
|
+
errors_1.assert(state instanceof state_1.TenantProjectState, "((Can only update tenant on tenant projects.))");
|
|
1840
|
+
return state.updateTenant(reqBody, ctx.params.query.updateMask);
|
|
1841
|
+
}
|
|
@@ -83,7 +83,7 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
|
|
|
83
83
|
res.json(specWithEmulatorServer(req.protocol, req.headers.host));
|
|
84
84
|
});
|
|
85
85
|
registerLegacyRoutes(app);
|
|
86
|
-
handlers_1.registerHandlers(app, (apiKey) => getProjectStateById(getProjectIdByApiKey(apiKey)));
|
|
86
|
+
handlers_1.registerHandlers(app, (apiKey, tenantId) => getProjectStateById(getProjectIdByApiKey(apiKey), tenantId));
|
|
87
87
|
const apiKeyAuthenticator = (ctx, info) => {
|
|
88
88
|
if (info.in !== "query") {
|
|
89
89
|
throw new Error('apiKey must be defined as in: "query" in API spec.');
|
|
@@ -169,6 +169,9 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
|
|
|
169
169
|
"google-fieldmask"() {
|
|
170
170
|
return true;
|
|
171
171
|
},
|
|
172
|
+
"google-duration"() {
|
|
173
|
+
return true;
|
|
174
|
+
},
|
|
172
175
|
uint64() {
|
|
173
176
|
return true;
|
|
174
177
|
},
|
|
@@ -235,21 +238,15 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
|
|
|
235
238
|
return defaultProjectId;
|
|
236
239
|
}
|
|
237
240
|
function getProjectStateById(projectId, tenantId) {
|
|
238
|
-
let
|
|
239
|
-
if (!
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
projectStateForId.set(projectId, agentProject);
|
|
241
|
+
let agentState = projectStateForId.get(projectId);
|
|
242
|
+
if (!agentState) {
|
|
243
|
+
agentState = new state_1.AgentProjectState(projectId);
|
|
244
|
+
projectStateForId.set(projectId, agentState);
|
|
243
245
|
}
|
|
244
246
|
if (!tenantId) {
|
|
245
|
-
return
|
|
246
|
-
}
|
|
247
|
-
let tenantState = agentProject.tenantProjects.get(tenantId);
|
|
248
|
-
if (!tenantState) {
|
|
249
|
-
tenantState = new state_1.TenantProjectState(projectId, tenantId, agentProject.state);
|
|
250
|
-
agentProject.tenantProjects.set(tenantId, tenantState);
|
|
247
|
+
return agentState;
|
|
251
248
|
}
|
|
252
|
-
return
|
|
249
|
+
return agentState.getTenantProject(tenantId);
|
|
253
250
|
}
|
|
254
251
|
}
|
|
255
252
|
exports.createApp = createApp;
|