1 | /*! firebase-admin v12.0.0 */
|
2 | ;
|
3 | /*!
|
4 | * Copyright 2021 Google Inc.
|
5 | *
|
6 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7 | * you may not use this file except in compliance with the License.
|
8 | * You may obtain a copy of the License at
|
9 | *
|
10 | * http://www.apache.org/licenses/LICENSE-2.0
|
11 | *
|
12 | * Unless required by applicable law or agreed to in writing, software
|
13 | * distributed under the License is distributed on an "AS IS" BASIS,
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15 | * See the License for the specific language governing permissions and
|
16 | * limitations under the License.
|
17 | */
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.BaseAuth = exports.createFirebaseTokenGenerator = void 0;
|
20 | const error_1 = require("../utils/error");
|
21 | const deep_copy_1 = require("../utils/deep-copy");
|
22 | const validator = require("../utils/validator");
|
23 | const auth_api_request_1 = require("./auth-api-request");
|
24 | const token_generator_1 = require("./token-generator");
|
25 | const token_verifier_1 = require("./token-verifier");
|
26 | const auth_config_1 = require("./auth-config");
|
27 | const user_record_1 = require("./user-record");
|
28 | const identifier_1 = require("./identifier");
|
29 | const crypto_signer_1 = require("../utils/crypto-signer");
|
30 | /**
|
31 | * @internal
|
32 | */
|
33 | function createFirebaseTokenGenerator(app, tenantId) {
|
34 | try {
|
35 | const signer = (0, auth_api_request_1.useEmulator)() ? new token_generator_1.EmulatedSigner() : (0, crypto_signer_1.cryptoSignerFromApp)(app);
|
36 | return new token_generator_1.FirebaseTokenGenerator(signer, tenantId);
|
37 | }
|
38 | catch (err) {
|
39 | throw (0, token_generator_1.handleCryptoSignerError)(err);
|
40 | }
|
41 | }
|
42 | exports.createFirebaseTokenGenerator = createFirebaseTokenGenerator;
|
43 | /**
|
44 | * Common parent interface for both `Auth` and `TenantAwareAuth` APIs.
|
45 | */
|
46 | class BaseAuth {
|
47 | /**
|
48 | * The BaseAuth class constructor.
|
49 | *
|
50 | * @param app - The FirebaseApp to associate with this Auth instance.
|
51 | * @param authRequestHandler - The RPC request handler for this instance.
|
52 | * @param tokenGenerator - Optional token generator. If not specified, a
|
53 | * (non-tenant-aware) instance will be created. Use this paramter to
|
54 | * specify a tenant-aware tokenGenerator.
|
55 | * @constructor
|
56 | * @internal
|
57 | */
|
58 | constructor(app,
|
59 | /** @internal */ authRequestHandler, tokenGenerator) {
|
60 | this.authRequestHandler = authRequestHandler;
|
61 | if (tokenGenerator) {
|
62 | this.tokenGenerator = tokenGenerator;
|
63 | }
|
64 | else {
|
65 | this.tokenGenerator = createFirebaseTokenGenerator(app);
|
66 | }
|
67 | this.sessionCookieVerifier = (0, token_verifier_1.createSessionCookieVerifier)(app);
|
68 | this.idTokenVerifier = (0, token_verifier_1.createIdTokenVerifier)(app);
|
69 | this.authBlockingTokenVerifier = (0, token_verifier_1.createAuthBlockingTokenVerifier)(app);
|
70 | }
|
71 | /**
|
72 | * Creates a new Firebase custom token (JWT) that can be sent back to a client
|
73 | * device to use to sign in with the client SDKs' `signInWithCustomToken()`
|
74 | * methods. (Tenant-aware instances will also embed the tenant ID in the
|
75 | * token.)
|
76 | *
|
77 | * See {@link https://firebase.google.com/docs/auth/admin/create-custom-tokens | Create Custom Tokens}
|
78 | * for code samples and detailed documentation.
|
79 | *
|
80 | * @param uid - The `uid` to use as the custom token's subject.
|
81 | * @param developerClaims - Optional additional claims to include
|
82 | * in the custom token's payload.
|
83 | *
|
84 | * @returns A promise fulfilled with a custom token for the
|
85 | * provided `uid` and payload.
|
86 | */
|
87 | createCustomToken(uid, developerClaims) {
|
88 | return this.tokenGenerator.createCustomToken(uid, developerClaims);
|
89 | }
|
90 | /**
|
91 | * Verifies a Firebase ID token (JWT). If the token is valid, the promise is
|
92 | * fulfilled with the token's decoded claims; otherwise, the promise is
|
93 | * rejected.
|
94 | *
|
95 | * If `checkRevoked` is set to true, first verifies whether the corresponding
|
96 | * user is disabled. If yes, an `auth/user-disabled` error is thrown. If no,
|
97 | * verifies if the session corresponding to the ID token was revoked. If the
|
98 | * corresponding user's session was invalidated, an `auth/id-token-revoked`
|
99 | * error is thrown. If not specified the check is not applied.
|
100 | *
|
101 | * See {@link https://firebase.google.com/docs/auth/admin/verify-id-tokens | Verify ID Tokens}
|
102 | * for code samples and detailed documentation.
|
103 | *
|
104 | * @param idToken - The ID token to verify.
|
105 | * @param checkRevoked - Whether to check if the ID token was revoked.
|
106 | * This requires an extra request to the Firebase Auth backend to check
|
107 | * the `tokensValidAfterTime` time for the corresponding user.
|
108 | * When not specified, this additional check is not applied.
|
109 | *
|
110 | * @returns A promise fulfilled with the
|
111 | * token's decoded claims if the ID token is valid; otherwise, a rejected
|
112 | * promise.
|
113 | */
|
114 | verifyIdToken(idToken, checkRevoked = false) {
|
115 | const isEmulator = (0, auth_api_request_1.useEmulator)();
|
116 | return this.idTokenVerifier.verifyJWT(idToken, isEmulator)
|
117 | .then((decodedIdToken) => {
|
118 | // Whether to check if the token was revoked.
|
119 | if (checkRevoked || isEmulator) {
|
120 | return this.verifyDecodedJWTNotRevokedOrDisabled(decodedIdToken, error_1.AuthClientErrorCode.ID_TOKEN_REVOKED);
|
121 | }
|
122 | return decodedIdToken;
|
123 | });
|
124 | }
|
125 | /**
|
126 | * Gets the user data for the user corresponding to a given `uid`.
|
127 | *
|
128 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data | Retrieve user data}
|
129 | * for code samples and detailed documentation.
|
130 | *
|
131 | * @param uid - The `uid` corresponding to the user whose data to fetch.
|
132 | *
|
133 | * @returns A promise fulfilled with the user
|
134 | * data corresponding to the provided `uid`.
|
135 | */
|
136 | getUser(uid) {
|
137 | return this.authRequestHandler.getAccountInfoByUid(uid)
|
138 | .then((response) => {
|
139 | // Returns the user record populated with server response.
|
140 | return new user_record_1.UserRecord(response.users[0]);
|
141 | });
|
142 | }
|
143 | /**
|
144 | * Gets the user data for the user corresponding to a given email.
|
145 | *
|
146 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data | Retrieve user data}
|
147 | * for code samples and detailed documentation.
|
148 | *
|
149 | * @param email - The email corresponding to the user whose data to
|
150 | * fetch.
|
151 | *
|
152 | * @returns A promise fulfilled with the user
|
153 | * data corresponding to the provided email.
|
154 | */
|
155 | getUserByEmail(email) {
|
156 | return this.authRequestHandler.getAccountInfoByEmail(email)
|
157 | .then((response) => {
|
158 | // Returns the user record populated with server response.
|
159 | return new user_record_1.UserRecord(response.users[0]);
|
160 | });
|
161 | }
|
162 | /**
|
163 | * Gets the user data for the user corresponding to a given phone number. The
|
164 | * phone number has to conform to the E.164 specification.
|
165 | *
|
166 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data | Retrieve user data}
|
167 | * for code samples and detailed documentation.
|
168 | *
|
169 | * @param phoneNumber - The phone number corresponding to the user whose
|
170 | * data to fetch.
|
171 | *
|
172 | * @returns A promise fulfilled with the user
|
173 | * data corresponding to the provided phone number.
|
174 | */
|
175 | getUserByPhoneNumber(phoneNumber) {
|
176 | return this.authRequestHandler.getAccountInfoByPhoneNumber(phoneNumber)
|
177 | .then((response) => {
|
178 | // Returns the user record populated with server response.
|
179 | return new user_record_1.UserRecord(response.users[0]);
|
180 | });
|
181 | }
|
182 | /**
|
183 | * Gets the user data for the user corresponding to a given provider id.
|
184 | *
|
185 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data | Retrieve user data}
|
186 | * for code samples and detailed documentation.
|
187 | *
|
188 | * @param providerId - The provider ID, for example, "google.com" for the
|
189 | * Google provider.
|
190 | * @param uid - The user identifier for the given provider.
|
191 | *
|
192 | * @returns A promise fulfilled with the user data corresponding to the
|
193 | * given provider id.
|
194 | */
|
195 | getUserByProviderUid(providerId, uid) {
|
196 | // Although we don't really advertise it, we want to also handle
|
197 | // non-federated idps with this call. So if we detect one of them, we'll
|
198 | // reroute this request appropriately.
|
199 | if (providerId === 'phone') {
|
200 | return this.getUserByPhoneNumber(uid);
|
201 | }
|
202 | else if (providerId === 'email') {
|
203 | return this.getUserByEmail(uid);
|
204 | }
|
205 | return this.authRequestHandler.getAccountInfoByFederatedUid(providerId, uid)
|
206 | .then((response) => {
|
207 | // Returns the user record populated with server response.
|
208 | return new user_record_1.UserRecord(response.users[0]);
|
209 | });
|
210 | }
|
211 | /**
|
212 | * Gets the user data corresponding to the specified identifiers.
|
213 | *
|
214 | * There are no ordering guarantees; in particular, the nth entry in the result list is not
|
215 | * guaranteed to correspond to the nth entry in the input parameters list.
|
216 | *
|
217 | * Only a maximum of 100 identifiers may be supplied. If more than 100 identifiers are supplied,
|
218 | * this method throws a FirebaseAuthError.
|
219 | *
|
220 | * @param identifiers - The identifiers used to indicate which user records should be returned.
|
221 | * Must not have more than 100 entries.
|
222 | * @returns A promise that resolves to the corresponding user records.
|
223 | * @throws FirebaseAuthError If any of the identifiers are invalid or if more than 100
|
224 | * identifiers are specified.
|
225 | */
|
226 | getUsers(identifiers) {
|
227 | if (!validator.isArray(identifiers)) {
|
228 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '`identifiers` parameter must be an array');
|
229 | }
|
230 | return this.authRequestHandler
|
231 | .getAccountInfoByIdentifiers(identifiers)
|
232 | .then((response) => {
|
233 | /**
|
234 | * Checks if the specified identifier is within the list of
|
235 | * UserRecords.
|
236 | */
|
237 | const isUserFound = ((id, userRecords) => {
|
238 | return !!userRecords.find((userRecord) => {
|
239 | if ((0, identifier_1.isUidIdentifier)(id)) {
|
240 | return id.uid === userRecord.uid;
|
241 | }
|
242 | else if ((0, identifier_1.isEmailIdentifier)(id)) {
|
243 | return id.email === userRecord.email;
|
244 | }
|
245 | else if ((0, identifier_1.isPhoneIdentifier)(id)) {
|
246 | return id.phoneNumber === userRecord.phoneNumber;
|
247 | }
|
248 | else if ((0, identifier_1.isProviderIdentifier)(id)) {
|
249 | const matchingUserInfo = userRecord.providerData.find((userInfo) => {
|
250 | return id.providerId === userInfo.providerId;
|
251 | });
|
252 | return !!matchingUserInfo && id.providerUid === matchingUserInfo.uid;
|
253 | }
|
254 | else {
|
255 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'Unhandled identifier type');
|
256 | }
|
257 | });
|
258 | });
|
259 | const users = response.users ? response.users.map((user) => new user_record_1.UserRecord(user)) : [];
|
260 | const notFound = identifiers.filter((id) => !isUserFound(id, users));
|
261 | return { users, notFound };
|
262 | });
|
263 | }
|
264 | /**
|
265 | * Retrieves a list of users (single batch only) with a size of `maxResults`
|
266 | * starting from the offset as specified by `pageToken`. This is used to
|
267 | * retrieve all the users of a specified project in batches.
|
268 | *
|
269 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#list_all_users | List all users}
|
270 | * for code samples and detailed documentation.
|
271 | *
|
272 | * @param maxResults - The page size, 1000 if undefined. This is also
|
273 | * the maximum allowed limit.
|
274 | * @param pageToken - The next page token. If not specified, returns
|
275 | * users starting without any offset.
|
276 | * @returns A promise that resolves with
|
277 | * the current batch of downloaded users and the next page token.
|
278 | */
|
279 | listUsers(maxResults, pageToken) {
|
280 | return this.authRequestHandler.downloadAccount(maxResults, pageToken)
|
281 | .then((response) => {
|
282 | // List of users to return.
|
283 | const users = [];
|
284 | // Convert each user response to a UserRecord.
|
285 | response.users.forEach((userResponse) => {
|
286 | users.push(new user_record_1.UserRecord(userResponse));
|
287 | });
|
288 | // Return list of user records and the next page token if available.
|
289 | const result = {
|
290 | users,
|
291 | pageToken: response.nextPageToken,
|
292 | };
|
293 | // Delete result.pageToken if undefined.
|
294 | if (typeof result.pageToken === 'undefined') {
|
295 | delete result.pageToken;
|
296 | }
|
297 | return result;
|
298 | });
|
299 | }
|
300 | /**
|
301 | * Creates a new user.
|
302 | *
|
303 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#create_a_user | Create a user}
|
304 | * for code samples and detailed documentation.
|
305 | *
|
306 | * @param properties - The properties to set on the
|
307 | * new user record to be created.
|
308 | *
|
309 | * @returns A promise fulfilled with the user
|
310 | * data corresponding to the newly created user.
|
311 | */
|
312 | createUser(properties) {
|
313 | return this.authRequestHandler.createNewAccount(properties)
|
314 | .then((uid) => {
|
315 | // Return the corresponding user record.
|
316 | return this.getUser(uid);
|
317 | })
|
318 | .catch((error) => {
|
319 | if (error.code === 'auth/user-not-found') {
|
320 | // Something must have happened after creating the user and then retrieving it.
|
321 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'Unable to create the user record provided.');
|
322 | }
|
323 | throw error;
|
324 | });
|
325 | }
|
326 | /**
|
327 | * Deletes an existing user.
|
328 | *
|
329 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#delete_a_user | Delete a user}
|
330 | * for code samples and detailed documentation.
|
331 | *
|
332 | * @param uid - The `uid` corresponding to the user to delete.
|
333 | *
|
334 | * @returns An empty promise fulfilled once the user has been
|
335 | * deleted.
|
336 | */
|
337 | deleteUser(uid) {
|
338 | return this.authRequestHandler.deleteAccount(uid)
|
339 | .then(() => {
|
340 | // Return nothing on success.
|
341 | });
|
342 | }
|
343 | /**
|
344 | * Deletes the users specified by the given uids.
|
345 | *
|
346 | * Deleting a non-existing user won't generate an error (i.e. this method
|
347 | * is idempotent.) Non-existing users are considered to be successfully
|
348 | * deleted, and are therefore counted in the
|
349 | * `DeleteUsersResult.successCount` value.
|
350 | *
|
351 | * Only a maximum of 1000 identifiers may be supplied. If more than 1000
|
352 | * identifiers are supplied, this method throws a FirebaseAuthError.
|
353 | *
|
354 | * This API is currently rate limited at the server to 1 QPS. If you exceed
|
355 | * this, you may get a quota exceeded error. Therefore, if you want to
|
356 | * delete more than 1000 users, you may need to add a delay to ensure you
|
357 | * don't go over this limit.
|
358 | *
|
359 | * @param uids - The `uids` corresponding to the users to delete.
|
360 | *
|
361 | * @returns A Promise that resolves to the total number of successful/failed
|
362 | * deletions, as well as the array of errors that corresponds to the
|
363 | * failed deletions.
|
364 | */
|
365 | deleteUsers(uids) {
|
366 | if (!validator.isArray(uids)) {
|
367 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '`uids` parameter must be an array');
|
368 | }
|
369 | return this.authRequestHandler.deleteAccounts(uids, /*force=*/ true)
|
370 | .then((batchDeleteAccountsResponse) => {
|
371 | const result = {
|
372 | failureCount: 0,
|
373 | successCount: uids.length,
|
374 | errors: [],
|
375 | };
|
376 | if (!validator.isNonEmptyArray(batchDeleteAccountsResponse.errors)) {
|
377 | return result;
|
378 | }
|
379 | result.failureCount = batchDeleteAccountsResponse.errors.length;
|
380 | result.successCount = uids.length - batchDeleteAccountsResponse.errors.length;
|
381 | result.errors = batchDeleteAccountsResponse.errors.map((batchDeleteErrorInfo) => {
|
382 | if (batchDeleteErrorInfo.index === undefined) {
|
383 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'Corrupt BatchDeleteAccountsResponse detected');
|
384 | }
|
385 | const errMsgToError = (msg) => {
|
386 | // We unconditionally set force=true, so the 'NOT_DISABLED' error
|
387 | // should not be possible.
|
388 | const code = msg && msg.startsWith('NOT_DISABLED') ?
|
389 | error_1.AuthClientErrorCode.USER_NOT_DISABLED : error_1.AuthClientErrorCode.INTERNAL_ERROR;
|
390 | return new error_1.FirebaseAuthError(code, batchDeleteErrorInfo.message);
|
391 | };
|
392 | return {
|
393 | index: batchDeleteErrorInfo.index,
|
394 | error: errMsgToError(batchDeleteErrorInfo.message),
|
395 | };
|
396 | });
|
397 | return result;
|
398 | });
|
399 | }
|
400 | /**
|
401 | * Updates an existing user.
|
402 | *
|
403 | * See {@link https://firebase.google.com/docs/auth/admin/manage-users#update_a_user | Update a user}
|
404 | * for code samples and detailed documentation.
|
405 | *
|
406 | * @param uid - The `uid` corresponding to the user to update.
|
407 | * @param properties - The properties to update on
|
408 | * the provided user.
|
409 | *
|
410 | * @returns A promise fulfilled with the
|
411 | * updated user data.
|
412 | */
|
413 | updateUser(uid, properties) {
|
414 | // Although we don't really advertise it, we want to also handle linking of
|
415 | // non-federated idps with this call. So if we detect one of them, we'll
|
416 | // adjust the properties parameter appropriately. This *does* imply that a
|
417 | // conflict could arise, e.g. if the user provides a phoneNumber property,
|
418 | // but also provides a providerToLink with a 'phone' provider id. In that
|
419 | // case, we'll throw an error.
|
420 | properties = (0, deep_copy_1.deepCopy)(properties);
|
421 | if (properties?.providerToLink) {
|
422 | if (properties.providerToLink.providerId === 'email') {
|
423 | if (typeof properties.email !== 'undefined') {
|
424 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Both UpdateRequest.email and UpdateRequest.providerToLink.providerId='email' were set. To "
|
425 | + 'link to the email/password provider, only specify the UpdateRequest.email field.');
|
426 | }
|
427 | properties.email = properties.providerToLink.uid;
|
428 | delete properties.providerToLink;
|
429 | }
|
430 | else if (properties.providerToLink.providerId === 'phone') {
|
431 | if (typeof properties.phoneNumber !== 'undefined') {
|
432 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Both UpdateRequest.phoneNumber and UpdateRequest.providerToLink.providerId='phone' were set. To "
|
433 | + 'link to a phone provider, only specify the UpdateRequest.phoneNumber field.');
|
434 | }
|
435 | properties.phoneNumber = properties.providerToLink.uid;
|
436 | delete properties.providerToLink;
|
437 | }
|
438 | }
|
439 | if (properties?.providersToUnlink) {
|
440 | if (properties.providersToUnlink.indexOf('phone') !== -1) {
|
441 | // If we've been told to unlink the phone provider both via setting
|
442 | // phoneNumber to null *and* by setting providersToUnlink to include
|
443 | // 'phone', then we'll reject that. Though it might also be reasonable
|
444 | // to relax this restriction and just unlink it.
|
445 | if (properties.phoneNumber === null) {
|
446 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Both UpdateRequest.phoneNumber=null and UpdateRequest.providersToUnlink=['phone'] were set. To "
|
447 | + 'unlink from a phone provider, only specify the UpdateRequest.phoneNumber=null field.');
|
448 | }
|
449 | }
|
450 | }
|
451 | return this.authRequestHandler.updateExistingAccount(uid, properties)
|
452 | .then((existingUid) => {
|
453 | // Return the corresponding user record.
|
454 | return this.getUser(existingUid);
|
455 | });
|
456 | }
|
457 | /**
|
458 | * Sets additional developer claims on an existing user identified by the
|
459 | * provided `uid`, typically used to define user roles and levels of
|
460 | * access. These claims should propagate to all devices where the user is
|
461 | * already signed in (after token expiration or when token refresh is forced)
|
462 | * and the next time the user signs in. If a reserved OIDC claim name
|
463 | * is used (sub, iat, iss, etc), an error is thrown. They are set on the
|
464 | * authenticated user's ID token JWT.
|
465 | *
|
466 | * See {@link https://firebase.google.com/docs/auth/admin/custom-claims |
|
467 | * Defining user roles and access levels}
|
468 | * for code samples and detailed documentation.
|
469 | *
|
470 | * @param uid - The `uid` of the user to edit.
|
471 | * @param customUserClaims - The developer claims to set. If null is
|
472 | * passed, existing custom claims are deleted. Passing a custom claims payload
|
473 | * larger than 1000 bytes will throw an error. Custom claims are added to the
|
474 | * user's ID token which is transmitted on every authenticated request.
|
475 | * For profile non-access related user attributes, use database or other
|
476 | * separate storage systems.
|
477 | * @returns A promise that resolves when the operation completes
|
478 | * successfully.
|
479 | */
|
480 | setCustomUserClaims(uid, customUserClaims) {
|
481 | return this.authRequestHandler.setCustomUserClaims(uid, customUserClaims)
|
482 | .then(() => {
|
483 | // Return nothing on success.
|
484 | });
|
485 | }
|
486 | /**
|
487 | * Revokes all refresh tokens for an existing user.
|
488 | *
|
489 | * This API will update the user's {@link UserRecord.tokensValidAfterTime} to
|
490 | * the current UTC. It is important that the server on which this is called has
|
491 | * its clock set correctly and synchronized.
|
492 | *
|
493 | * While this will revoke all sessions for a specified user and disable any
|
494 | * new ID tokens for existing sessions from getting minted, existing ID tokens
|
495 | * may remain active until their natural expiration (one hour). To verify that
|
496 | * ID tokens are revoked, use {@link BaseAuth.verifyIdToken}
|
497 | * where `checkRevoked` is set to true.
|
498 | *
|
499 | * @param uid - The `uid` corresponding to the user whose refresh tokens
|
500 | * are to be revoked.
|
501 | *
|
502 | * @returns An empty promise fulfilled once the user's refresh
|
503 | * tokens have been revoked.
|
504 | */
|
505 | revokeRefreshTokens(uid) {
|
506 | return this.authRequestHandler.revokeRefreshTokens(uid)
|
507 | .then(() => {
|
508 | // Return nothing on success.
|
509 | });
|
510 | }
|
511 | /**
|
512 | * Imports the provided list of users into Firebase Auth.
|
513 | * A maximum of 1000 users are allowed to be imported one at a time.
|
514 | * When importing users with passwords,
|
515 | * {@link UserImportOptions} are required to be
|
516 | * specified.
|
517 | * This operation is optimized for bulk imports and will ignore checks on `uid`,
|
518 | * `email` and other identifier uniqueness which could result in duplications.
|
519 | *
|
520 | * @param users - The list of user records to import to Firebase Auth.
|
521 | * @param options - The user import options, required when the users provided include
|
522 | * password credentials.
|
523 | * @returns A promise that resolves when
|
524 | * the operation completes with the result of the import. This includes the
|
525 | * number of successful imports, the number of failed imports and their
|
526 | * corresponding errors.
|
527 | */
|
528 | importUsers(users, options) {
|
529 | return this.authRequestHandler.uploadAccount(users, options);
|
530 | }
|
531 | /**
|
532 | * Creates a new Firebase session cookie with the specified options. The created
|
533 | * JWT string can be set as a server-side session cookie with a custom cookie
|
534 | * policy, and be used for session management. The session cookie JWT will have
|
535 | * the same payload claims as the provided ID token.
|
536 | *
|
537 | * See {@link https://firebase.google.com/docs/auth/admin/manage-cookies | Manage Session Cookies}
|
538 | * for code samples and detailed documentation.
|
539 | *
|
540 | * @param idToken - The Firebase ID token to exchange for a session
|
541 | * cookie.
|
542 | * @param sessionCookieOptions - The session
|
543 | * cookie options which includes custom session duration.
|
544 | *
|
545 | * @returns A promise that resolves on success with the
|
546 | * created session cookie.
|
547 | */
|
548 | createSessionCookie(idToken, sessionCookieOptions) {
|
549 | // Return rejected promise if expiresIn is not available.
|
550 | if (!validator.isNonNullObject(sessionCookieOptions) ||
|
551 | !validator.isNumber(sessionCookieOptions.expiresIn)) {
|
552 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION));
|
553 | }
|
554 | return this.authRequestHandler.createSessionCookie(idToken, sessionCookieOptions.expiresIn);
|
555 | }
|
556 | /**
|
557 | * Verifies a Firebase session cookie. Returns a Promise with the cookie claims.
|
558 | * Rejects the promise if the cookie could not be verified.
|
559 | *
|
560 | * If `checkRevoked` is set to true, first verifies whether the corresponding
|
561 | * user is disabled: If yes, an `auth/user-disabled` error is thrown. If no,
|
562 | * verifies if the session corresponding to the session cookie was revoked.
|
563 | * If the corresponding user's session was invalidated, an
|
564 | * `auth/session-cookie-revoked` error is thrown. If not specified the check
|
565 | * is not performed.
|
566 | *
|
567 | * See {@link https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions |
|
568 | * Verify Session Cookies}
|
569 | * for code samples and detailed documentation
|
570 | *
|
571 | * @param sessionCookie - The session cookie to verify.
|
572 | * @param checkForRevocation - Whether to check if the session cookie was
|
573 | * revoked. This requires an extra request to the Firebase Auth backend to
|
574 | * check the `tokensValidAfterTime` time for the corresponding user.
|
575 | * When not specified, this additional check is not performed.
|
576 | *
|
577 | * @returns A promise fulfilled with the
|
578 | * session cookie's decoded claims if the session cookie is valid; otherwise,
|
579 | * a rejected promise.
|
580 | */
|
581 | verifySessionCookie(sessionCookie, checkRevoked = false) {
|
582 | const isEmulator = (0, auth_api_request_1.useEmulator)();
|
583 | return this.sessionCookieVerifier.verifyJWT(sessionCookie, isEmulator)
|
584 | .then((decodedIdToken) => {
|
585 | // Whether to check if the token was revoked.
|
586 | if (checkRevoked || isEmulator) {
|
587 | return this.verifyDecodedJWTNotRevokedOrDisabled(decodedIdToken, error_1.AuthClientErrorCode.SESSION_COOKIE_REVOKED);
|
588 | }
|
589 | return decodedIdToken;
|
590 | });
|
591 | }
|
592 | /**
|
593 | * Generates the out of band email action link to reset a user's password.
|
594 | * The link is generated for the user with the specified email address. The
|
595 | * optional {@link ActionCodeSettings} object
|
596 | * defines whether the link is to be handled by a mobile app or browser and the
|
597 | * additional state information to be passed in the deep link, etc.
|
598 | *
|
599 | * @example
|
600 | * ```javascript
|
601 | * var actionCodeSettings = {
|
602 | * url: 'https://www.example.com/?email=user@example.com',
|
603 | * iOS: {
|
604 | * bundleId: 'com.example.ios'
|
605 | * },
|
606 | * android: {
|
607 | * packageName: 'com.example.android',
|
608 | * installApp: true,
|
609 | * minimumVersion: '12'
|
610 | * },
|
611 | * handleCodeInApp: true,
|
612 | * dynamicLinkDomain: 'custom.page.link'
|
613 | * };
|
614 | * admin.auth()
|
615 | * .generatePasswordResetLink('user@example.com', actionCodeSettings)
|
616 | * .then(function(link) {
|
617 | * // The link was successfully generated.
|
618 | * })
|
619 | * .catch(function(error) {
|
620 | * // Some error occurred, you can inspect the code: error.code
|
621 | * });
|
622 | * ```
|
623 | *
|
624 | * @param email - The email address of the user whose password is to be
|
625 | * reset.
|
626 | * @param actionCodeSettings - The action
|
627 | * code settings. If specified, the state/continue URL is set as the
|
628 | * "continueUrl" parameter in the password reset link. The default password
|
629 | * reset landing page will use this to display a link to go back to the app
|
630 | * if it is installed.
|
631 | * If the actionCodeSettings is not specified, no URL is appended to the
|
632 | * action URL.
|
633 | * The state URL provided must belong to a domain that is whitelisted by the
|
634 | * developer in the console. Otherwise an error is thrown.
|
635 | * Mobile app redirects are only applicable if the developer configures
|
636 | * and accepts the Firebase Dynamic Links terms of service.
|
637 | * The Android package name and iOS bundle ID are respected only if they
|
638 | * are configured in the same Firebase Auth project.
|
639 | * @returns A promise that resolves with the generated link.
|
640 | */
|
641 | generatePasswordResetLink(email, actionCodeSettings) {
|
642 | return this.authRequestHandler.getEmailActionLink('PASSWORD_RESET', email, actionCodeSettings);
|
643 | }
|
644 | /**
|
645 | * Generates the out of band email action link to verify the user's ownership
|
646 | * of the specified email. The {@link ActionCodeSettings} object provided
|
647 | * as an argument to this method defines whether the link is to be handled by a
|
648 | * mobile app or browser along with additional state information to be passed in
|
649 | * the deep link, etc.
|
650 | *
|
651 | * @example
|
652 | * ```javascript
|
653 | * var actionCodeSettings = {
|
654 | * url: 'https://www.example.com/cart?email=user@example.com&cartId=123',
|
655 | * iOS: {
|
656 | * bundleId: 'com.example.ios'
|
657 | * },
|
658 | * android: {
|
659 | * packageName: 'com.example.android',
|
660 | * installApp: true,
|
661 | * minimumVersion: '12'
|
662 | * },
|
663 | * handleCodeInApp: true,
|
664 | * dynamicLinkDomain: 'custom.page.link'
|
665 | * };
|
666 | * admin.auth()
|
667 | * .generateEmailVerificationLink('user@example.com', actionCodeSettings)
|
668 | * .then(function(link) {
|
669 | * // The link was successfully generated.
|
670 | * })
|
671 | * .catch(function(error) {
|
672 | * // Some error occurred, you can inspect the code: error.code
|
673 | * });
|
674 | * ```
|
675 | *
|
676 | * @param email - The email account to verify.
|
677 | * @param actionCodeSettings - The action
|
678 | * code settings. If specified, the state/continue URL is set as the
|
679 | * "continueUrl" parameter in the email verification link. The default email
|
680 | * verification landing page will use this to display a link to go back to
|
681 | * the app if it is installed.
|
682 | * If the actionCodeSettings is not specified, no URL is appended to the
|
683 | * action URL.
|
684 | * The state URL provided must belong to a domain that is whitelisted by the
|
685 | * developer in the console. Otherwise an error is thrown.
|
686 | * Mobile app redirects are only applicable if the developer configures
|
687 | * and accepts the Firebase Dynamic Links terms of service.
|
688 | * The Android package name and iOS bundle ID are respected only if they
|
689 | * are configured in the same Firebase Auth project.
|
690 | * @returns A promise that resolves with the generated link.
|
691 | */
|
692 | generateEmailVerificationLink(email, actionCodeSettings) {
|
693 | return this.authRequestHandler.getEmailActionLink('VERIFY_EMAIL', email, actionCodeSettings);
|
694 | }
|
695 | /**
|
696 | * Generates an out-of-band email action link to verify the user's ownership
|
697 | * of the specified email. The {@link ActionCodeSettings} object provided
|
698 | * as an argument to this method defines whether the link is to be handled by a
|
699 | * mobile app or browser along with additional state information to be passed in
|
700 | * the deep link, etc.
|
701 | *
|
702 | * @param email - The current email account.
|
703 | * @param newEmail - The email address the account is being updated to.
|
704 | * @param actionCodeSettings - The action
|
705 | * code settings. If specified, the state/continue URL is set as the
|
706 | * "continueUrl" parameter in the email verification link. The default email
|
707 | * verification landing page will use this to display a link to go back to
|
708 | * the app if it is installed.
|
709 | * If the actionCodeSettings is not specified, no URL is appended to the
|
710 | * action URL.
|
711 | * The state URL provided must belong to a domain that is authorized
|
712 | * in the console, or an error will be thrown.
|
713 | * Mobile app redirects are only applicable if the developer configures
|
714 | * and accepts the Firebase Dynamic Links terms of service.
|
715 | * The Android package name and iOS bundle ID are respected only if they
|
716 | * are configured in the same Firebase Auth project.
|
717 | * @returns A promise that resolves with the generated link.
|
718 | */
|
719 | generateVerifyAndChangeEmailLink(email, newEmail, actionCodeSettings) {
|
720 | return this.authRequestHandler.getEmailActionLink('VERIFY_AND_CHANGE_EMAIL', email, actionCodeSettings, newEmail);
|
721 | }
|
722 | /**
|
723 | * Generates the out of band email action link to verify the user's ownership
|
724 | * of the specified email. The {@link ActionCodeSettings} object provided
|
725 | * as an argument to this method defines whether the link is to be handled by a
|
726 | * mobile app or browser along with additional state information to be passed in
|
727 | * the deep link, etc.
|
728 | *
|
729 | * @example
|
730 | * ```javascript
|
731 | * var actionCodeSettings = {
|
732 | * url: 'https://www.example.com/cart?email=user@example.com&cartId=123',
|
733 | * iOS: {
|
734 | * bundleId: 'com.example.ios'
|
735 | * },
|
736 | * android: {
|
737 | * packageName: 'com.example.android',
|
738 | * installApp: true,
|
739 | * minimumVersion: '12'
|
740 | * },
|
741 | * handleCodeInApp: true,
|
742 | * dynamicLinkDomain: 'custom.page.link'
|
743 | * };
|
744 | * admin.auth()
|
745 | * .generateEmailVerificationLink('user@example.com', actionCodeSettings)
|
746 | * .then(function(link) {
|
747 | * // The link was successfully generated.
|
748 | * })
|
749 | * .catch(function(error) {
|
750 | * // Some error occurred, you can inspect the code: error.code
|
751 | * });
|
752 | * ```
|
753 | *
|
754 | * @param email - The email account to verify.
|
755 | * @param actionCodeSettings - The action
|
756 | * code settings. If specified, the state/continue URL is set as the
|
757 | * "continueUrl" parameter in the email verification link. The default email
|
758 | * verification landing page will use this to display a link to go back to
|
759 | * the app if it is installed.
|
760 | * If the actionCodeSettings is not specified, no URL is appended to the
|
761 | * action URL.
|
762 | * The state URL provided must belong to a domain that is whitelisted by the
|
763 | * developer in the console. Otherwise an error is thrown.
|
764 | * Mobile app redirects are only applicable if the developer configures
|
765 | * and accepts the Firebase Dynamic Links terms of service.
|
766 | * The Android package name and iOS bundle ID are respected only if they
|
767 | * are configured in the same Firebase Auth project.
|
768 | * @returns A promise that resolves with the generated link.
|
769 | */
|
770 | generateSignInWithEmailLink(email, actionCodeSettings) {
|
771 | return this.authRequestHandler.getEmailActionLink('EMAIL_SIGNIN', email, actionCodeSettings);
|
772 | }
|
773 | /**
|
774 | * Returns the list of existing provider configurations matching the filter
|
775 | * provided. At most, 100 provider configs can be listed at a time.
|
776 | *
|
777 | * SAML and OIDC provider support requires Google Cloud's Identity Platform
|
778 | * (GCIP). To learn more about GCIP, including pricing and features,
|
779 | * see the {@link https://cloud.google.com/identity-platform | GCIP documentation}.
|
780 | *
|
781 | * @param options - The provider config filter to apply.
|
782 | * @returns A promise that resolves with the list of provider configs meeting the
|
783 | * filter requirements.
|
784 | */
|
785 | listProviderConfigs(options) {
|
786 | const processResponse = (response, providerConfigs) => {
|
787 | // Return list of provider configuration and the next page token if available.
|
788 | const result = {
|
789 | providerConfigs,
|
790 | };
|
791 | // Delete result.pageToken if undefined.
|
792 | if (Object.prototype.hasOwnProperty.call(response, 'nextPageToken')) {
|
793 | result.pageToken = response.nextPageToken;
|
794 | }
|
795 | return result;
|
796 | };
|
797 | if (options && options.type === 'oidc') {
|
798 | return this.authRequestHandler.listOAuthIdpConfigs(options.maxResults, options.pageToken)
|
799 | .then((response) => {
|
800 | // List of provider configurations to return.
|
801 | const providerConfigs = [];
|
802 | // Convert each provider config response to a OIDCConfig.
|
803 | response.oauthIdpConfigs.forEach((configResponse) => {
|
804 | providerConfigs.push(new auth_config_1.OIDCConfig(configResponse));
|
805 | });
|
806 | // Return list of provider configuration and the next page token if available.
|
807 | return processResponse(response, providerConfigs);
|
808 | });
|
809 | }
|
810 | else if (options && options.type === 'saml') {
|
811 | return this.authRequestHandler.listInboundSamlConfigs(options.maxResults, options.pageToken)
|
812 | .then((response) => {
|
813 | // List of provider configurations to return.
|
814 | const providerConfigs = [];
|
815 | // Convert each provider config response to a SAMLConfig.
|
816 | response.inboundSamlConfigs.forEach((configResponse) => {
|
817 | providerConfigs.push(new auth_config_1.SAMLConfig(configResponse));
|
818 | });
|
819 | // Return list of provider configuration and the next page token if available.
|
820 | return processResponse(response, providerConfigs);
|
821 | });
|
822 | }
|
823 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"AuthProviderConfigFilter.type" must be either "saml" or "oidc"'));
|
824 | }
|
825 | /**
|
826 | * Looks up an Auth provider configuration by the provided ID.
|
827 | * Returns a promise that resolves with the provider configuration
|
828 | * corresponding to the provider ID specified. If the specified ID does not
|
829 | * exist, an `auth/configuration-not-found` error is thrown.
|
830 | *
|
831 | * SAML and OIDC provider support requires Google Cloud's Identity Platform
|
832 | * (GCIP). To learn more about GCIP, including pricing and features,
|
833 | * see the {@link https://cloud.google.com/identity-platform | GCIP documentation}.
|
834 | *
|
835 | * @param providerId - The provider ID corresponding to the provider
|
836 | * config to return.
|
837 | * @returns A promise that resolves
|
838 | * with the configuration corresponding to the provided ID.
|
839 | */
|
840 | getProviderConfig(providerId) {
|
841 | if (auth_config_1.OIDCConfig.isProviderId(providerId)) {
|
842 | return this.authRequestHandler.getOAuthIdpConfig(providerId)
|
843 | .then((response) => {
|
844 | return new auth_config_1.OIDCConfig(response);
|
845 | });
|
846 | }
|
847 | else if (auth_config_1.SAMLConfig.isProviderId(providerId)) {
|
848 | return this.authRequestHandler.getInboundSamlConfig(providerId)
|
849 | .then((response) => {
|
850 | return new auth_config_1.SAMLConfig(response);
|
851 | });
|
852 | }
|
853 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
|
854 | }
|
855 | /**
|
856 | * Deletes the provider configuration corresponding to the provider ID passed.
|
857 | * If the specified ID does not exist, an `auth/configuration-not-found` error
|
858 | * is thrown.
|
859 | *
|
860 | * SAML and OIDC provider support requires Google Cloud's Identity Platform
|
861 | * (GCIP). To learn more about GCIP, including pricing and features,
|
862 | * see the {@link https://cloud.google.com/identity-platform | GCIP documentation}.
|
863 | *
|
864 | * @param providerId - The provider ID corresponding to the provider
|
865 | * config to delete.
|
866 | * @returns A promise that resolves on completion.
|
867 | */
|
868 | deleteProviderConfig(providerId) {
|
869 | if (auth_config_1.OIDCConfig.isProviderId(providerId)) {
|
870 | return this.authRequestHandler.deleteOAuthIdpConfig(providerId);
|
871 | }
|
872 | else if (auth_config_1.SAMLConfig.isProviderId(providerId)) {
|
873 | return this.authRequestHandler.deleteInboundSamlConfig(providerId);
|
874 | }
|
875 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
|
876 | }
|
877 | /**
|
878 | * Returns a promise that resolves with the updated `AuthProviderConfig`
|
879 | * corresponding to the provider ID specified.
|
880 | * If the specified ID does not exist, an `auth/configuration-not-found` error
|
881 | * is thrown.
|
882 | *
|
883 | * SAML and OIDC provider support requires Google Cloud's Identity Platform
|
884 | * (GCIP). To learn more about GCIP, including pricing and features,
|
885 | * see the {@link https://cloud.google.com/identity-platform | GCIP documentation}.
|
886 | *
|
887 | * @param providerId - The provider ID corresponding to the provider
|
888 | * config to update.
|
889 | * @param updatedConfig - The updated configuration.
|
890 | * @returns A promise that resolves with the updated provider configuration.
|
891 | */
|
892 | updateProviderConfig(providerId, updatedConfig) {
|
893 | if (!validator.isNonNullObject(updatedConfig)) {
|
894 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CONFIG, 'Request is missing "UpdateAuthProviderRequest" configuration.'));
|
895 | }
|
896 | if (auth_config_1.OIDCConfig.isProviderId(providerId)) {
|
897 | return this.authRequestHandler.updateOAuthIdpConfig(providerId, updatedConfig)
|
898 | .then((response) => {
|
899 | return new auth_config_1.OIDCConfig(response);
|
900 | });
|
901 | }
|
902 | else if (auth_config_1.SAMLConfig.isProviderId(providerId)) {
|
903 | return this.authRequestHandler.updateInboundSamlConfig(providerId, updatedConfig)
|
904 | .then((response) => {
|
905 | return new auth_config_1.SAMLConfig(response);
|
906 | });
|
907 | }
|
908 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
|
909 | }
|
910 | /**
|
911 | * Returns a promise that resolves with the newly created `AuthProviderConfig`
|
912 | * when the new provider configuration is created.
|
913 | *
|
914 | * SAML and OIDC provider support requires Google Cloud's Identity Platform
|
915 | * (GCIP). To learn more about GCIP, including pricing and features,
|
916 | * see the {@link https://cloud.google.com/identity-platform | GCIP documentation}.
|
917 | *
|
918 | * @param config - The provider configuration to create.
|
919 | * @returns A promise that resolves with the created provider configuration.
|
920 | */
|
921 | createProviderConfig(config) {
|
922 | if (!validator.isNonNullObject(config)) {
|
923 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CONFIG, 'Request is missing "AuthProviderConfig" configuration.'));
|
924 | }
|
925 | if (auth_config_1.OIDCConfig.isProviderId(config.providerId)) {
|
926 | return this.authRequestHandler.createOAuthIdpConfig(config)
|
927 | .then((response) => {
|
928 | return new auth_config_1.OIDCConfig(response);
|
929 | });
|
930 | }
|
931 | else if (auth_config_1.SAMLConfig.isProviderId(config.providerId)) {
|
932 | return this.authRequestHandler.createInboundSamlConfig(config)
|
933 | .then((response) => {
|
934 | return new auth_config_1.SAMLConfig(response);
|
935 | });
|
936 | }
|
937 | return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
|
938 | }
|
939 | /** @alpha */
|
940 | // eslint-disable-next-line @typescript-eslint/naming-convention
|
941 | _verifyAuthBlockingToken(token, audience) {
|
942 | const isEmulator = (0, auth_api_request_1.useEmulator)();
|
943 | return this.authBlockingTokenVerifier._verifyAuthBlockingToken(token, isEmulator, audience)
|
944 | .then((decodedAuthBlockingToken) => {
|
945 | return decodedAuthBlockingToken;
|
946 | });
|
947 | }
|
948 | /**
|
949 | * Verifies the decoded Firebase issued JWT is not revoked or disabled. Returns a promise that
|
950 | * resolves with the decoded claims on success. Rejects the promise with revocation error if revoked
|
951 | * or user disabled.
|
952 | *
|
953 | * @param decodedIdToken - The JWT's decoded claims.
|
954 | * @param revocationErrorInfo - The revocation error info to throw on revocation
|
955 | * detection.
|
956 | * @returns A promise that will be fulfilled after a successful verification.
|
957 | */
|
958 | verifyDecodedJWTNotRevokedOrDisabled(decodedIdToken, revocationErrorInfo) {
|
959 | // Get tokens valid after time for the corresponding user.
|
960 | return this.getUser(decodedIdToken.sub)
|
961 | .then((user) => {
|
962 | if (user.disabled) {
|
963 | throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_DISABLED, 'The user record is disabled.');
|
964 | }
|
965 | // If no tokens valid after time available, token is not revoked.
|
966 | if (user.tokensValidAfterTime) {
|
967 | // Get the ID token authentication time and convert to milliseconds UTC.
|
968 | const authTimeUtc = decodedIdToken.auth_time * 1000;
|
969 | // Get user tokens valid after time in milliseconds UTC.
|
970 | const validSinceUtc = new Date(user.tokensValidAfterTime).getTime();
|
971 | // Check if authentication time is older than valid since time.
|
972 | if (authTimeUtc < validSinceUtc) {
|
973 | throw new error_1.FirebaseAuthError(revocationErrorInfo);
|
974 | }
|
975 | }
|
976 | // All checks above passed. Return the decoded token.
|
977 | return decodedIdToken;
|
978 | });
|
979 | }
|
980 | }
|
981 | exports.BaseAuth = BaseAuth;
|