8575 lines
358 KiB
JavaScript
8575 lines
358 KiB
JavaScript
'use strict';
|
|
|
|
var app = require('@firebase/app');
|
|
var util = require('@firebase/util');
|
|
var tslib = require('tslib');
|
|
var component = require('@firebase/component');
|
|
var logger = require('@firebase/logger');
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _debugErrorMap() {
|
|
return {
|
|
["admin-restricted-operation" /* AuthErrorCode.ADMIN_ONLY_OPERATION */]: 'This operation is restricted to administrators only.',
|
|
["argument-error" /* AuthErrorCode.ARGUMENT_ERROR */]: '',
|
|
["app-not-authorized" /* AuthErrorCode.APP_NOT_AUTHORIZED */]: "This app, identified by the domain where it's hosted, is not " +
|
|
'authorized to use Firebase Authentication with the provided API key. ' +
|
|
'Review your key configuration in the Google API console.',
|
|
["app-not-installed" /* AuthErrorCode.APP_NOT_INSTALLED */]: 'The requested mobile application corresponding to the identifier (' +
|
|
'Android package name or iOS bundle ID) provided is not installed on ' +
|
|
'this device.',
|
|
["captcha-check-failed" /* AuthErrorCode.CAPTCHA_CHECK_FAILED */]: 'The reCAPTCHA response token provided is either invalid, expired, ' +
|
|
'already used or the domain associated with it does not match the list ' +
|
|
'of whitelisted domains.',
|
|
["code-expired" /* AuthErrorCode.CODE_EXPIRED */]: 'The SMS code has expired. Please re-send the verification code to try ' +
|
|
'again.',
|
|
["cordova-not-ready" /* AuthErrorCode.CORDOVA_NOT_READY */]: 'Cordova framework is not ready.',
|
|
["cors-unsupported" /* AuthErrorCode.CORS_UNSUPPORTED */]: 'This browser is not supported.',
|
|
["credential-already-in-use" /* AuthErrorCode.CREDENTIAL_ALREADY_IN_USE */]: 'This credential is already associated with a different user account.',
|
|
["custom-token-mismatch" /* AuthErrorCode.CREDENTIAL_MISMATCH */]: 'The custom token corresponds to a different audience.',
|
|
["requires-recent-login" /* AuthErrorCode.CREDENTIAL_TOO_OLD_LOGIN_AGAIN */]: 'This operation is sensitive and requires recent authentication. Log in ' +
|
|
'again before retrying this request.',
|
|
["dependent-sdk-initialized-before-auth" /* AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH */]: 'Another Firebase SDK was initialized and is trying to use Auth before Auth is ' +
|
|
'initialized. Please be sure to call `initializeAuth` or `getAuth` before ' +
|
|
'starting any other Firebase SDK.',
|
|
["dynamic-link-not-activated" /* AuthErrorCode.DYNAMIC_LINK_NOT_ACTIVATED */]: 'Please activate Dynamic Links in the Firebase Console and agree to the terms and ' +
|
|
'conditions.',
|
|
["email-change-needs-verification" /* AuthErrorCode.EMAIL_CHANGE_NEEDS_VERIFICATION */]: 'Multi-factor users must always have a verified email.',
|
|
["email-already-in-use" /* AuthErrorCode.EMAIL_EXISTS */]: 'The email address is already in use by another account.',
|
|
["emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */]: 'Auth instance has already been used to make a network call. Auth can ' +
|
|
'no longer be configured to use the emulator. Try calling ' +
|
|
'"connectAuthEmulator()" sooner.',
|
|
["expired-action-code" /* AuthErrorCode.EXPIRED_OOB_CODE */]: 'The action code has expired.',
|
|
["cancelled-popup-request" /* AuthErrorCode.EXPIRED_POPUP_REQUEST */]: 'This operation has been cancelled due to another conflicting popup being opened.',
|
|
["internal-error" /* AuthErrorCode.INTERNAL_ERROR */]: 'An internal AuthError has occurred.',
|
|
["invalid-app-credential" /* AuthErrorCode.INVALID_APP_CREDENTIAL */]: 'The phone verification request contains an invalid application verifier.' +
|
|
' The reCAPTCHA token response is either invalid or expired.',
|
|
["invalid-app-id" /* AuthErrorCode.INVALID_APP_ID */]: 'The mobile app identifier is not registered for the current project.',
|
|
["invalid-user-token" /* AuthErrorCode.INVALID_AUTH */]: "This user's credential isn't valid for this project. This can happen " +
|
|
"if the user's token has been tampered with, or if the user isn't for " +
|
|
'the project associated with this API key.',
|
|
["invalid-auth-event" /* AuthErrorCode.INVALID_AUTH_EVENT */]: 'An internal AuthError has occurred.',
|
|
["invalid-verification-code" /* AuthErrorCode.INVALID_CODE */]: 'The SMS verification code used to create the phone auth credential is ' +
|
|
'invalid. Please resend the verification code sms and be sure to use the ' +
|
|
'verification code provided by the user.',
|
|
["invalid-continue-uri" /* AuthErrorCode.INVALID_CONTINUE_URI */]: 'The continue URL provided in the request is invalid.',
|
|
["invalid-cordova-configuration" /* AuthErrorCode.INVALID_CORDOVA_CONFIGURATION */]: 'The following Cordova plugins must be installed to enable OAuth sign-in: ' +
|
|
'cordova-plugin-buildinfo, cordova-universal-links-plugin, ' +
|
|
'cordova-plugin-browsertab, cordova-plugin-inappbrowser and ' +
|
|
'cordova-plugin-customurlscheme.',
|
|
["invalid-custom-token" /* AuthErrorCode.INVALID_CUSTOM_TOKEN */]: 'The custom token format is incorrect. Please check the documentation.',
|
|
["invalid-dynamic-link-domain" /* AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN */]: 'The provided dynamic link domain is not configured or authorized for the current project.',
|
|
["invalid-email" /* AuthErrorCode.INVALID_EMAIL */]: 'The email address is badly formatted.',
|
|
["invalid-emulator-scheme" /* AuthErrorCode.INVALID_EMULATOR_SCHEME */]: 'Emulator URL must start with a valid scheme (http:// or https://).',
|
|
["invalid-api-key" /* AuthErrorCode.INVALID_API_KEY */]: 'Your API key is invalid, please check you have copied it correctly.',
|
|
["invalid-cert-hash" /* AuthErrorCode.INVALID_CERT_HASH */]: 'The SHA-1 certificate hash provided is invalid.',
|
|
["invalid-credential" /* AuthErrorCode.INVALID_CREDENTIAL */]: 'The supplied auth credential is incorrect, malformed or has expired.',
|
|
["invalid-message-payload" /* AuthErrorCode.INVALID_MESSAGE_PAYLOAD */]: 'The email template corresponding to this action contains invalid characters in its message. ' +
|
|
'Please fix by going to the Auth email templates section in the Firebase Console.',
|
|
["invalid-multi-factor-session" /* AuthErrorCode.INVALID_MFA_SESSION */]: 'The request does not contain a valid proof of first factor successful sign-in.',
|
|
["invalid-oauth-provider" /* AuthErrorCode.INVALID_OAUTH_PROVIDER */]: 'EmailAuthProvider is not supported for this operation. This operation ' +
|
|
'only supports OAuth providers.',
|
|
["invalid-oauth-client-id" /* AuthErrorCode.INVALID_OAUTH_CLIENT_ID */]: 'The OAuth client ID provided is either invalid or does not match the ' +
|
|
'specified API key.',
|
|
["unauthorized-domain" /* AuthErrorCode.INVALID_ORIGIN */]: 'This domain is not authorized for OAuth operations for your Firebase ' +
|
|
'project. Edit the list of authorized domains from the Firebase console.',
|
|
["invalid-action-code" /* AuthErrorCode.INVALID_OOB_CODE */]: 'The action code is invalid. This can happen if the code is malformed, ' +
|
|
'expired, or has already been used.',
|
|
["wrong-password" /* AuthErrorCode.INVALID_PASSWORD */]: 'The password is invalid or the user does not have a password.',
|
|
["invalid-persistence-type" /* AuthErrorCode.INVALID_PERSISTENCE */]: 'The specified persistence type is invalid. It can only be local, session or none.',
|
|
["invalid-phone-number" /* AuthErrorCode.INVALID_PHONE_NUMBER */]: 'The format of the phone number provided is incorrect. Please enter the ' +
|
|
'phone number in a format that can be parsed into E.164 format. E.164 ' +
|
|
'phone numbers are written in the format [+][country code][subscriber ' +
|
|
'number including area code].',
|
|
["invalid-provider-id" /* AuthErrorCode.INVALID_PROVIDER_ID */]: 'The specified provider ID is invalid.',
|
|
["invalid-recipient-email" /* AuthErrorCode.INVALID_RECIPIENT_EMAIL */]: 'The email corresponding to this action failed to send as the provided ' +
|
|
'recipient email address is invalid.',
|
|
["invalid-sender" /* AuthErrorCode.INVALID_SENDER */]: 'The email template corresponding to this action contains an invalid sender email or name. ' +
|
|
'Please fix by going to the Auth email templates section in the Firebase Console.',
|
|
["invalid-verification-id" /* AuthErrorCode.INVALID_SESSION_INFO */]: 'The verification ID used to create the phone auth credential is invalid.',
|
|
["invalid-tenant-id" /* AuthErrorCode.INVALID_TENANT_ID */]: "The Auth instance's tenant ID is invalid.",
|
|
["login-blocked" /* AuthErrorCode.LOGIN_BLOCKED */]: 'Login blocked by user-provided method: {$originalMessage}',
|
|
["missing-android-pkg-name" /* AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME */]: 'An Android Package Name must be provided if the Android App is required to be installed.',
|
|
["auth-domain-config-required" /* AuthErrorCode.MISSING_AUTH_DOMAIN */]: 'Be sure to include authDomain when calling firebase.initializeApp(), ' +
|
|
'by following the instructions in the Firebase console.',
|
|
["missing-app-credential" /* AuthErrorCode.MISSING_APP_CREDENTIAL */]: 'The phone verification request is missing an application verifier ' +
|
|
'assertion. A reCAPTCHA response token needs to be provided.',
|
|
["missing-verification-code" /* AuthErrorCode.MISSING_CODE */]: 'The phone auth credential was created with an empty SMS verification code.',
|
|
["missing-continue-uri" /* AuthErrorCode.MISSING_CONTINUE_URI */]: 'A continue URL must be provided in the request.',
|
|
["missing-iframe-start" /* AuthErrorCode.MISSING_IFRAME_START */]: 'An internal AuthError has occurred.',
|
|
["missing-ios-bundle-id" /* AuthErrorCode.MISSING_IOS_BUNDLE_ID */]: 'An iOS Bundle ID must be provided if an App Store ID is provided.',
|
|
["missing-or-invalid-nonce" /* AuthErrorCode.MISSING_OR_INVALID_NONCE */]: 'The request does not contain a valid nonce. This can occur if the ' +
|
|
'SHA-256 hash of the provided raw nonce does not match the hashed nonce ' +
|
|
'in the ID token payload.',
|
|
["missing-password" /* AuthErrorCode.MISSING_PASSWORD */]: 'A non-empty password must be provided',
|
|
["missing-multi-factor-info" /* AuthErrorCode.MISSING_MFA_INFO */]: 'No second factor identifier is provided.',
|
|
["missing-multi-factor-session" /* AuthErrorCode.MISSING_MFA_SESSION */]: 'The request is missing proof of first factor successful sign-in.',
|
|
["missing-phone-number" /* AuthErrorCode.MISSING_PHONE_NUMBER */]: 'To send verification codes, provide a phone number for the recipient.',
|
|
["missing-verification-id" /* AuthErrorCode.MISSING_SESSION_INFO */]: 'The phone auth credential was created with an empty verification ID.',
|
|
["app-deleted" /* AuthErrorCode.MODULE_DESTROYED */]: 'This instance of FirebaseApp has been deleted.',
|
|
["multi-factor-info-not-found" /* AuthErrorCode.MFA_INFO_NOT_FOUND */]: 'The user does not have a second factor matching the identifier provided.',
|
|
["multi-factor-auth-required" /* AuthErrorCode.MFA_REQUIRED */]: 'Proof of ownership of a second factor is required to complete sign-in.',
|
|
["account-exists-with-different-credential" /* AuthErrorCode.NEED_CONFIRMATION */]: 'An account already exists with the same email address but different ' +
|
|
'sign-in credentials. Sign in using a provider associated with this ' +
|
|
'email address.',
|
|
["network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */]: 'A network AuthError (such as timeout, interrupted connection or unreachable host) has occurred.',
|
|
["no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */]: 'An internal AuthError has occurred.',
|
|
["no-such-provider" /* AuthErrorCode.NO_SUCH_PROVIDER */]: 'User was not linked to an account with the given provider.',
|
|
["null-user" /* AuthErrorCode.NULL_USER */]: 'A null user object was provided as the argument for an operation which ' +
|
|
'requires a non-null user object.',
|
|
["operation-not-allowed" /* AuthErrorCode.OPERATION_NOT_ALLOWED */]: 'The given sign-in provider is disabled for this Firebase project. ' +
|
|
'Enable it in the Firebase console, under the sign-in method tab of the ' +
|
|
'Auth section.',
|
|
["operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */]: 'This operation is not supported in the environment this application is ' +
|
|
'running on. "location.protocol" must be http, https or chrome-extension' +
|
|
' and web storage must be enabled.',
|
|
["popup-blocked" /* AuthErrorCode.POPUP_BLOCKED */]: 'Unable to establish a connection with the popup. It may have been blocked by the browser.',
|
|
["popup-closed-by-user" /* AuthErrorCode.POPUP_CLOSED_BY_USER */]: 'The popup has been closed by the user before finalizing the operation.',
|
|
["provider-already-linked" /* AuthErrorCode.PROVIDER_ALREADY_LINKED */]: 'User can only be linked to one identity for the given provider.',
|
|
["quota-exceeded" /* AuthErrorCode.QUOTA_EXCEEDED */]: "The project's quota for this operation has been exceeded.",
|
|
["redirect-cancelled-by-user" /* AuthErrorCode.REDIRECT_CANCELLED_BY_USER */]: 'The redirect operation has been cancelled by the user before finalizing.',
|
|
["redirect-operation-pending" /* AuthErrorCode.REDIRECT_OPERATION_PENDING */]: 'A redirect sign-in operation is already pending.',
|
|
["rejected-credential" /* AuthErrorCode.REJECTED_CREDENTIAL */]: 'The request contains malformed or mismatching credentials.',
|
|
["second-factor-already-in-use" /* AuthErrorCode.SECOND_FACTOR_ALREADY_ENROLLED */]: 'The second factor is already enrolled on this account.',
|
|
["maximum-second-factor-count-exceeded" /* AuthErrorCode.SECOND_FACTOR_LIMIT_EXCEEDED */]: 'The maximum allowed number of second factors on a user has been exceeded.',
|
|
["tenant-id-mismatch" /* AuthErrorCode.TENANT_ID_MISMATCH */]: "The provided tenant ID does not match the Auth instance's tenant ID",
|
|
["timeout" /* AuthErrorCode.TIMEOUT */]: 'The operation has timed out.',
|
|
["user-token-expired" /* AuthErrorCode.TOKEN_EXPIRED */]: "The user's credential is no longer valid. The user must sign in again.",
|
|
["too-many-requests" /* AuthErrorCode.TOO_MANY_ATTEMPTS_TRY_LATER */]: 'We have blocked all requests from this device due to unusual activity. ' +
|
|
'Try again later.',
|
|
["unauthorized-continue-uri" /* AuthErrorCode.UNAUTHORIZED_DOMAIN */]: 'The domain of the continue URL is not whitelisted. Please whitelist ' +
|
|
'the domain in the Firebase console.',
|
|
["unsupported-first-factor" /* AuthErrorCode.UNSUPPORTED_FIRST_FACTOR */]: 'Enrolling a second factor or signing in with a multi-factor account requires sign-in with a supported first factor.',
|
|
["unsupported-persistence-type" /* AuthErrorCode.UNSUPPORTED_PERSISTENCE */]: 'The current environment does not support the specified persistence type.',
|
|
["unsupported-tenant-operation" /* AuthErrorCode.UNSUPPORTED_TENANT_OPERATION */]: 'This operation is not supported in a multi-tenant context.',
|
|
["unverified-email" /* AuthErrorCode.UNVERIFIED_EMAIL */]: 'The operation requires a verified email.',
|
|
["user-cancelled" /* AuthErrorCode.USER_CANCELLED */]: 'The user did not grant your application the permissions it requested.',
|
|
["user-not-found" /* AuthErrorCode.USER_DELETED */]: 'There is no user record corresponding to this identifier. The user may ' +
|
|
'have been deleted.',
|
|
["user-disabled" /* AuthErrorCode.USER_DISABLED */]: 'The user account has been disabled by an administrator.',
|
|
["user-mismatch" /* AuthErrorCode.USER_MISMATCH */]: 'The supplied credentials do not correspond to the previously signed in user.',
|
|
["user-signed-out" /* AuthErrorCode.USER_SIGNED_OUT */]: '',
|
|
["weak-password" /* AuthErrorCode.WEAK_PASSWORD */]: 'The password must be 6 characters long or more.',
|
|
["web-storage-unsupported" /* AuthErrorCode.WEB_STORAGE_UNSUPPORTED */]: 'This browser is not supported or 3rd party cookies and data may be disabled.',
|
|
["already-initialized" /* AuthErrorCode.ALREADY_INITIALIZED */]: 'initializeAuth() has already been called with ' +
|
|
'different options. To avoid this error, call initializeAuth() with the ' +
|
|
'same options as when it was originally called, or call getAuth() to return the' +
|
|
' already initialized instance.',
|
|
["missing-recaptcha-token" /* AuthErrorCode.MISSING_RECAPTCHA_TOKEN */]: 'The reCAPTCHA token is missing when sending request to the backend.',
|
|
["invalid-recaptcha-token" /* AuthErrorCode.INVALID_RECAPTCHA_TOKEN */]: 'The reCAPTCHA token is invalid when sending request to the backend.',
|
|
["invalid-recaptcha-action" /* AuthErrorCode.INVALID_RECAPTCHA_ACTION */]: 'The reCAPTCHA action is invalid when sending request to the backend.',
|
|
["recaptcha-not-enabled" /* AuthErrorCode.RECAPTCHA_NOT_ENABLED */]: 'reCAPTCHA Enterprise integration is not enabled for this project.',
|
|
["missing-client-type" /* AuthErrorCode.MISSING_CLIENT_TYPE */]: 'The reCAPTCHA client type is missing when sending request to the backend.',
|
|
["missing-recaptcha-version" /* AuthErrorCode.MISSING_RECAPTCHA_VERSION */]: 'The reCAPTCHA version is missing when sending request to the backend.',
|
|
["invalid-req-type" /* AuthErrorCode.INVALID_REQ_TYPE */]: 'Invalid request parameters.',
|
|
["invalid-recaptcha-version" /* AuthErrorCode.INVALID_RECAPTCHA_VERSION */]: 'The reCAPTCHA version is invalid when sending request to the backend.',
|
|
["unsupported-password-policy-schema-version" /* AuthErrorCode.UNSUPPORTED_PASSWORD_POLICY_SCHEMA_VERSION */]: 'The password policy received from the backend uses a schema version that is not supported by this version of the Firebase SDK.',
|
|
["password-does-not-meet-requirements" /* AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS */]: 'The password does not meet the requirements.'
|
|
};
|
|
}
|
|
function _prodErrorMap() {
|
|
// We will include this one message in the prod error map since by the very
|
|
// nature of this error, developers will never be able to see the message
|
|
// using the debugErrorMap (which is installed during auth initialization).
|
|
return {
|
|
["dependent-sdk-initialized-before-auth" /* AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH */]: 'Another Firebase SDK was initialized and is trying to use Auth before Auth is ' +
|
|
'initialized. Please be sure to call `initializeAuth` or `getAuth` before ' +
|
|
'starting any other Firebase SDK.'
|
|
};
|
|
}
|
|
/**
|
|
* A verbose error map with detailed descriptions for most error codes.
|
|
*
|
|
* See discussion at {@link AuthErrorMap}
|
|
*
|
|
* @public
|
|
*/
|
|
const debugErrorMap = _debugErrorMap;
|
|
/**
|
|
* A minimal error map with all verbose error messages stripped.
|
|
*
|
|
* See discussion at {@link AuthErrorMap}
|
|
*
|
|
* @public
|
|
*/
|
|
const prodErrorMap = _prodErrorMap;
|
|
const _DEFAULT_AUTH_ERROR_FACTORY = new util.ErrorFactory('auth', 'Firebase', _prodErrorMap());
|
|
/**
|
|
* A map of potential `Auth` error codes, for easier comparison with errors
|
|
* thrown by the SDK.
|
|
*
|
|
* @remarks
|
|
* Note that you can't tree-shake individual keys
|
|
* in the map, so by using the map you might substantially increase your
|
|
* bundle size.
|
|
*
|
|
* @public
|
|
*/
|
|
const AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = {
|
|
ADMIN_ONLY_OPERATION: 'auth/admin-restricted-operation',
|
|
ARGUMENT_ERROR: 'auth/argument-error',
|
|
APP_NOT_AUTHORIZED: 'auth/app-not-authorized',
|
|
APP_NOT_INSTALLED: 'auth/app-not-installed',
|
|
CAPTCHA_CHECK_FAILED: 'auth/captcha-check-failed',
|
|
CODE_EXPIRED: 'auth/code-expired',
|
|
CORDOVA_NOT_READY: 'auth/cordova-not-ready',
|
|
CORS_UNSUPPORTED: 'auth/cors-unsupported',
|
|
CREDENTIAL_ALREADY_IN_USE: 'auth/credential-already-in-use',
|
|
CREDENTIAL_MISMATCH: 'auth/custom-token-mismatch',
|
|
CREDENTIAL_TOO_OLD_LOGIN_AGAIN: 'auth/requires-recent-login',
|
|
DEPENDENT_SDK_INIT_BEFORE_AUTH: 'auth/dependent-sdk-initialized-before-auth',
|
|
DYNAMIC_LINK_NOT_ACTIVATED: 'auth/dynamic-link-not-activated',
|
|
EMAIL_CHANGE_NEEDS_VERIFICATION: 'auth/email-change-needs-verification',
|
|
EMAIL_EXISTS: 'auth/email-already-in-use',
|
|
EMULATOR_CONFIG_FAILED: 'auth/emulator-config-failed',
|
|
EXPIRED_OOB_CODE: 'auth/expired-action-code',
|
|
EXPIRED_POPUP_REQUEST: 'auth/cancelled-popup-request',
|
|
INTERNAL_ERROR: 'auth/internal-error',
|
|
INVALID_API_KEY: 'auth/invalid-api-key',
|
|
INVALID_APP_CREDENTIAL: 'auth/invalid-app-credential',
|
|
INVALID_APP_ID: 'auth/invalid-app-id',
|
|
INVALID_AUTH: 'auth/invalid-user-token',
|
|
INVALID_AUTH_EVENT: 'auth/invalid-auth-event',
|
|
INVALID_CERT_HASH: 'auth/invalid-cert-hash',
|
|
INVALID_CODE: 'auth/invalid-verification-code',
|
|
INVALID_CONTINUE_URI: 'auth/invalid-continue-uri',
|
|
INVALID_CORDOVA_CONFIGURATION: 'auth/invalid-cordova-configuration',
|
|
INVALID_CUSTOM_TOKEN: 'auth/invalid-custom-token',
|
|
INVALID_DYNAMIC_LINK_DOMAIN: 'auth/invalid-dynamic-link-domain',
|
|
INVALID_EMAIL: 'auth/invalid-email',
|
|
INVALID_EMULATOR_SCHEME: 'auth/invalid-emulator-scheme',
|
|
INVALID_IDP_RESPONSE: 'auth/invalid-credential',
|
|
INVALID_LOGIN_CREDENTIALS: 'auth/invalid-credential',
|
|
INVALID_MESSAGE_PAYLOAD: 'auth/invalid-message-payload',
|
|
INVALID_MFA_SESSION: 'auth/invalid-multi-factor-session',
|
|
INVALID_OAUTH_CLIENT_ID: 'auth/invalid-oauth-client-id',
|
|
INVALID_OAUTH_PROVIDER: 'auth/invalid-oauth-provider',
|
|
INVALID_OOB_CODE: 'auth/invalid-action-code',
|
|
INVALID_ORIGIN: 'auth/unauthorized-domain',
|
|
INVALID_PASSWORD: 'auth/wrong-password',
|
|
INVALID_PERSISTENCE: 'auth/invalid-persistence-type',
|
|
INVALID_PHONE_NUMBER: 'auth/invalid-phone-number',
|
|
INVALID_PROVIDER_ID: 'auth/invalid-provider-id',
|
|
INVALID_RECIPIENT_EMAIL: 'auth/invalid-recipient-email',
|
|
INVALID_SENDER: 'auth/invalid-sender',
|
|
INVALID_SESSION_INFO: 'auth/invalid-verification-id',
|
|
INVALID_TENANT_ID: 'auth/invalid-tenant-id',
|
|
MFA_INFO_NOT_FOUND: 'auth/multi-factor-info-not-found',
|
|
MFA_REQUIRED: 'auth/multi-factor-auth-required',
|
|
MISSING_ANDROID_PACKAGE_NAME: 'auth/missing-android-pkg-name',
|
|
MISSING_APP_CREDENTIAL: 'auth/missing-app-credential',
|
|
MISSING_AUTH_DOMAIN: 'auth/auth-domain-config-required',
|
|
MISSING_CODE: 'auth/missing-verification-code',
|
|
MISSING_CONTINUE_URI: 'auth/missing-continue-uri',
|
|
MISSING_IFRAME_START: 'auth/missing-iframe-start',
|
|
MISSING_IOS_BUNDLE_ID: 'auth/missing-ios-bundle-id',
|
|
MISSING_OR_INVALID_NONCE: 'auth/missing-or-invalid-nonce',
|
|
MISSING_MFA_INFO: 'auth/missing-multi-factor-info',
|
|
MISSING_MFA_SESSION: 'auth/missing-multi-factor-session',
|
|
MISSING_PHONE_NUMBER: 'auth/missing-phone-number',
|
|
MISSING_SESSION_INFO: 'auth/missing-verification-id',
|
|
MODULE_DESTROYED: 'auth/app-deleted',
|
|
NEED_CONFIRMATION: 'auth/account-exists-with-different-credential',
|
|
NETWORK_REQUEST_FAILED: 'auth/network-request-failed',
|
|
NULL_USER: 'auth/null-user',
|
|
NO_AUTH_EVENT: 'auth/no-auth-event',
|
|
NO_SUCH_PROVIDER: 'auth/no-such-provider',
|
|
OPERATION_NOT_ALLOWED: 'auth/operation-not-allowed',
|
|
OPERATION_NOT_SUPPORTED: 'auth/operation-not-supported-in-this-environment',
|
|
POPUP_BLOCKED: 'auth/popup-blocked',
|
|
POPUP_CLOSED_BY_USER: 'auth/popup-closed-by-user',
|
|
PROVIDER_ALREADY_LINKED: 'auth/provider-already-linked',
|
|
QUOTA_EXCEEDED: 'auth/quota-exceeded',
|
|
REDIRECT_CANCELLED_BY_USER: 'auth/redirect-cancelled-by-user',
|
|
REDIRECT_OPERATION_PENDING: 'auth/redirect-operation-pending',
|
|
REJECTED_CREDENTIAL: 'auth/rejected-credential',
|
|
SECOND_FACTOR_ALREADY_ENROLLED: 'auth/second-factor-already-in-use',
|
|
SECOND_FACTOR_LIMIT_EXCEEDED: 'auth/maximum-second-factor-count-exceeded',
|
|
TENANT_ID_MISMATCH: 'auth/tenant-id-mismatch',
|
|
TIMEOUT: 'auth/timeout',
|
|
TOKEN_EXPIRED: 'auth/user-token-expired',
|
|
TOO_MANY_ATTEMPTS_TRY_LATER: 'auth/too-many-requests',
|
|
UNAUTHORIZED_DOMAIN: 'auth/unauthorized-continue-uri',
|
|
UNSUPPORTED_FIRST_FACTOR: 'auth/unsupported-first-factor',
|
|
UNSUPPORTED_PERSISTENCE: 'auth/unsupported-persistence-type',
|
|
UNSUPPORTED_TENANT_OPERATION: 'auth/unsupported-tenant-operation',
|
|
UNVERIFIED_EMAIL: 'auth/unverified-email',
|
|
USER_CANCELLED: 'auth/user-cancelled',
|
|
USER_DELETED: 'auth/user-not-found',
|
|
USER_DISABLED: 'auth/user-disabled',
|
|
USER_MISMATCH: 'auth/user-mismatch',
|
|
USER_SIGNED_OUT: 'auth/user-signed-out',
|
|
WEAK_PASSWORD: 'auth/weak-password',
|
|
WEB_STORAGE_UNSUPPORTED: 'auth/web-storage-unsupported',
|
|
ALREADY_INITIALIZED: 'auth/already-initialized',
|
|
RECAPTCHA_NOT_ENABLED: 'auth/recaptcha-not-enabled',
|
|
MISSING_RECAPTCHA_TOKEN: 'auth/missing-recaptcha-token',
|
|
INVALID_RECAPTCHA_TOKEN: 'auth/invalid-recaptcha-token',
|
|
INVALID_RECAPTCHA_ACTION: 'auth/invalid-recaptcha-action',
|
|
MISSING_CLIENT_TYPE: 'auth/missing-client-type',
|
|
MISSING_RECAPTCHA_VERSION: 'auth/missing-recaptcha-version',
|
|
INVALID_RECAPTCHA_VERSION: 'auth/invalid-recaptcha-version',
|
|
INVALID_REQ_TYPE: 'auth/invalid-req-type'
|
|
};
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const logClient = new logger.Logger('@firebase/auth');
|
|
function _logWarn(msg, ...args) {
|
|
if (logClient.logLevel <= logger.LogLevel.WARN) {
|
|
logClient.warn(`Auth (${app.SDK_VERSION}): ${msg}`, ...args);
|
|
}
|
|
}
|
|
function _logError(msg, ...args) {
|
|
if (logClient.logLevel <= logger.LogLevel.ERROR) {
|
|
logClient.error(`Auth (${app.SDK_VERSION}): ${msg}`, ...args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _fail(authOrCode, ...rest) {
|
|
throw createErrorInternal(authOrCode, ...rest);
|
|
}
|
|
function _createError(authOrCode, ...rest) {
|
|
return createErrorInternal(authOrCode, ...rest);
|
|
}
|
|
function _errorWithCustomMessage(auth, code, message) {
|
|
const errorMap = Object.assign(Object.assign({}, prodErrorMap()), { [code]: message });
|
|
const factory = new util.ErrorFactory('auth', 'Firebase', errorMap);
|
|
return factory.create(code, {
|
|
appName: auth.name
|
|
});
|
|
}
|
|
function _serverAppCurrentUserOperationNotSupportedError(auth) {
|
|
return _errorWithCustomMessage(auth, "operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */, 'Operations that alter the current user are not supported in conjunction with FirebaseServerApp');
|
|
}
|
|
function _assertInstanceOf(auth, object, instance) {
|
|
const constructorInstance = instance;
|
|
if (!(object instanceof constructorInstance)) {
|
|
if (constructorInstance.name !== object.constructor.name) {
|
|
_fail(auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
}
|
|
throw _errorWithCustomMessage(auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */, `Type of ${object.constructor.name} does not match expected instance.` +
|
|
`Did you pass a reference from a different Auth SDK?`);
|
|
}
|
|
}
|
|
function createErrorInternal(authOrCode, ...rest) {
|
|
if (typeof authOrCode !== 'string') {
|
|
const code = rest[0];
|
|
const fullParams = [...rest.slice(1)];
|
|
if (fullParams[0]) {
|
|
fullParams[0].appName = authOrCode.name;
|
|
}
|
|
return authOrCode._errorFactory.create(code, ...fullParams);
|
|
}
|
|
return _DEFAULT_AUTH_ERROR_FACTORY.create(authOrCode, ...rest);
|
|
}
|
|
function _assert(assertion, authOrCode, ...rest) {
|
|
if (!assertion) {
|
|
throw createErrorInternal(authOrCode, ...rest);
|
|
}
|
|
}
|
|
/**
|
|
* Unconditionally fails, throwing an internal error with the given message.
|
|
*
|
|
* @param failure type of failure encountered
|
|
* @throws Error
|
|
*/
|
|
function debugFail(failure) {
|
|
// Log the failure in addition to throw an exception, just in case the
|
|
// exception is swallowed.
|
|
const message = `INTERNAL ASSERTION FAILED: ` + failure;
|
|
_logError(message);
|
|
// NOTE: We don't use FirebaseError here because these are internal failures
|
|
// that cannot be handled by the user. (Also it would create a circular
|
|
// dependency between the error and assert modules which doesn't work.)
|
|
throw new Error(message);
|
|
}
|
|
/**
|
|
* Fails if the given assertion condition is false, throwing an Error with the
|
|
* given message if it did.
|
|
*
|
|
* @param assertion
|
|
* @param message
|
|
*/
|
|
function debugAssert(assertion, message) {
|
|
if (!assertion) {
|
|
debugFail(message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _getCurrentUrl() {
|
|
var _a;
|
|
return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.href)) || '';
|
|
}
|
|
function _isHttpOrHttps() {
|
|
return _getCurrentScheme() === 'http:' || _getCurrentScheme() === 'https:';
|
|
}
|
|
function _getCurrentScheme() {
|
|
var _a;
|
|
return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.protocol)) || null;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Determine whether the browser is working online
|
|
*/
|
|
function _isOnline() {
|
|
if (typeof navigator !== 'undefined' &&
|
|
navigator &&
|
|
'onLine' in navigator &&
|
|
typeof navigator.onLine === 'boolean' &&
|
|
// Apply only for traditional web apps and Chrome extensions.
|
|
// This is especially true for Cordova apps which have unreliable
|
|
// navigator.onLine behavior unless cordova-plugin-network-information is
|
|
// installed which overwrites the native navigator.onLine value and
|
|
// defines navigator.connection.
|
|
(_isHttpOrHttps() || util.isBrowserExtension() || 'connection' in navigator)) {
|
|
return navigator.onLine;
|
|
}
|
|
// If we can't determine the state, assume it is online.
|
|
return true;
|
|
}
|
|
function _getUserLanguage() {
|
|
if (typeof navigator === 'undefined') {
|
|
return null;
|
|
}
|
|
const navigatorLanguage = navigator;
|
|
return (
|
|
// Most reliable, but only supported in Chrome/Firefox.
|
|
(navigatorLanguage.languages && navigatorLanguage.languages[0]) ||
|
|
// Supported in most browsers, but returns the language of the browser
|
|
// UI, not the language set in browser settings.
|
|
navigatorLanguage.language ||
|
|
// Couldn't determine language.
|
|
null);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* A structure to help pick between a range of long and short delay durations
|
|
* depending on the current environment. In general, the long delay is used for
|
|
* mobile environments whereas short delays are used for desktop environments.
|
|
*/
|
|
class Delay {
|
|
constructor(shortDelay, longDelay) {
|
|
this.shortDelay = shortDelay;
|
|
this.longDelay = longDelay;
|
|
// Internal error when improperly initialized.
|
|
debugAssert(longDelay > shortDelay, 'Short delay should be less than long delay!');
|
|
this.isMobile = util.isMobileCordova() || util.isReactNative();
|
|
}
|
|
get() {
|
|
if (!_isOnline()) {
|
|
// Pick the shorter timeout.
|
|
return Math.min(5000 /* DelayMin.OFFLINE */, this.shortDelay);
|
|
}
|
|
// If running in a mobile environment, return the long delay, otherwise
|
|
// return the short delay.
|
|
// This could be improved in the future to dynamically change based on other
|
|
// variables instead of just reading the current environment.
|
|
return this.isMobile ? this.longDelay : this.shortDelay;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _emulatorUrl(config, path) {
|
|
debugAssert(config.emulator, 'Emulator should always be set here');
|
|
const { url } = config.emulator;
|
|
if (!path) {
|
|
return url;
|
|
}
|
|
return `${url}${path.startsWith('/') ? path.slice(1) : path}`;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class FetchProvider {
|
|
static initialize(fetchImpl, headersImpl, responseImpl) {
|
|
this.fetchImpl = fetchImpl;
|
|
if (headersImpl) {
|
|
this.headersImpl = headersImpl;
|
|
}
|
|
if (responseImpl) {
|
|
this.responseImpl = responseImpl;
|
|
}
|
|
}
|
|
static fetch() {
|
|
if (this.fetchImpl) {
|
|
return this.fetchImpl;
|
|
}
|
|
if (typeof self !== 'undefined' && 'fetch' in self) {
|
|
return self.fetch;
|
|
}
|
|
if (typeof globalThis !== 'undefined' && globalThis.fetch) {
|
|
return globalThis.fetch;
|
|
}
|
|
if (typeof fetch !== 'undefined') {
|
|
return fetch;
|
|
}
|
|
debugFail('Could not find fetch implementation, make sure you call FetchProvider.initialize() with an appropriate polyfill');
|
|
}
|
|
static headers() {
|
|
if (this.headersImpl) {
|
|
return this.headersImpl;
|
|
}
|
|
if (typeof self !== 'undefined' && 'Headers' in self) {
|
|
return self.Headers;
|
|
}
|
|
if (typeof globalThis !== 'undefined' && globalThis.Headers) {
|
|
return globalThis.Headers;
|
|
}
|
|
if (typeof Headers !== 'undefined') {
|
|
return Headers;
|
|
}
|
|
debugFail('Could not find Headers implementation, make sure you call FetchProvider.initialize() with an appropriate polyfill');
|
|
}
|
|
static response() {
|
|
if (this.responseImpl) {
|
|
return this.responseImpl;
|
|
}
|
|
if (typeof self !== 'undefined' && 'Response' in self) {
|
|
return self.Response;
|
|
}
|
|
if (typeof globalThis !== 'undefined' && globalThis.Response) {
|
|
return globalThis.Response;
|
|
}
|
|
if (typeof Response !== 'undefined') {
|
|
return Response;
|
|
}
|
|
debugFail('Could not find Response implementation, make sure you call FetchProvider.initialize() with an appropriate polyfill');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Map from errors returned by the server to errors to developer visible errors
|
|
*/
|
|
const SERVER_ERROR_MAP = {
|
|
// Custom token errors.
|
|
["CREDENTIAL_MISMATCH" /* ServerError.CREDENTIAL_MISMATCH */]: "custom-token-mismatch" /* AuthErrorCode.CREDENTIAL_MISMATCH */,
|
|
// This can only happen if the SDK sends a bad request.
|
|
["MISSING_CUSTOM_TOKEN" /* ServerError.MISSING_CUSTOM_TOKEN */]: "internal-error" /* AuthErrorCode.INTERNAL_ERROR */,
|
|
// Create Auth URI errors.
|
|
["INVALID_IDENTIFIER" /* ServerError.INVALID_IDENTIFIER */]: "invalid-email" /* AuthErrorCode.INVALID_EMAIL */,
|
|
// This can only happen if the SDK sends a bad request.
|
|
["MISSING_CONTINUE_URI" /* ServerError.MISSING_CONTINUE_URI */]: "internal-error" /* AuthErrorCode.INTERNAL_ERROR */,
|
|
// Sign in with email and password errors (some apply to sign up too).
|
|
["INVALID_PASSWORD" /* ServerError.INVALID_PASSWORD */]: "wrong-password" /* AuthErrorCode.INVALID_PASSWORD */,
|
|
// This can only happen if the SDK sends a bad request.
|
|
["MISSING_PASSWORD" /* ServerError.MISSING_PASSWORD */]: "missing-password" /* AuthErrorCode.MISSING_PASSWORD */,
|
|
// Thrown if Email Enumeration Protection is enabled in the project and the email or password is
|
|
// invalid.
|
|
["INVALID_LOGIN_CREDENTIALS" /* ServerError.INVALID_LOGIN_CREDENTIALS */]: "invalid-credential" /* AuthErrorCode.INVALID_CREDENTIAL */,
|
|
// Sign up with email and password errors.
|
|
["EMAIL_EXISTS" /* ServerError.EMAIL_EXISTS */]: "email-already-in-use" /* AuthErrorCode.EMAIL_EXISTS */,
|
|
["PASSWORD_LOGIN_DISABLED" /* ServerError.PASSWORD_LOGIN_DISABLED */]: "operation-not-allowed" /* AuthErrorCode.OPERATION_NOT_ALLOWED */,
|
|
// Verify assertion for sign in with credential errors:
|
|
["INVALID_IDP_RESPONSE" /* ServerError.INVALID_IDP_RESPONSE */]: "invalid-credential" /* AuthErrorCode.INVALID_CREDENTIAL */,
|
|
["INVALID_PENDING_TOKEN" /* ServerError.INVALID_PENDING_TOKEN */]: "invalid-credential" /* AuthErrorCode.INVALID_CREDENTIAL */,
|
|
["FEDERATED_USER_ID_ALREADY_LINKED" /* ServerError.FEDERATED_USER_ID_ALREADY_LINKED */]: "credential-already-in-use" /* AuthErrorCode.CREDENTIAL_ALREADY_IN_USE */,
|
|
// This can only happen if the SDK sends a bad request.
|
|
["MISSING_REQ_TYPE" /* ServerError.MISSING_REQ_TYPE */]: "internal-error" /* AuthErrorCode.INTERNAL_ERROR */,
|
|
// Send Password reset email errors:
|
|
["EMAIL_NOT_FOUND" /* ServerError.EMAIL_NOT_FOUND */]: "user-not-found" /* AuthErrorCode.USER_DELETED */,
|
|
["RESET_PASSWORD_EXCEED_LIMIT" /* ServerError.RESET_PASSWORD_EXCEED_LIMIT */]: "too-many-requests" /* AuthErrorCode.TOO_MANY_ATTEMPTS_TRY_LATER */,
|
|
["EXPIRED_OOB_CODE" /* ServerError.EXPIRED_OOB_CODE */]: "expired-action-code" /* AuthErrorCode.EXPIRED_OOB_CODE */,
|
|
["INVALID_OOB_CODE" /* ServerError.INVALID_OOB_CODE */]: "invalid-action-code" /* AuthErrorCode.INVALID_OOB_CODE */,
|
|
// This can only happen if the SDK sends a bad request.
|
|
["MISSING_OOB_CODE" /* ServerError.MISSING_OOB_CODE */]: "internal-error" /* AuthErrorCode.INTERNAL_ERROR */,
|
|
// Operations that require ID token in request:
|
|
["CREDENTIAL_TOO_OLD_LOGIN_AGAIN" /* ServerError.CREDENTIAL_TOO_OLD_LOGIN_AGAIN */]: "requires-recent-login" /* AuthErrorCode.CREDENTIAL_TOO_OLD_LOGIN_AGAIN */,
|
|
["INVALID_ID_TOKEN" /* ServerError.INVALID_ID_TOKEN */]: "invalid-user-token" /* AuthErrorCode.INVALID_AUTH */,
|
|
["TOKEN_EXPIRED" /* ServerError.TOKEN_EXPIRED */]: "user-token-expired" /* AuthErrorCode.TOKEN_EXPIRED */,
|
|
["USER_NOT_FOUND" /* ServerError.USER_NOT_FOUND */]: "user-token-expired" /* AuthErrorCode.TOKEN_EXPIRED */,
|
|
// Other errors.
|
|
["TOO_MANY_ATTEMPTS_TRY_LATER" /* ServerError.TOO_MANY_ATTEMPTS_TRY_LATER */]: "too-many-requests" /* AuthErrorCode.TOO_MANY_ATTEMPTS_TRY_LATER */,
|
|
["PASSWORD_DOES_NOT_MEET_REQUIREMENTS" /* ServerError.PASSWORD_DOES_NOT_MEET_REQUIREMENTS */]: "password-does-not-meet-requirements" /* AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS */,
|
|
// Phone Auth related errors.
|
|
["INVALID_CODE" /* ServerError.INVALID_CODE */]: "invalid-verification-code" /* AuthErrorCode.INVALID_CODE */,
|
|
["INVALID_SESSION_INFO" /* ServerError.INVALID_SESSION_INFO */]: "invalid-verification-id" /* AuthErrorCode.INVALID_SESSION_INFO */,
|
|
["INVALID_TEMPORARY_PROOF" /* ServerError.INVALID_TEMPORARY_PROOF */]: "invalid-credential" /* AuthErrorCode.INVALID_CREDENTIAL */,
|
|
["MISSING_SESSION_INFO" /* ServerError.MISSING_SESSION_INFO */]: "missing-verification-id" /* AuthErrorCode.MISSING_SESSION_INFO */,
|
|
["SESSION_EXPIRED" /* ServerError.SESSION_EXPIRED */]: "code-expired" /* AuthErrorCode.CODE_EXPIRED */,
|
|
// Other action code errors when additional settings passed.
|
|
// MISSING_CONTINUE_URI is getting mapped to INTERNAL_ERROR above.
|
|
// This is OK as this error will be caught by client side validation.
|
|
["MISSING_ANDROID_PACKAGE_NAME" /* ServerError.MISSING_ANDROID_PACKAGE_NAME */]: "missing-android-pkg-name" /* AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME */,
|
|
["UNAUTHORIZED_DOMAIN" /* ServerError.UNAUTHORIZED_DOMAIN */]: "unauthorized-continue-uri" /* AuthErrorCode.UNAUTHORIZED_DOMAIN */,
|
|
// getProjectConfig errors when clientId is passed.
|
|
["INVALID_OAUTH_CLIENT_ID" /* ServerError.INVALID_OAUTH_CLIENT_ID */]: "invalid-oauth-client-id" /* AuthErrorCode.INVALID_OAUTH_CLIENT_ID */,
|
|
// User actions (sign-up or deletion) disabled errors.
|
|
["ADMIN_ONLY_OPERATION" /* ServerError.ADMIN_ONLY_OPERATION */]: "admin-restricted-operation" /* AuthErrorCode.ADMIN_ONLY_OPERATION */,
|
|
// Multi factor related errors.
|
|
["INVALID_MFA_PENDING_CREDENTIAL" /* ServerError.INVALID_MFA_PENDING_CREDENTIAL */]: "invalid-multi-factor-session" /* AuthErrorCode.INVALID_MFA_SESSION */,
|
|
["MFA_ENROLLMENT_NOT_FOUND" /* ServerError.MFA_ENROLLMENT_NOT_FOUND */]: "multi-factor-info-not-found" /* AuthErrorCode.MFA_INFO_NOT_FOUND */,
|
|
["MISSING_MFA_ENROLLMENT_ID" /* ServerError.MISSING_MFA_ENROLLMENT_ID */]: "missing-multi-factor-info" /* AuthErrorCode.MISSING_MFA_INFO */,
|
|
["MISSING_MFA_PENDING_CREDENTIAL" /* ServerError.MISSING_MFA_PENDING_CREDENTIAL */]: "missing-multi-factor-session" /* AuthErrorCode.MISSING_MFA_SESSION */,
|
|
["SECOND_FACTOR_EXISTS" /* ServerError.SECOND_FACTOR_EXISTS */]: "second-factor-already-in-use" /* AuthErrorCode.SECOND_FACTOR_ALREADY_ENROLLED */,
|
|
["SECOND_FACTOR_LIMIT_EXCEEDED" /* ServerError.SECOND_FACTOR_LIMIT_EXCEEDED */]: "maximum-second-factor-count-exceeded" /* AuthErrorCode.SECOND_FACTOR_LIMIT_EXCEEDED */,
|
|
// Blocking functions related errors.
|
|
["BLOCKING_FUNCTION_ERROR_RESPONSE" /* ServerError.BLOCKING_FUNCTION_ERROR_RESPONSE */]: "internal-error" /* AuthErrorCode.INTERNAL_ERROR */,
|
|
// Recaptcha related errors.
|
|
["RECAPTCHA_NOT_ENABLED" /* ServerError.RECAPTCHA_NOT_ENABLED */]: "recaptcha-not-enabled" /* AuthErrorCode.RECAPTCHA_NOT_ENABLED */,
|
|
["MISSING_RECAPTCHA_TOKEN" /* ServerError.MISSING_RECAPTCHA_TOKEN */]: "missing-recaptcha-token" /* AuthErrorCode.MISSING_RECAPTCHA_TOKEN */,
|
|
["INVALID_RECAPTCHA_TOKEN" /* ServerError.INVALID_RECAPTCHA_TOKEN */]: "invalid-recaptcha-token" /* AuthErrorCode.INVALID_RECAPTCHA_TOKEN */,
|
|
["INVALID_RECAPTCHA_ACTION" /* ServerError.INVALID_RECAPTCHA_ACTION */]: "invalid-recaptcha-action" /* AuthErrorCode.INVALID_RECAPTCHA_ACTION */,
|
|
["MISSING_CLIENT_TYPE" /* ServerError.MISSING_CLIENT_TYPE */]: "missing-client-type" /* AuthErrorCode.MISSING_CLIENT_TYPE */,
|
|
["MISSING_RECAPTCHA_VERSION" /* ServerError.MISSING_RECAPTCHA_VERSION */]: "missing-recaptcha-version" /* AuthErrorCode.MISSING_RECAPTCHA_VERSION */,
|
|
["INVALID_RECAPTCHA_VERSION" /* ServerError.INVALID_RECAPTCHA_VERSION */]: "invalid-recaptcha-version" /* AuthErrorCode.INVALID_RECAPTCHA_VERSION */,
|
|
["INVALID_REQ_TYPE" /* ServerError.INVALID_REQ_TYPE */]: "invalid-req-type" /* AuthErrorCode.INVALID_REQ_TYPE */
|
|
};
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const DEFAULT_API_TIMEOUT_MS = new Delay(30000, 60000);
|
|
function _addTidIfNecessary(auth, request) {
|
|
if (auth.tenantId && !request.tenantId) {
|
|
return Object.assign(Object.assign({}, request), { tenantId: auth.tenantId });
|
|
}
|
|
return request;
|
|
}
|
|
async function _performApiRequest(auth, method, path, request, customErrorMap = {}) {
|
|
return _performFetchWithErrorHandling(auth, customErrorMap, async () => {
|
|
let body = {};
|
|
let params = {};
|
|
if (request) {
|
|
if (method === "GET" /* HttpMethod.GET */) {
|
|
params = request;
|
|
}
|
|
else {
|
|
body = {
|
|
body: JSON.stringify(request)
|
|
};
|
|
}
|
|
}
|
|
const query = util.querystring(Object.assign({ key: auth.config.apiKey }, params)).slice(1);
|
|
const headers = await auth._getAdditionalHeaders();
|
|
headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/json';
|
|
if (auth.languageCode) {
|
|
headers["X-Firebase-Locale" /* HttpHeader.X_FIREBASE_LOCALE */] = auth.languageCode;
|
|
}
|
|
const fetchArgs = Object.assign({ method,
|
|
headers }, body);
|
|
/* Security-conscious server-side frameworks tend to have built in mitigations for referrer
|
|
problems". See the Cloudflare GitHub issue #487: Error: The 'referrerPolicy' field on
|
|
'RequestInitializerDict' is not implemented."
|
|
https://github.com/cloudflare/next-on-pages/issues/487 */
|
|
if (!util.isCloudflareWorker()) {
|
|
fetchArgs.referrerPolicy = 'no-referrer';
|
|
}
|
|
return FetchProvider.fetch()(_getFinalTarget(auth, auth.config.apiHost, path, query), fetchArgs);
|
|
});
|
|
}
|
|
async function _performFetchWithErrorHandling(auth, customErrorMap, fetchFn) {
|
|
auth._canInitEmulator = false;
|
|
const errorMap = Object.assign(Object.assign({}, SERVER_ERROR_MAP), customErrorMap);
|
|
try {
|
|
const networkTimeout = new NetworkTimeout(auth);
|
|
const response = await Promise.race([
|
|
fetchFn(),
|
|
networkTimeout.promise
|
|
]);
|
|
// If we've reached this point, the fetch succeeded and the networkTimeout
|
|
// didn't throw; clear the network timeout delay so that Node won't hang
|
|
networkTimeout.clearNetworkTimeout();
|
|
const json = await response.json();
|
|
if ('needConfirmation' in json) {
|
|
throw _makeTaggedError(auth, "account-exists-with-different-credential" /* AuthErrorCode.NEED_CONFIRMATION */, json);
|
|
}
|
|
if (response.ok && !('errorMessage' in json)) {
|
|
return json;
|
|
}
|
|
else {
|
|
const errorMessage = response.ok ? json.errorMessage : json.error.message;
|
|
const [serverErrorCode, serverErrorMessage] = errorMessage.split(' : ');
|
|
if (serverErrorCode === "FEDERATED_USER_ID_ALREADY_LINKED" /* ServerError.FEDERATED_USER_ID_ALREADY_LINKED */) {
|
|
throw _makeTaggedError(auth, "credential-already-in-use" /* AuthErrorCode.CREDENTIAL_ALREADY_IN_USE */, json);
|
|
}
|
|
else if (serverErrorCode === "EMAIL_EXISTS" /* ServerError.EMAIL_EXISTS */) {
|
|
throw _makeTaggedError(auth, "email-already-in-use" /* AuthErrorCode.EMAIL_EXISTS */, json);
|
|
}
|
|
else if (serverErrorCode === "USER_DISABLED" /* ServerError.USER_DISABLED */) {
|
|
throw _makeTaggedError(auth, "user-disabled" /* AuthErrorCode.USER_DISABLED */, json);
|
|
}
|
|
const authError = errorMap[serverErrorCode] ||
|
|
serverErrorCode
|
|
.toLowerCase()
|
|
.replace(/[_\s]+/g, '-');
|
|
if (serverErrorMessage) {
|
|
throw _errorWithCustomMessage(auth, authError, serverErrorMessage);
|
|
}
|
|
else {
|
|
_fail(auth, authError);
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
if (e instanceof util.FirebaseError) {
|
|
throw e;
|
|
}
|
|
// Changing this to a different error code will log user out when there is a network error
|
|
// because we treat any error other than NETWORK_REQUEST_FAILED as token is invalid.
|
|
// https://github.com/firebase/firebase-js-sdk/blob/4fbc73610d70be4e0852e7de63a39cb7897e8546/packages/auth/src/core/auth/auth_impl.ts#L309-L316
|
|
_fail(auth, "network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */, { 'message': String(e) });
|
|
}
|
|
}
|
|
async function _performSignInRequest(auth, method, path, request, customErrorMap = {}) {
|
|
const serverResponse = (await _performApiRequest(auth, method, path, request, customErrorMap));
|
|
if ('mfaPendingCredential' in serverResponse) {
|
|
_fail(auth, "multi-factor-auth-required" /* AuthErrorCode.MFA_REQUIRED */, {
|
|
_serverResponse: serverResponse
|
|
});
|
|
}
|
|
return serverResponse;
|
|
}
|
|
function _getFinalTarget(auth, host, path, query) {
|
|
const base = `${host}${path}?${query}`;
|
|
if (!auth.config.emulator) {
|
|
return `${auth.config.apiScheme}://${base}`;
|
|
}
|
|
return _emulatorUrl(auth.config, base);
|
|
}
|
|
function _parseEnforcementState(enforcementStateStr) {
|
|
switch (enforcementStateStr) {
|
|
case 'ENFORCE':
|
|
return "ENFORCE" /* EnforcementState.ENFORCE */;
|
|
case 'AUDIT':
|
|
return "AUDIT" /* EnforcementState.AUDIT */;
|
|
case 'OFF':
|
|
return "OFF" /* EnforcementState.OFF */;
|
|
default:
|
|
return "ENFORCEMENT_STATE_UNSPECIFIED" /* EnforcementState.ENFORCEMENT_STATE_UNSPECIFIED */;
|
|
}
|
|
}
|
|
class NetworkTimeout {
|
|
constructor(auth) {
|
|
this.auth = auth;
|
|
// Node timers and browser timers are fundamentally incompatible, but we
|
|
// don't care about the value here
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
this.timer = null;
|
|
this.promise = new Promise((_, reject) => {
|
|
this.timer = setTimeout(() => {
|
|
return reject(_createError(this.auth, "network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */));
|
|
}, DEFAULT_API_TIMEOUT_MS.get());
|
|
});
|
|
}
|
|
clearNetworkTimeout() {
|
|
clearTimeout(this.timer);
|
|
}
|
|
}
|
|
function _makeTaggedError(auth, code, response) {
|
|
const errorParams = {
|
|
appName: auth.name
|
|
};
|
|
if (response.email) {
|
|
errorParams.email = response.email;
|
|
}
|
|
if (response.phoneNumber) {
|
|
errorParams.phoneNumber = response.phoneNumber;
|
|
}
|
|
const error = _createError(auth, code, errorParams);
|
|
// We know customData is defined on error because errorParams is defined
|
|
error.customData._tokenResponse = response;
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function isV2(grecaptcha) {
|
|
return (grecaptcha !== undefined &&
|
|
grecaptcha.getResponse !== undefined);
|
|
}
|
|
function isEnterprise(grecaptcha) {
|
|
return (grecaptcha !== undefined &&
|
|
grecaptcha.enterprise !== undefined);
|
|
}
|
|
class RecaptchaConfig {
|
|
constructor(response) {
|
|
/**
|
|
* The reCAPTCHA site key.
|
|
*/
|
|
this.siteKey = '';
|
|
/**
|
|
* The list of providers and their enablement status for reCAPTCHA Enterprise.
|
|
*/
|
|
this.recaptchaEnforcementState = [];
|
|
if (response.recaptchaKey === undefined) {
|
|
throw new Error('recaptchaKey undefined');
|
|
}
|
|
// Example response.recaptchaKey: "projects/proj123/keys/sitekey123"
|
|
this.siteKey = response.recaptchaKey.split('/')[3];
|
|
this.recaptchaEnforcementState = response.recaptchaEnforcementState;
|
|
}
|
|
/**
|
|
* Returns the reCAPTCHA Enterprise enforcement state for the given provider.
|
|
*
|
|
* @param providerStr - The provider whose enforcement state is to be returned.
|
|
* @returns The reCAPTCHA Enterprise enforcement state for the given provider.
|
|
*/
|
|
getProviderEnforcementState(providerStr) {
|
|
if (!this.recaptchaEnforcementState ||
|
|
this.recaptchaEnforcementState.length === 0) {
|
|
return null;
|
|
}
|
|
for (const recaptchaEnforcementState of this.recaptchaEnforcementState) {
|
|
if (recaptchaEnforcementState.provider &&
|
|
recaptchaEnforcementState.provider === providerStr) {
|
|
return _parseEnforcementState(recaptchaEnforcementState.enforcementState);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Returns true if the reCAPTCHA Enterprise enforcement state for the provider is set to ENFORCE or AUDIT.
|
|
*
|
|
* @param providerStr - The provider whose enablement state is to be returned.
|
|
* @returns Whether or not reCAPTCHA Enterprise protection is enabled for the given provider.
|
|
*/
|
|
isProviderEnabled(providerStr) {
|
|
return (this.getProviderEnforcementState(providerStr) ===
|
|
"ENFORCE" /* EnforcementState.ENFORCE */ ||
|
|
this.getProviderEnforcementState(providerStr) === "AUDIT" /* EnforcementState.AUDIT */);
|
|
}
|
|
/**
|
|
* Returns true if reCAPTCHA Enterprise protection is enabled in at least one provider, otherwise
|
|
* returns false.
|
|
*
|
|
* @returns Whether or not reCAPTCHA Enterprise protection is enabled for at least one provider.
|
|
*/
|
|
isAnyProviderEnabled() {
|
|
return (this.isProviderEnabled("EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */) ||
|
|
this.isProviderEnabled("PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function getRecaptchaParams(auth) {
|
|
return ((await _performApiRequest(auth, "GET" /* HttpMethod.GET */, "/v1/recaptchaParams" /* Endpoint.GET_RECAPTCHA_PARAM */)).recaptchaSiteKey || '');
|
|
}
|
|
async function getRecaptchaConfig(auth, request) {
|
|
return _performApiRequest(auth, "GET" /* HttpMethod.GET */, "/v2/recaptchaConfig" /* Endpoint.GET_RECAPTCHA_CONFIG */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function deleteAccount(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:delete" /* Endpoint.DELETE_ACCOUNT */, request);
|
|
}
|
|
async function deleteLinkedAccounts(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:update" /* Endpoint.SET_ACCOUNT_INFO */, request);
|
|
}
|
|
async function getAccountInfo(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:lookup" /* Endpoint.GET_ACCOUNT_INFO */, request);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function utcTimestampToDateString(utcTimestamp) {
|
|
if (!utcTimestamp) {
|
|
return undefined;
|
|
}
|
|
try {
|
|
// Convert to date object.
|
|
const date = new Date(Number(utcTimestamp));
|
|
// Test date is valid.
|
|
if (!isNaN(date.getTime())) {
|
|
// Convert to UTC date string.
|
|
return date.toUTCString();
|
|
}
|
|
}
|
|
catch (e) {
|
|
// Do nothing. undefined will be returned.
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Returns a JSON Web Token (JWT) used to identify the user to a Firebase service.
|
|
*
|
|
* @remarks
|
|
* Returns the current token if it has not expired or if it will not expire in the next five
|
|
* minutes. Otherwise, this will refresh the token and return a new one.
|
|
*
|
|
* @param user - The user.
|
|
* @param forceRefresh - Force refresh regardless of token expiration.
|
|
*
|
|
* @public
|
|
*/
|
|
function getIdToken(user, forceRefresh = false) {
|
|
return util.getModularInstance(user).getIdToken(forceRefresh);
|
|
}
|
|
/**
|
|
* Returns a deserialized JSON Web Token (JWT) used to identify the user to a Firebase service.
|
|
*
|
|
* @remarks
|
|
* Returns the current token if it has not expired or if it will not expire in the next five
|
|
* minutes. Otherwise, this will refresh the token and return a new one.
|
|
*
|
|
* @param user - The user.
|
|
* @param forceRefresh - Force refresh regardless of token expiration.
|
|
*
|
|
* @public
|
|
*/
|
|
async function getIdTokenResult(user, forceRefresh = false) {
|
|
const userInternal = util.getModularInstance(user);
|
|
const token = await userInternal.getIdToken(forceRefresh);
|
|
const claims = _parseToken(token);
|
|
_assert(claims && claims.exp && claims.auth_time && claims.iat, userInternal.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const firebase = typeof claims.firebase === 'object' ? claims.firebase : undefined;
|
|
const signInProvider = firebase === null || firebase === void 0 ? void 0 : firebase['sign_in_provider'];
|
|
return {
|
|
claims,
|
|
token,
|
|
authTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.auth_time)),
|
|
issuedAtTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.iat)),
|
|
expirationTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.exp)),
|
|
signInProvider: signInProvider || null,
|
|
signInSecondFactor: (firebase === null || firebase === void 0 ? void 0 : firebase['sign_in_second_factor']) || null
|
|
};
|
|
}
|
|
function secondsStringToMilliseconds(seconds) {
|
|
return Number(seconds) * 1000;
|
|
}
|
|
function _parseToken(token) {
|
|
const [algorithm, payload, signature] = token.split('.');
|
|
if (algorithm === undefined ||
|
|
payload === undefined ||
|
|
signature === undefined) {
|
|
_logError('JWT malformed, contained fewer than 3 sections');
|
|
return null;
|
|
}
|
|
try {
|
|
const decoded = util.base64Decode(payload);
|
|
if (!decoded) {
|
|
_logError('Failed to decode base64 JWT payload');
|
|
return null;
|
|
}
|
|
return JSON.parse(decoded);
|
|
}
|
|
catch (e) {
|
|
_logError('Caught error parsing JWT payload as JSON', e === null || e === void 0 ? void 0 : e.toString());
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Extract expiresIn TTL from a token by subtracting the expiration from the issuance.
|
|
*/
|
|
function _tokenExpiresIn(token) {
|
|
const parsedToken = _parseToken(token);
|
|
_assert(parsedToken, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
_assert(typeof parsedToken.exp !== 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
_assert(typeof parsedToken.iat !== 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
return Number(parsedToken.exp) - Number(parsedToken.iat);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function _logoutIfInvalidated(user, promise, bypassAuthState = false) {
|
|
if (bypassAuthState) {
|
|
return promise;
|
|
}
|
|
try {
|
|
return await promise;
|
|
}
|
|
catch (e) {
|
|
if (e instanceof util.FirebaseError && isUserInvalidated(e)) {
|
|
if (user.auth.currentUser === user) {
|
|
await user.auth.signOut();
|
|
}
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
function isUserInvalidated({ code }) {
|
|
return (code === `auth/${"user-disabled" /* AuthErrorCode.USER_DISABLED */}` ||
|
|
code === `auth/${"user-token-expired" /* AuthErrorCode.TOKEN_EXPIRED */}`);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class ProactiveRefresh {
|
|
constructor(user) {
|
|
this.user = user;
|
|
this.isRunning = false;
|
|
// Node timers and browser timers return fundamentally different types.
|
|
// We don't actually care what the value is but TS won't accept unknown and
|
|
// we can't cast properly in both environments.
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
this.timerId = null;
|
|
this.errorBackoff = 30000 /* Duration.RETRY_BACKOFF_MIN */;
|
|
}
|
|
_start() {
|
|
if (this.isRunning) {
|
|
return;
|
|
}
|
|
this.isRunning = true;
|
|
this.schedule();
|
|
}
|
|
_stop() {
|
|
if (!this.isRunning) {
|
|
return;
|
|
}
|
|
this.isRunning = false;
|
|
if (this.timerId !== null) {
|
|
clearTimeout(this.timerId);
|
|
}
|
|
}
|
|
getInterval(wasError) {
|
|
var _a;
|
|
if (wasError) {
|
|
const interval = this.errorBackoff;
|
|
this.errorBackoff = Math.min(this.errorBackoff * 2, 960000 /* Duration.RETRY_BACKOFF_MAX */);
|
|
return interval;
|
|
}
|
|
else {
|
|
// Reset the error backoff
|
|
this.errorBackoff = 30000 /* Duration.RETRY_BACKOFF_MIN */;
|
|
const expTime = (_a = this.user.stsTokenManager.expirationTime) !== null && _a !== void 0 ? _a : 0;
|
|
const interval = expTime - Date.now() - 300000 /* Duration.OFFSET */;
|
|
return Math.max(0, interval);
|
|
}
|
|
}
|
|
schedule(wasError = false) {
|
|
if (!this.isRunning) {
|
|
// Just in case...
|
|
return;
|
|
}
|
|
const interval = this.getInterval(wasError);
|
|
this.timerId = setTimeout(async () => {
|
|
await this.iteration();
|
|
}, interval);
|
|
}
|
|
async iteration() {
|
|
try {
|
|
await this.user.getIdToken(true);
|
|
}
|
|
catch (e) {
|
|
// Only retry on network errors
|
|
if ((e === null || e === void 0 ? void 0 : e.code) ===
|
|
`auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
|
|
this.schedule(/* wasError */ true);
|
|
}
|
|
return;
|
|
}
|
|
this.schedule();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class UserMetadata {
|
|
constructor(createdAt, lastLoginAt) {
|
|
this.createdAt = createdAt;
|
|
this.lastLoginAt = lastLoginAt;
|
|
this._initializeTime();
|
|
}
|
|
_initializeTime() {
|
|
this.lastSignInTime = utcTimestampToDateString(this.lastLoginAt);
|
|
this.creationTime = utcTimestampToDateString(this.createdAt);
|
|
}
|
|
_copy(metadata) {
|
|
this.createdAt = metadata.createdAt;
|
|
this.lastLoginAt = metadata.lastLoginAt;
|
|
this._initializeTime();
|
|
}
|
|
toJSON() {
|
|
return {
|
|
createdAt: this.createdAt,
|
|
lastLoginAt: this.lastLoginAt
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function _reloadWithoutSaving(user) {
|
|
var _a;
|
|
const auth = user.auth;
|
|
const idToken = await user.getIdToken();
|
|
const response = await _logoutIfInvalidated(user, getAccountInfo(auth, { idToken }));
|
|
_assert(response === null || response === void 0 ? void 0 : response.users.length, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const coreAccount = response.users[0];
|
|
user._notifyReloadListener(coreAccount);
|
|
const newProviderData = ((_a = coreAccount.providerUserInfo) === null || _a === void 0 ? void 0 : _a.length)
|
|
? extractProviderData(coreAccount.providerUserInfo)
|
|
: [];
|
|
const providerData = mergeProviderData(user.providerData, newProviderData);
|
|
// Preserves the non-nonymous status of the stored user, even if no more
|
|
// credentials (federated or email/password) are linked to the user. If
|
|
// the user was previously anonymous, then use provider data to update.
|
|
// On the other hand, if it was not anonymous before, it should never be
|
|
// considered anonymous now.
|
|
const oldIsAnonymous = user.isAnonymous;
|
|
const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !(providerData === null || providerData === void 0 ? void 0 : providerData.length);
|
|
const isAnonymous = !oldIsAnonymous ? false : newIsAnonymous;
|
|
const updates = {
|
|
uid: coreAccount.localId,
|
|
displayName: coreAccount.displayName || null,
|
|
photoURL: coreAccount.photoUrl || null,
|
|
email: coreAccount.email || null,
|
|
emailVerified: coreAccount.emailVerified || false,
|
|
phoneNumber: coreAccount.phoneNumber || null,
|
|
tenantId: coreAccount.tenantId || null,
|
|
providerData,
|
|
metadata: new UserMetadata(coreAccount.createdAt, coreAccount.lastLoginAt),
|
|
isAnonymous
|
|
};
|
|
Object.assign(user, updates);
|
|
}
|
|
/**
|
|
* Reloads user account data, if signed in.
|
|
*
|
|
* @param user - The user.
|
|
*
|
|
* @public
|
|
*/
|
|
async function reload(user) {
|
|
const userInternal = util.getModularInstance(user);
|
|
await _reloadWithoutSaving(userInternal);
|
|
// Even though the current user hasn't changed, update
|
|
// current user will trigger a persistence update w/ the
|
|
// new info.
|
|
await userInternal.auth._persistUserIfCurrent(userInternal);
|
|
userInternal.auth._notifyListenersIfCurrent(userInternal);
|
|
}
|
|
function mergeProviderData(original, newData) {
|
|
const deduped = original.filter(o => !newData.some(n => n.providerId === o.providerId));
|
|
return [...deduped, ...newData];
|
|
}
|
|
function extractProviderData(providers) {
|
|
return providers.map((_a) => {
|
|
var { providerId } = _a, provider = tslib.__rest(_a, ["providerId"]);
|
|
return {
|
|
providerId,
|
|
uid: provider.rawId || '',
|
|
displayName: provider.displayName || null,
|
|
email: provider.email || null,
|
|
phoneNumber: provider.phoneNumber || null,
|
|
photoURL: provider.photoUrl || null
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function requestStsToken(auth, refreshToken) {
|
|
const response = await _performFetchWithErrorHandling(auth, {}, async () => {
|
|
const body = util.querystring({
|
|
'grant_type': 'refresh_token',
|
|
'refresh_token': refreshToken
|
|
}).slice(1);
|
|
const { tokenApiHost, apiKey } = auth.config;
|
|
const url = _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
|
|
const headers = await auth._getAdditionalHeaders();
|
|
headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/x-www-form-urlencoded';
|
|
return FetchProvider.fetch()(url, {
|
|
method: "POST" /* HttpMethod.POST */,
|
|
headers,
|
|
body
|
|
});
|
|
});
|
|
// The response comes back in snake_case. Convert to camel:
|
|
return {
|
|
accessToken: response.access_token,
|
|
expiresIn: response.expires_in,
|
|
refreshToken: response.refresh_token
|
|
};
|
|
}
|
|
async function revokeToken(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts:revokeToken" /* Endpoint.REVOKE_TOKEN */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* We need to mark this class as internal explicitly to exclude it in the public typings, because
|
|
* it references AuthInternal which has a circular dependency with UserInternal.
|
|
*
|
|
* @internal
|
|
*/
|
|
class StsTokenManager {
|
|
constructor() {
|
|
this.refreshToken = null;
|
|
this.accessToken = null;
|
|
this.expirationTime = null;
|
|
}
|
|
get isExpired() {
|
|
return (!this.expirationTime ||
|
|
Date.now() > this.expirationTime - 30000 /* Buffer.TOKEN_REFRESH */);
|
|
}
|
|
updateFromServerResponse(response) {
|
|
_assert(response.idToken, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
_assert(typeof response.idToken !== 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
_assert(typeof response.refreshToken !== 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const expiresIn = 'expiresIn' in response && typeof response.expiresIn !== 'undefined'
|
|
? Number(response.expiresIn)
|
|
: _tokenExpiresIn(response.idToken);
|
|
this.updateTokensAndExpiration(response.idToken, response.refreshToken, expiresIn);
|
|
}
|
|
updateFromIdToken(idToken) {
|
|
_assert(idToken.length !== 0, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const expiresIn = _tokenExpiresIn(idToken);
|
|
this.updateTokensAndExpiration(idToken, null, expiresIn);
|
|
}
|
|
async getToken(auth, forceRefresh = false) {
|
|
if (!forceRefresh && this.accessToken && !this.isExpired) {
|
|
return this.accessToken;
|
|
}
|
|
_assert(this.refreshToken, auth, "user-token-expired" /* AuthErrorCode.TOKEN_EXPIRED */);
|
|
if (this.refreshToken) {
|
|
await this.refresh(auth, this.refreshToken);
|
|
return this.accessToken;
|
|
}
|
|
return null;
|
|
}
|
|
clearRefreshToken() {
|
|
this.refreshToken = null;
|
|
}
|
|
async refresh(auth, oldToken) {
|
|
const { accessToken, refreshToken, expiresIn } = await requestStsToken(auth, oldToken);
|
|
this.updateTokensAndExpiration(accessToken, refreshToken, Number(expiresIn));
|
|
}
|
|
updateTokensAndExpiration(accessToken, refreshToken, expiresInSec) {
|
|
this.refreshToken = refreshToken || null;
|
|
this.accessToken = accessToken || null;
|
|
this.expirationTime = Date.now() + expiresInSec * 1000;
|
|
}
|
|
static fromJSON(appName, object) {
|
|
const { refreshToken, accessToken, expirationTime } = object;
|
|
const manager = new StsTokenManager();
|
|
if (refreshToken) {
|
|
_assert(typeof refreshToken === 'string', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, {
|
|
appName
|
|
});
|
|
manager.refreshToken = refreshToken;
|
|
}
|
|
if (accessToken) {
|
|
_assert(typeof accessToken === 'string', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, {
|
|
appName
|
|
});
|
|
manager.accessToken = accessToken;
|
|
}
|
|
if (expirationTime) {
|
|
_assert(typeof expirationTime === 'number', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, {
|
|
appName
|
|
});
|
|
manager.expirationTime = expirationTime;
|
|
}
|
|
return manager;
|
|
}
|
|
toJSON() {
|
|
return {
|
|
refreshToken: this.refreshToken,
|
|
accessToken: this.accessToken,
|
|
expirationTime: this.expirationTime
|
|
};
|
|
}
|
|
_assign(stsTokenManager) {
|
|
this.accessToken = stsTokenManager.accessToken;
|
|
this.refreshToken = stsTokenManager.refreshToken;
|
|
this.expirationTime = stsTokenManager.expirationTime;
|
|
}
|
|
_clone() {
|
|
return Object.assign(new StsTokenManager(), this.toJSON());
|
|
}
|
|
_performRefresh() {
|
|
return debugFail('not implemented');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function assertStringOrUndefined(assertion, appName) {
|
|
_assert(typeof assertion === 'string' || typeof assertion === 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, { appName });
|
|
}
|
|
class UserImpl {
|
|
constructor(_a) {
|
|
var { uid, auth, stsTokenManager } = _a, opt = tslib.__rest(_a, ["uid", "auth", "stsTokenManager"]);
|
|
// For the user object, provider is always Firebase.
|
|
this.providerId = "firebase" /* ProviderId.FIREBASE */;
|
|
this.proactiveRefresh = new ProactiveRefresh(this);
|
|
this.reloadUserInfo = null;
|
|
this.reloadListener = null;
|
|
this.uid = uid;
|
|
this.auth = auth;
|
|
this.stsTokenManager = stsTokenManager;
|
|
this.accessToken = stsTokenManager.accessToken;
|
|
this.displayName = opt.displayName || null;
|
|
this.email = opt.email || null;
|
|
this.emailVerified = opt.emailVerified || false;
|
|
this.phoneNumber = opt.phoneNumber || null;
|
|
this.photoURL = opt.photoURL || null;
|
|
this.isAnonymous = opt.isAnonymous || false;
|
|
this.tenantId = opt.tenantId || null;
|
|
this.providerData = opt.providerData ? [...opt.providerData] : [];
|
|
this.metadata = new UserMetadata(opt.createdAt || undefined, opt.lastLoginAt || undefined);
|
|
}
|
|
async getIdToken(forceRefresh) {
|
|
const accessToken = await _logoutIfInvalidated(this, this.stsTokenManager.getToken(this.auth, forceRefresh));
|
|
_assert(accessToken, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
if (this.accessToken !== accessToken) {
|
|
this.accessToken = accessToken;
|
|
await this.auth._persistUserIfCurrent(this);
|
|
this.auth._notifyListenersIfCurrent(this);
|
|
}
|
|
return accessToken;
|
|
}
|
|
getIdTokenResult(forceRefresh) {
|
|
return getIdTokenResult(this, forceRefresh);
|
|
}
|
|
reload() {
|
|
return reload(this);
|
|
}
|
|
_assign(user) {
|
|
if (this === user) {
|
|
return;
|
|
}
|
|
_assert(this.uid === user.uid, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
this.displayName = user.displayName;
|
|
this.photoURL = user.photoURL;
|
|
this.email = user.email;
|
|
this.emailVerified = user.emailVerified;
|
|
this.phoneNumber = user.phoneNumber;
|
|
this.isAnonymous = user.isAnonymous;
|
|
this.tenantId = user.tenantId;
|
|
this.providerData = user.providerData.map(userInfo => (Object.assign({}, userInfo)));
|
|
this.metadata._copy(user.metadata);
|
|
this.stsTokenManager._assign(user.stsTokenManager);
|
|
}
|
|
_clone(auth) {
|
|
const newUser = new UserImpl(Object.assign(Object.assign({}, this), { auth, stsTokenManager: this.stsTokenManager._clone() }));
|
|
newUser.metadata._copy(this.metadata);
|
|
return newUser;
|
|
}
|
|
_onReload(callback) {
|
|
// There should only ever be one listener, and that is a single instance of MultiFactorUser
|
|
_assert(!this.reloadListener, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
this.reloadListener = callback;
|
|
if (this.reloadUserInfo) {
|
|
this._notifyReloadListener(this.reloadUserInfo);
|
|
this.reloadUserInfo = null;
|
|
}
|
|
}
|
|
_notifyReloadListener(userInfo) {
|
|
if (this.reloadListener) {
|
|
this.reloadListener(userInfo);
|
|
}
|
|
else {
|
|
// If no listener is subscribed yet, save the result so it's available when they do subscribe
|
|
this.reloadUserInfo = userInfo;
|
|
}
|
|
}
|
|
_startProactiveRefresh() {
|
|
this.proactiveRefresh._start();
|
|
}
|
|
_stopProactiveRefresh() {
|
|
this.proactiveRefresh._stop();
|
|
}
|
|
async _updateTokensIfNecessary(response, reload = false) {
|
|
let tokensRefreshed = false;
|
|
if (response.idToken &&
|
|
response.idToken !== this.stsTokenManager.accessToken) {
|
|
this.stsTokenManager.updateFromServerResponse(response);
|
|
tokensRefreshed = true;
|
|
}
|
|
if (reload) {
|
|
await _reloadWithoutSaving(this);
|
|
}
|
|
await this.auth._persistUserIfCurrent(this);
|
|
if (tokensRefreshed) {
|
|
this.auth._notifyListenersIfCurrent(this);
|
|
}
|
|
}
|
|
async delete() {
|
|
if (app._isFirebaseServerApp(this.auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(this.auth));
|
|
}
|
|
const idToken = await this.getIdToken();
|
|
await _logoutIfInvalidated(this, deleteAccount(this.auth, { idToken }));
|
|
this.stsTokenManager.clearRefreshToken();
|
|
// TODO: Determine if cancellable-promises are necessary to use in this class so that delete()
|
|
// cancels pending actions...
|
|
return this.auth.signOut();
|
|
}
|
|
toJSON() {
|
|
return Object.assign(Object.assign({ uid: this.uid, email: this.email || undefined, emailVerified: this.emailVerified, displayName: this.displayName || undefined, isAnonymous: this.isAnonymous, photoURL: this.photoURL || undefined, phoneNumber: this.phoneNumber || undefined, tenantId: this.tenantId || undefined, providerData: this.providerData.map(userInfo => (Object.assign({}, userInfo))), stsTokenManager: this.stsTokenManager.toJSON(),
|
|
// Redirect event ID must be maintained in case there is a pending
|
|
// redirect event.
|
|
_redirectEventId: this._redirectEventId }, this.metadata.toJSON()), {
|
|
// Required for compatibility with the legacy SDK (go/firebase-auth-sdk-persistence-parsing):
|
|
apiKey: this.auth.config.apiKey, appName: this.auth.name });
|
|
}
|
|
get refreshToken() {
|
|
return this.stsTokenManager.refreshToken || '';
|
|
}
|
|
static _fromJSON(auth, object) {
|
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
const displayName = (_a = object.displayName) !== null && _a !== void 0 ? _a : undefined;
|
|
const email = (_b = object.email) !== null && _b !== void 0 ? _b : undefined;
|
|
const phoneNumber = (_c = object.phoneNumber) !== null && _c !== void 0 ? _c : undefined;
|
|
const photoURL = (_d = object.photoURL) !== null && _d !== void 0 ? _d : undefined;
|
|
const tenantId = (_e = object.tenantId) !== null && _e !== void 0 ? _e : undefined;
|
|
const _redirectEventId = (_f = object._redirectEventId) !== null && _f !== void 0 ? _f : undefined;
|
|
const createdAt = (_g = object.createdAt) !== null && _g !== void 0 ? _g : undefined;
|
|
const lastLoginAt = (_h = object.lastLoginAt) !== null && _h !== void 0 ? _h : undefined;
|
|
const { uid, emailVerified, isAnonymous, providerData, stsTokenManager: plainObjectTokenManager } = object;
|
|
_assert(uid && plainObjectTokenManager, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const stsTokenManager = StsTokenManager.fromJSON(this.name, plainObjectTokenManager);
|
|
_assert(typeof uid === 'string', auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
assertStringOrUndefined(displayName, auth.name);
|
|
assertStringOrUndefined(email, auth.name);
|
|
_assert(typeof emailVerified === 'boolean', auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
_assert(typeof isAnonymous === 'boolean', auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
assertStringOrUndefined(phoneNumber, auth.name);
|
|
assertStringOrUndefined(photoURL, auth.name);
|
|
assertStringOrUndefined(tenantId, auth.name);
|
|
assertStringOrUndefined(_redirectEventId, auth.name);
|
|
assertStringOrUndefined(createdAt, auth.name);
|
|
assertStringOrUndefined(lastLoginAt, auth.name);
|
|
const user = new UserImpl({
|
|
uid,
|
|
auth,
|
|
email,
|
|
emailVerified,
|
|
displayName,
|
|
isAnonymous,
|
|
photoURL,
|
|
phoneNumber,
|
|
tenantId,
|
|
stsTokenManager,
|
|
createdAt,
|
|
lastLoginAt
|
|
});
|
|
if (providerData && Array.isArray(providerData)) {
|
|
user.providerData = providerData.map(userInfo => (Object.assign({}, userInfo)));
|
|
}
|
|
if (_redirectEventId) {
|
|
user._redirectEventId = _redirectEventId;
|
|
}
|
|
return user;
|
|
}
|
|
/**
|
|
* Initialize a User from an idToken server response
|
|
* @param auth
|
|
* @param idTokenResponse
|
|
*/
|
|
static async _fromIdTokenResponse(auth, idTokenResponse, isAnonymous = false) {
|
|
const stsTokenManager = new StsTokenManager();
|
|
stsTokenManager.updateFromServerResponse(idTokenResponse);
|
|
// Initialize the Firebase Auth user.
|
|
const user = new UserImpl({
|
|
uid: idTokenResponse.localId,
|
|
auth,
|
|
stsTokenManager,
|
|
isAnonymous
|
|
});
|
|
// Updates the user info and data and resolves with a user instance.
|
|
await _reloadWithoutSaving(user);
|
|
return user;
|
|
}
|
|
/**
|
|
* Initialize a User from an idToken server response
|
|
* @param auth
|
|
* @param idTokenResponse
|
|
*/
|
|
static async _fromGetAccountInfoResponse(auth, response, idToken) {
|
|
const coreAccount = response.users[0];
|
|
_assert(coreAccount.localId !== undefined, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const providerData = coreAccount.providerUserInfo !== undefined
|
|
? extractProviderData(coreAccount.providerUserInfo)
|
|
: [];
|
|
const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !(providerData === null || providerData === void 0 ? void 0 : providerData.length);
|
|
const stsTokenManager = new StsTokenManager();
|
|
stsTokenManager.updateFromIdToken(idToken);
|
|
// Initialize the Firebase Auth user.
|
|
const user = new UserImpl({
|
|
uid: coreAccount.localId,
|
|
auth,
|
|
stsTokenManager,
|
|
isAnonymous
|
|
});
|
|
// update the user with data from the GetAccountInfo response.
|
|
const updates = {
|
|
uid: coreAccount.localId,
|
|
displayName: coreAccount.displayName || null,
|
|
photoURL: coreAccount.photoUrl || null,
|
|
email: coreAccount.email || null,
|
|
emailVerified: coreAccount.emailVerified || false,
|
|
phoneNumber: coreAccount.phoneNumber || null,
|
|
tenantId: coreAccount.tenantId || null,
|
|
providerData,
|
|
metadata: new UserMetadata(coreAccount.createdAt, coreAccount.lastLoginAt),
|
|
isAnonymous: !(coreAccount.email && coreAccount.passwordHash) &&
|
|
!(providerData === null || providerData === void 0 ? void 0 : providerData.length)
|
|
};
|
|
Object.assign(user, updates);
|
|
return user;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const instanceCache = new Map();
|
|
function _getInstance(cls) {
|
|
debugAssert(cls instanceof Function, 'Expected a class definition');
|
|
let instance = instanceCache.get(cls);
|
|
if (instance) {
|
|
debugAssert(instance instanceof cls, 'Instance stored in cache mismatched with class');
|
|
return instance;
|
|
}
|
|
instance = new cls();
|
|
instanceCache.set(cls, instance);
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class InMemoryPersistence {
|
|
constructor() {
|
|
this.type = "NONE" /* PersistenceType.NONE */;
|
|
this.storage = {};
|
|
}
|
|
async _isAvailable() {
|
|
return true;
|
|
}
|
|
async _set(key, value) {
|
|
this.storage[key] = value;
|
|
}
|
|
async _get(key) {
|
|
const value = this.storage[key];
|
|
return value === undefined ? null : value;
|
|
}
|
|
async _remove(key) {
|
|
delete this.storage[key];
|
|
}
|
|
_addListener(_key, _listener) {
|
|
// Listeners are not supported for in-memory storage since it cannot be shared across windows/workers
|
|
return;
|
|
}
|
|
_removeListener(_key, _listener) {
|
|
// Listeners are not supported for in-memory storage since it cannot be shared across windows/workers
|
|
return;
|
|
}
|
|
}
|
|
InMemoryPersistence.type = 'NONE';
|
|
/**
|
|
* An implementation of {@link Persistence} of type 'NONE'.
|
|
*
|
|
* @public
|
|
*/
|
|
const inMemoryPersistence = InMemoryPersistence;
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _persistenceKeyName(key, apiKey, appName) {
|
|
return `${"firebase" /* Namespace.PERSISTENCE */}:${key}:${apiKey}:${appName}`;
|
|
}
|
|
class PersistenceUserManager {
|
|
constructor(persistence, auth, userKey) {
|
|
this.persistence = persistence;
|
|
this.auth = auth;
|
|
this.userKey = userKey;
|
|
const { config, name } = this.auth;
|
|
this.fullUserKey = _persistenceKeyName(this.userKey, config.apiKey, name);
|
|
this.fullPersistenceKey = _persistenceKeyName("persistence" /* KeyName.PERSISTENCE_USER */, config.apiKey, name);
|
|
this.boundEventHandler = auth._onStorageEvent.bind(auth);
|
|
this.persistence._addListener(this.fullUserKey, this.boundEventHandler);
|
|
}
|
|
setCurrentUser(user) {
|
|
return this.persistence._set(this.fullUserKey, user.toJSON());
|
|
}
|
|
async getCurrentUser() {
|
|
const blob = await this.persistence._get(this.fullUserKey);
|
|
return blob ? UserImpl._fromJSON(this.auth, blob) : null;
|
|
}
|
|
removeCurrentUser() {
|
|
return this.persistence._remove(this.fullUserKey);
|
|
}
|
|
savePersistenceForRedirect() {
|
|
return this.persistence._set(this.fullPersistenceKey, this.persistence.type);
|
|
}
|
|
async setPersistence(newPersistence) {
|
|
if (this.persistence === newPersistence) {
|
|
return;
|
|
}
|
|
const currentUser = await this.getCurrentUser();
|
|
await this.removeCurrentUser();
|
|
this.persistence = newPersistence;
|
|
if (currentUser) {
|
|
return this.setCurrentUser(currentUser);
|
|
}
|
|
}
|
|
delete() {
|
|
this.persistence._removeListener(this.fullUserKey, this.boundEventHandler);
|
|
}
|
|
static async create(auth, persistenceHierarchy, userKey = "authUser" /* KeyName.AUTH_USER */) {
|
|
if (!persistenceHierarchy.length) {
|
|
return new PersistenceUserManager(_getInstance(inMemoryPersistence), auth, userKey);
|
|
}
|
|
// Eliminate any persistences that are not available
|
|
const availablePersistences = (await Promise.all(persistenceHierarchy.map(async (persistence) => {
|
|
if (await persistence._isAvailable()) {
|
|
return persistence;
|
|
}
|
|
return undefined;
|
|
}))).filter(persistence => persistence);
|
|
// Fall back to the first persistence listed, or in memory if none available
|
|
let selectedPersistence = availablePersistences[0] ||
|
|
_getInstance(inMemoryPersistence);
|
|
const key = _persistenceKeyName(userKey, auth.config.apiKey, auth.name);
|
|
// Pull out the existing user, setting the chosen persistence to that
|
|
// persistence if the user exists.
|
|
let userToMigrate = null;
|
|
// Note, here we check for a user in _all_ persistences, not just the
|
|
// ones deemed available. If we can migrate a user out of a broken
|
|
// persistence, we will (but only if that persistence supports migration).
|
|
for (const persistence of persistenceHierarchy) {
|
|
try {
|
|
const blob = await persistence._get(key);
|
|
if (blob) {
|
|
const user = UserImpl._fromJSON(auth, blob); // throws for unparsable blob (wrong format)
|
|
if (persistence !== selectedPersistence) {
|
|
userToMigrate = user;
|
|
}
|
|
selectedPersistence = persistence;
|
|
break;
|
|
}
|
|
}
|
|
catch (_a) { }
|
|
}
|
|
// If we find the user in a persistence that does support migration, use
|
|
// that migration path (of only persistences that support migration)
|
|
const migrationHierarchy = availablePersistences.filter(p => p._shouldAllowMigration);
|
|
// If the persistence does _not_ allow migration, just finish off here
|
|
if (!selectedPersistence._shouldAllowMigration ||
|
|
!migrationHierarchy.length) {
|
|
return new PersistenceUserManager(selectedPersistence, auth, userKey);
|
|
}
|
|
selectedPersistence = migrationHierarchy[0];
|
|
if (userToMigrate) {
|
|
// This normally shouldn't throw since chosenPersistence.isAvailable() is true, but if it does
|
|
// we'll just let it bubble to surface the error.
|
|
await selectedPersistence._set(key, userToMigrate.toJSON());
|
|
}
|
|
// Attempt to clear the key in other persistences but ignore errors. This helps prevent issues
|
|
// such as users getting stuck with a previous account after signing out and refreshing the tab.
|
|
await Promise.all(persistenceHierarchy.map(async (persistence) => {
|
|
if (persistence !== selectedPersistence) {
|
|
try {
|
|
await persistence._remove(key);
|
|
}
|
|
catch (_a) { }
|
|
}
|
|
}));
|
|
return new PersistenceUserManager(selectedPersistence, auth, userKey);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Determine the browser for the purposes of reporting usage to the API
|
|
*/
|
|
function _getBrowserName(userAgent) {
|
|
const ua = userAgent.toLowerCase();
|
|
if (ua.includes('opera/') || ua.includes('opr/') || ua.includes('opios/')) {
|
|
return "Opera" /* BrowserName.OPERA */;
|
|
}
|
|
else if (_isIEMobile(ua)) {
|
|
// Windows phone IEMobile browser.
|
|
return "IEMobile" /* BrowserName.IEMOBILE */;
|
|
}
|
|
else if (ua.includes('msie') || ua.includes('trident/')) {
|
|
return "IE" /* BrowserName.IE */;
|
|
}
|
|
else if (ua.includes('edge/')) {
|
|
return "Edge" /* BrowserName.EDGE */;
|
|
}
|
|
else if (_isFirefox(ua)) {
|
|
return "Firefox" /* BrowserName.FIREFOX */;
|
|
}
|
|
else if (ua.includes('silk/')) {
|
|
return "Silk" /* BrowserName.SILK */;
|
|
}
|
|
else if (_isBlackBerry(ua)) {
|
|
// Blackberry browser.
|
|
return "Blackberry" /* BrowserName.BLACKBERRY */;
|
|
}
|
|
else if (_isWebOS(ua)) {
|
|
// WebOS default browser.
|
|
return "Webos" /* BrowserName.WEBOS */;
|
|
}
|
|
else if (_isSafari(ua)) {
|
|
return "Safari" /* BrowserName.SAFARI */;
|
|
}
|
|
else if ((ua.includes('chrome/') || _isChromeIOS(ua)) &&
|
|
!ua.includes('edge/')) {
|
|
return "Chrome" /* BrowserName.CHROME */;
|
|
}
|
|
else if (_isAndroid(ua)) {
|
|
// Android stock browser.
|
|
return "Android" /* BrowserName.ANDROID */;
|
|
}
|
|
else {
|
|
// Most modern browsers have name/version at end of user agent string.
|
|
const re = /([a-zA-Z\d\.]+)\/[a-zA-Z\d\.]*$/;
|
|
const matches = userAgent.match(re);
|
|
if ((matches === null || matches === void 0 ? void 0 : matches.length) === 2) {
|
|
return matches[1];
|
|
}
|
|
}
|
|
return "Other" /* BrowserName.OTHER */;
|
|
}
|
|
function _isFirefox(ua = util.getUA()) {
|
|
return /firefox\//i.test(ua);
|
|
}
|
|
function _isSafari(userAgent = util.getUA()) {
|
|
const ua = userAgent.toLowerCase();
|
|
return (ua.includes('safari/') &&
|
|
!ua.includes('chrome/') &&
|
|
!ua.includes('crios/') &&
|
|
!ua.includes('android'));
|
|
}
|
|
function _isChromeIOS(ua = util.getUA()) {
|
|
return /crios\//i.test(ua);
|
|
}
|
|
function _isIEMobile(ua = util.getUA()) {
|
|
return /iemobile/i.test(ua);
|
|
}
|
|
function _isAndroid(ua = util.getUA()) {
|
|
return /android/i.test(ua);
|
|
}
|
|
function _isBlackBerry(ua = util.getUA()) {
|
|
return /blackberry/i.test(ua);
|
|
}
|
|
function _isWebOS(ua = util.getUA()) {
|
|
return /webos/i.test(ua);
|
|
}
|
|
function _isIOS(ua = util.getUA()) {
|
|
return (/iphone|ipad|ipod/i.test(ua) ||
|
|
(/macintosh/i.test(ua) && /mobile/i.test(ua)));
|
|
}
|
|
function _isIOS7Or8(ua = util.getUA()) {
|
|
return (/(iPad|iPhone|iPod).*OS 7_\d/i.test(ua) ||
|
|
/(iPad|iPhone|iPod).*OS 8_\d/i.test(ua));
|
|
}
|
|
function _isIOSStandalone(ua = util.getUA()) {
|
|
var _a;
|
|
return _isIOS(ua) && !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.standalone);
|
|
}
|
|
function _isIE10() {
|
|
return util.isIE() && document.documentMode === 10;
|
|
}
|
|
function _isMobileBrowser(ua = util.getUA()) {
|
|
// TODO: implement getBrowserName equivalent for OS.
|
|
return (_isIOS(ua) ||
|
|
_isAndroid(ua) ||
|
|
_isWebOS(ua) ||
|
|
_isBlackBerry(ua) ||
|
|
/windows phone/i.test(ua) ||
|
|
_isIEMobile(ua));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
* Determine the SDK version string
|
|
*/
|
|
function _getClientVersion(clientPlatform, frameworks = []) {
|
|
let reportedPlatform;
|
|
switch (clientPlatform) {
|
|
case "Browser" /* ClientPlatform.BROWSER */:
|
|
// In a browser environment, report the browser name.
|
|
reportedPlatform = _getBrowserName(util.getUA());
|
|
break;
|
|
case "Worker" /* ClientPlatform.WORKER */:
|
|
// Technically a worker runs from a browser but we need to differentiate a
|
|
// worker from a browser.
|
|
// For example: Chrome-Worker/JsCore/4.9.1/FirebaseCore-web.
|
|
reportedPlatform = `${_getBrowserName(util.getUA())}-${clientPlatform}`;
|
|
break;
|
|
default:
|
|
reportedPlatform = clientPlatform;
|
|
}
|
|
const reportedFrameworks = frameworks.length
|
|
? frameworks.join(',')
|
|
: 'FirebaseCore-web'; /* default value if no other framework is used */
|
|
return `${reportedPlatform}/${"JsCore" /* ClientImplementation.CORE */}/${app.SDK_VERSION}/${reportedFrameworks}`;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2022 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class AuthMiddlewareQueue {
|
|
constructor(auth) {
|
|
this.auth = auth;
|
|
this.queue = [];
|
|
}
|
|
pushCallback(callback, onAbort) {
|
|
// The callback could be sync or async. Wrap it into a
|
|
// function that is always async.
|
|
const wrappedCallback = (user) => new Promise((resolve, reject) => {
|
|
try {
|
|
const result = callback(user);
|
|
// Either resolve with existing promise or wrap a non-promise
|
|
// return value into a promise.
|
|
resolve(result);
|
|
}
|
|
catch (e) {
|
|
// Sync callback throws.
|
|
reject(e);
|
|
}
|
|
});
|
|
// Attach the onAbort if present
|
|
wrappedCallback.onAbort = onAbort;
|
|
this.queue.push(wrappedCallback);
|
|
const index = this.queue.length - 1;
|
|
return () => {
|
|
// Unsubscribe. Replace with no-op. Do not remove from array, or it will disturb
|
|
// indexing of other elements.
|
|
this.queue[index] = () => Promise.resolve();
|
|
};
|
|
}
|
|
async runMiddleware(nextUser) {
|
|
if (this.auth.currentUser === nextUser) {
|
|
return;
|
|
}
|
|
// While running the middleware, build a temporary stack of onAbort
|
|
// callbacks to call if one middleware callback rejects.
|
|
const onAbortStack = [];
|
|
try {
|
|
for (const beforeStateCallback of this.queue) {
|
|
await beforeStateCallback(nextUser);
|
|
// Only push the onAbort if the callback succeeds
|
|
if (beforeStateCallback.onAbort) {
|
|
onAbortStack.push(beforeStateCallback.onAbort);
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
// Run all onAbort, with separate try/catch to ignore any errors and
|
|
// continue
|
|
onAbortStack.reverse();
|
|
for (const onAbort of onAbortStack) {
|
|
try {
|
|
onAbort();
|
|
}
|
|
catch (_) {
|
|
/* swallow error */
|
|
}
|
|
}
|
|
throw this.auth._errorFactory.create("login-blocked" /* AuthErrorCode.LOGIN_BLOCKED */, {
|
|
originalMessage: e === null || e === void 0 ? void 0 : e.message
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2023 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Fetches the password policy for the currently set tenant or the project if no tenant is set.
|
|
*
|
|
* @param auth Auth object.
|
|
* @param request Password policy request.
|
|
* @returns Password policy response.
|
|
*/
|
|
async function _getPasswordPolicy(auth, request = {}) {
|
|
return _performApiRequest(auth, "GET" /* HttpMethod.GET */, "/v2/passwordPolicy" /* Endpoint.GET_PASSWORD_POLICY */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2023 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
// Minimum min password length enforced by the backend, even if no minimum length is set.
|
|
const MINIMUM_MIN_PASSWORD_LENGTH = 6;
|
|
/**
|
|
* Stores password policy requirements and provides password validation against the policy.
|
|
*
|
|
* @internal
|
|
*/
|
|
class PasswordPolicyImpl {
|
|
constructor(response) {
|
|
var _a, _b, _c, _d;
|
|
// Only include custom strength options defined in the response.
|
|
const responseOptions = response.customStrengthOptions;
|
|
this.customStrengthOptions = {};
|
|
// TODO: Remove once the backend is updated to include the minimum min password length instead of undefined when there is no minimum length set.
|
|
this.customStrengthOptions.minPasswordLength =
|
|
(_a = responseOptions.minPasswordLength) !== null && _a !== void 0 ? _a : MINIMUM_MIN_PASSWORD_LENGTH;
|
|
if (responseOptions.maxPasswordLength) {
|
|
this.customStrengthOptions.maxPasswordLength =
|
|
responseOptions.maxPasswordLength;
|
|
}
|
|
if (responseOptions.containsLowercaseCharacter !== undefined) {
|
|
this.customStrengthOptions.containsLowercaseLetter =
|
|
responseOptions.containsLowercaseCharacter;
|
|
}
|
|
if (responseOptions.containsUppercaseCharacter !== undefined) {
|
|
this.customStrengthOptions.containsUppercaseLetter =
|
|
responseOptions.containsUppercaseCharacter;
|
|
}
|
|
if (responseOptions.containsNumericCharacter !== undefined) {
|
|
this.customStrengthOptions.containsNumericCharacter =
|
|
responseOptions.containsNumericCharacter;
|
|
}
|
|
if (responseOptions.containsNonAlphanumericCharacter !== undefined) {
|
|
this.customStrengthOptions.containsNonAlphanumericCharacter =
|
|
responseOptions.containsNonAlphanumericCharacter;
|
|
}
|
|
this.enforcementState = response.enforcementState;
|
|
if (this.enforcementState === 'ENFORCEMENT_STATE_UNSPECIFIED') {
|
|
this.enforcementState = 'OFF';
|
|
}
|
|
// Use an empty string if no non-alphanumeric characters are specified in the response.
|
|
this.allowedNonAlphanumericCharacters =
|
|
(_c = (_b = response.allowedNonAlphanumericCharacters) === null || _b === void 0 ? void 0 : _b.join('')) !== null && _c !== void 0 ? _c : '';
|
|
this.forceUpgradeOnSignin = (_d = response.forceUpgradeOnSignin) !== null && _d !== void 0 ? _d : false;
|
|
this.schemaVersion = response.schemaVersion;
|
|
}
|
|
validatePassword(password) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
const status = {
|
|
isValid: true,
|
|
passwordPolicy: this
|
|
};
|
|
// Check the password length and character options.
|
|
this.validatePasswordLengthOptions(password, status);
|
|
this.validatePasswordCharacterOptions(password, status);
|
|
// Combine the status into single isValid property.
|
|
status.isValid && (status.isValid = (_a = status.meetsMinPasswordLength) !== null && _a !== void 0 ? _a : true);
|
|
status.isValid && (status.isValid = (_b = status.meetsMaxPasswordLength) !== null && _b !== void 0 ? _b : true);
|
|
status.isValid && (status.isValid = (_c = status.containsLowercaseLetter) !== null && _c !== void 0 ? _c : true);
|
|
status.isValid && (status.isValid = (_d = status.containsUppercaseLetter) !== null && _d !== void 0 ? _d : true);
|
|
status.isValid && (status.isValid = (_e = status.containsNumericCharacter) !== null && _e !== void 0 ? _e : true);
|
|
status.isValid && (status.isValid = (_f = status.containsNonAlphanumericCharacter) !== null && _f !== void 0 ? _f : true);
|
|
return status;
|
|
}
|
|
/**
|
|
* Validates that the password meets the length options for the policy.
|
|
*
|
|
* @param password Password to validate.
|
|
* @param status Validation status.
|
|
*/
|
|
validatePasswordLengthOptions(password, status) {
|
|
const minPasswordLength = this.customStrengthOptions.minPasswordLength;
|
|
const maxPasswordLength = this.customStrengthOptions.maxPasswordLength;
|
|
if (minPasswordLength) {
|
|
status.meetsMinPasswordLength = password.length >= minPasswordLength;
|
|
}
|
|
if (maxPasswordLength) {
|
|
status.meetsMaxPasswordLength = password.length <= maxPasswordLength;
|
|
}
|
|
}
|
|
/**
|
|
* Validates that the password meets the character options for the policy.
|
|
*
|
|
* @param password Password to validate.
|
|
* @param status Validation status.
|
|
*/
|
|
validatePasswordCharacterOptions(password, status) {
|
|
// Assign statuses for requirements even if the password is an empty string.
|
|
this.updatePasswordCharacterOptionsStatuses(status,
|
|
/* containsLowercaseCharacter= */ false,
|
|
/* containsUppercaseCharacter= */ false,
|
|
/* containsNumericCharacter= */ false,
|
|
/* containsNonAlphanumericCharacter= */ false);
|
|
let passwordChar;
|
|
for (let i = 0; i < password.length; i++) {
|
|
passwordChar = password.charAt(i);
|
|
this.updatePasswordCharacterOptionsStatuses(status,
|
|
/* containsLowercaseCharacter= */ passwordChar >= 'a' &&
|
|
passwordChar <= 'z',
|
|
/* containsUppercaseCharacter= */ passwordChar >= 'A' &&
|
|
passwordChar <= 'Z',
|
|
/* containsNumericCharacter= */ passwordChar >= '0' &&
|
|
passwordChar <= '9',
|
|
/* containsNonAlphanumericCharacter= */ this.allowedNonAlphanumericCharacters.includes(passwordChar));
|
|
}
|
|
}
|
|
/**
|
|
* Updates the running validation status with the statuses for the character options.
|
|
* Expected to be called each time a character is processed to update each option status
|
|
* based on the current character.
|
|
*
|
|
* @param status Validation status.
|
|
* @param containsLowercaseCharacter Whether the character is a lowercase letter.
|
|
* @param containsUppercaseCharacter Whether the character is an uppercase letter.
|
|
* @param containsNumericCharacter Whether the character is a numeric character.
|
|
* @param containsNonAlphanumericCharacter Whether the character is a non-alphanumeric character.
|
|
*/
|
|
updatePasswordCharacterOptionsStatuses(status, containsLowercaseCharacter, containsUppercaseCharacter, containsNumericCharacter, containsNonAlphanumericCharacter) {
|
|
if (this.customStrengthOptions.containsLowercaseLetter) {
|
|
status.containsLowercaseLetter || (status.containsLowercaseLetter = containsLowercaseCharacter);
|
|
}
|
|
if (this.customStrengthOptions.containsUppercaseLetter) {
|
|
status.containsUppercaseLetter || (status.containsUppercaseLetter = containsUppercaseCharacter);
|
|
}
|
|
if (this.customStrengthOptions.containsNumericCharacter) {
|
|
status.containsNumericCharacter || (status.containsNumericCharacter = containsNumericCharacter);
|
|
}
|
|
if (this.customStrengthOptions.containsNonAlphanumericCharacter) {
|
|
status.containsNonAlphanumericCharacter || (status.containsNonAlphanumericCharacter = containsNonAlphanumericCharacter);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class AuthImpl {
|
|
constructor(app, heartbeatServiceProvider, appCheckServiceProvider, config) {
|
|
this.app = app;
|
|
this.heartbeatServiceProvider = heartbeatServiceProvider;
|
|
this.appCheckServiceProvider = appCheckServiceProvider;
|
|
this.config = config;
|
|
this.currentUser = null;
|
|
this.emulatorConfig = null;
|
|
this.operations = Promise.resolve();
|
|
this.authStateSubscription = new Subscription(this);
|
|
this.idTokenSubscription = new Subscription(this);
|
|
this.beforeStateQueue = new AuthMiddlewareQueue(this);
|
|
this.redirectUser = null;
|
|
this.isProactiveRefreshEnabled = false;
|
|
this.EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION = 1;
|
|
// Any network calls will set this to true and prevent subsequent emulator
|
|
// initialization
|
|
this._canInitEmulator = true;
|
|
this._isInitialized = false;
|
|
this._deleted = false;
|
|
this._initializationPromise = null;
|
|
this._popupRedirectResolver = null;
|
|
this._errorFactory = _DEFAULT_AUTH_ERROR_FACTORY;
|
|
this._agentRecaptchaConfig = null;
|
|
this._tenantRecaptchaConfigs = {};
|
|
this._projectPasswordPolicy = null;
|
|
this._tenantPasswordPolicies = {};
|
|
// Tracks the last notified UID for state change listeners to prevent
|
|
// repeated calls to the callbacks. Undefined means it's never been
|
|
// called, whereas null means it's been called with a signed out user
|
|
this.lastNotifiedUid = undefined;
|
|
this.languageCode = null;
|
|
this.tenantId = null;
|
|
this.settings = { appVerificationDisabledForTesting: false };
|
|
this.frameworks = [];
|
|
this.name = app.name;
|
|
this.clientVersion = config.sdkClientVersion;
|
|
}
|
|
_initializeWithPersistence(persistenceHierarchy, popupRedirectResolver) {
|
|
if (popupRedirectResolver) {
|
|
this._popupRedirectResolver = _getInstance(popupRedirectResolver);
|
|
}
|
|
// Have to check for app deletion throughout initialization (after each
|
|
// promise resolution)
|
|
this._initializationPromise = this.queue(async () => {
|
|
var _a, _b;
|
|
if (this._deleted) {
|
|
return;
|
|
}
|
|
this.persistenceManager = await PersistenceUserManager.create(this, persistenceHierarchy);
|
|
if (this._deleted) {
|
|
return;
|
|
}
|
|
// Initialize the resolver early if necessary (only applicable to web:
|
|
// this will cause the iframe to load immediately in certain cases)
|
|
if ((_a = this._popupRedirectResolver) === null || _a === void 0 ? void 0 : _a._shouldInitProactively) {
|
|
// If this fails, don't halt auth loading
|
|
try {
|
|
await this._popupRedirectResolver._initialize(this);
|
|
}
|
|
catch (e) {
|
|
/* Ignore the error */
|
|
}
|
|
}
|
|
await this.initializeCurrentUser(popupRedirectResolver);
|
|
this.lastNotifiedUid = ((_b = this.currentUser) === null || _b === void 0 ? void 0 : _b.uid) || null;
|
|
if (this._deleted) {
|
|
return;
|
|
}
|
|
this._isInitialized = true;
|
|
});
|
|
return this._initializationPromise;
|
|
}
|
|
/**
|
|
* If the persistence is changed in another window, the user manager will let us know
|
|
*/
|
|
async _onStorageEvent() {
|
|
if (this._deleted) {
|
|
return;
|
|
}
|
|
const user = await this.assertedPersistence.getCurrentUser();
|
|
if (!this.currentUser && !user) {
|
|
// No change, do nothing (was signed out and remained signed out).
|
|
return;
|
|
}
|
|
// If the same user is to be synchronized.
|
|
if (this.currentUser && user && this.currentUser.uid === user.uid) {
|
|
// Data update, simply copy data changes.
|
|
this._currentUser._assign(user);
|
|
// If tokens changed from previous user tokens, this will trigger
|
|
// notifyAuthListeners_.
|
|
await this.currentUser.getIdToken();
|
|
return;
|
|
}
|
|
// Update current Auth state. Either a new login or logout.
|
|
// Skip blocking callbacks, they should not apply to a change in another tab.
|
|
await this._updateCurrentUser(user, /* skipBeforeStateCallbacks */ true);
|
|
}
|
|
async initializeCurrentUserFromIdToken(idToken) {
|
|
try {
|
|
const response = await getAccountInfo(this, { idToken });
|
|
const user = await UserImpl._fromGetAccountInfoResponse(this, response, idToken);
|
|
await this.directlySetCurrentUser(user);
|
|
}
|
|
catch (err) {
|
|
console.warn('FirebaseServerApp could not login user with provided authIdToken: ', err);
|
|
await this.directlySetCurrentUser(null);
|
|
}
|
|
}
|
|
async initializeCurrentUser(popupRedirectResolver) {
|
|
var _a;
|
|
if (app._isFirebaseServerApp(this.app)) {
|
|
const idToken = this.app.settings.authIdToken;
|
|
if (idToken) {
|
|
// Start the auth operation in the next tick to allow a moment for the customer's app to
|
|
// attach an emulator, if desired.
|
|
return new Promise(resolve => {
|
|
setTimeout(() => this.initializeCurrentUserFromIdToken(idToken).then(resolve, resolve));
|
|
});
|
|
}
|
|
else {
|
|
return this.directlySetCurrentUser(null);
|
|
}
|
|
}
|
|
// First check to see if we have a pending redirect event.
|
|
const previouslyStoredUser = (await this.assertedPersistence.getCurrentUser());
|
|
let futureCurrentUser = previouslyStoredUser;
|
|
let needsTocheckMiddleware = false;
|
|
if (popupRedirectResolver && this.config.authDomain) {
|
|
await this.getOrInitRedirectPersistenceManager();
|
|
const redirectUserEventId = (_a = this.redirectUser) === null || _a === void 0 ? void 0 : _a._redirectEventId;
|
|
const storedUserEventId = futureCurrentUser === null || futureCurrentUser === void 0 ? void 0 : futureCurrentUser._redirectEventId;
|
|
const result = await this.tryRedirectSignIn(popupRedirectResolver);
|
|
// If the stored user (i.e. the old "currentUser") has a redirectId that
|
|
// matches the redirect user, then we want to initially sign in with the
|
|
// new user object from result.
|
|
// TODO(samgho): More thoroughly test all of this
|
|
if ((!redirectUserEventId || redirectUserEventId === storedUserEventId) &&
|
|
(result === null || result === void 0 ? void 0 : result.user)) {
|
|
futureCurrentUser = result.user;
|
|
needsTocheckMiddleware = true;
|
|
}
|
|
}
|
|
// If no user in persistence, there is no current user. Set to null.
|
|
if (!futureCurrentUser) {
|
|
return this.directlySetCurrentUser(null);
|
|
}
|
|
if (!futureCurrentUser._redirectEventId) {
|
|
// This isn't a redirect link operation, we can reload and bail.
|
|
// First though, ensure that we check the middleware is happy.
|
|
if (needsTocheckMiddleware) {
|
|
try {
|
|
await this.beforeStateQueue.runMiddleware(futureCurrentUser);
|
|
}
|
|
catch (e) {
|
|
futureCurrentUser = previouslyStoredUser;
|
|
// We know this is available since the bit is only set when the
|
|
// resolver is available
|
|
this._popupRedirectResolver._overrideRedirectResult(this, () => Promise.reject(e));
|
|
}
|
|
}
|
|
if (futureCurrentUser) {
|
|
return this.reloadAndSetCurrentUserOrClear(futureCurrentUser);
|
|
}
|
|
else {
|
|
return this.directlySetCurrentUser(null);
|
|
}
|
|
}
|
|
_assert(this._popupRedirectResolver, this, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
await this.getOrInitRedirectPersistenceManager();
|
|
// If the redirect user's event ID matches the current user's event ID,
|
|
// DO NOT reload the current user, otherwise they'll be cleared from storage.
|
|
// This is important for the reauthenticateWithRedirect() flow.
|
|
if (this.redirectUser &&
|
|
this.redirectUser._redirectEventId === futureCurrentUser._redirectEventId) {
|
|
return this.directlySetCurrentUser(futureCurrentUser);
|
|
}
|
|
return this.reloadAndSetCurrentUserOrClear(futureCurrentUser);
|
|
}
|
|
async tryRedirectSignIn(redirectResolver) {
|
|
// The redirect user needs to be checked (and signed in if available)
|
|
// during auth initialization. All of the normal sign in and link/reauth
|
|
// flows call back into auth and push things onto the promise queue. We
|
|
// need to await the result of the redirect sign in *inside the promise
|
|
// queue*. This presents a problem: we run into deadlock. See:
|
|
// ┌> [Initialization] ─────┐
|
|
// ┌> [<other queue tasks>] │
|
|
// └─ [getRedirectResult] <─┘
|
|
// where [] are tasks on the queue and arrows denote awaits
|
|
// Initialization will never complete because it's waiting on something
|
|
// that's waiting for initialization to complete!
|
|
//
|
|
// Instead, this method calls getRedirectResult() (stored in
|
|
// _completeRedirectFn) with an optional parameter that instructs all of
|
|
// the underlying auth operations to skip anything that mutates auth state.
|
|
let result = null;
|
|
try {
|
|
// We know this._popupRedirectResolver is set since redirectResolver
|
|
// is passed in. The _completeRedirectFn expects the unwrapped extern.
|
|
result = await this._popupRedirectResolver._completeRedirectFn(this, redirectResolver, true);
|
|
}
|
|
catch (e) {
|
|
// Swallow any errors here; the code can retrieve them in
|
|
// getRedirectResult().
|
|
await this._setRedirectUser(null);
|
|
}
|
|
return result;
|
|
}
|
|
async reloadAndSetCurrentUserOrClear(user) {
|
|
try {
|
|
await _reloadWithoutSaving(user);
|
|
}
|
|
catch (e) {
|
|
if ((e === null || e === void 0 ? void 0 : e.code) !==
|
|
`auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
|
|
// Something's wrong with the user's token. Log them out and remove
|
|
// them from storage
|
|
return this.directlySetCurrentUser(null);
|
|
}
|
|
}
|
|
return this.directlySetCurrentUser(user);
|
|
}
|
|
useDeviceLanguage() {
|
|
this.languageCode = _getUserLanguage();
|
|
}
|
|
async _delete() {
|
|
this._deleted = true;
|
|
}
|
|
async updateCurrentUser(userExtern) {
|
|
if (app._isFirebaseServerApp(this.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(this));
|
|
}
|
|
// The public updateCurrentUser method needs to make a copy of the user,
|
|
// and also check that the project matches
|
|
const user = userExtern
|
|
? util.getModularInstance(userExtern)
|
|
: null;
|
|
if (user) {
|
|
_assert(user.auth.config.apiKey === this.config.apiKey, this, "invalid-user-token" /* AuthErrorCode.INVALID_AUTH */);
|
|
}
|
|
return this._updateCurrentUser(user && user._clone(this));
|
|
}
|
|
async _updateCurrentUser(user, skipBeforeStateCallbacks = false) {
|
|
if (this._deleted) {
|
|
return;
|
|
}
|
|
if (user) {
|
|
_assert(this.tenantId === user.tenantId, this, "tenant-id-mismatch" /* AuthErrorCode.TENANT_ID_MISMATCH */);
|
|
}
|
|
if (!skipBeforeStateCallbacks) {
|
|
await this.beforeStateQueue.runMiddleware(user);
|
|
}
|
|
return this.queue(async () => {
|
|
await this.directlySetCurrentUser(user);
|
|
this.notifyAuthListeners();
|
|
});
|
|
}
|
|
async signOut() {
|
|
if (app._isFirebaseServerApp(this.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(this));
|
|
}
|
|
// Run first, to block _setRedirectUser() if any callbacks fail.
|
|
await this.beforeStateQueue.runMiddleware(null);
|
|
// Clear the redirect user when signOut is called
|
|
if (this.redirectPersistenceManager || this._popupRedirectResolver) {
|
|
await this._setRedirectUser(null);
|
|
}
|
|
// Prevent callbacks from being called again in _updateCurrentUser, as
|
|
// they were already called in the first line.
|
|
return this._updateCurrentUser(null, /* skipBeforeStateCallbacks */ true);
|
|
}
|
|
setPersistence(persistence) {
|
|
if (app._isFirebaseServerApp(this.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(this));
|
|
}
|
|
return this.queue(async () => {
|
|
await this.assertedPersistence.setPersistence(_getInstance(persistence));
|
|
});
|
|
}
|
|
_getRecaptchaConfig() {
|
|
if (this.tenantId == null) {
|
|
return this._agentRecaptchaConfig;
|
|
}
|
|
else {
|
|
return this._tenantRecaptchaConfigs[this.tenantId];
|
|
}
|
|
}
|
|
async validatePassword(password) {
|
|
if (!this._getPasswordPolicyInternal()) {
|
|
await this._updatePasswordPolicy();
|
|
}
|
|
// Password policy will be defined after fetching.
|
|
const passwordPolicy = this._getPasswordPolicyInternal();
|
|
// Check that the policy schema version is supported by the SDK.
|
|
// TODO: Update this logic to use a max supported policy schema version once we have multiple schema versions.
|
|
if (passwordPolicy.schemaVersion !==
|
|
this.EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION) {
|
|
return Promise.reject(this._errorFactory.create("unsupported-password-policy-schema-version" /* AuthErrorCode.UNSUPPORTED_PASSWORD_POLICY_SCHEMA_VERSION */, {}));
|
|
}
|
|
return passwordPolicy.validatePassword(password);
|
|
}
|
|
_getPasswordPolicyInternal() {
|
|
if (this.tenantId === null) {
|
|
return this._projectPasswordPolicy;
|
|
}
|
|
else {
|
|
return this._tenantPasswordPolicies[this.tenantId];
|
|
}
|
|
}
|
|
async _updatePasswordPolicy() {
|
|
const response = await _getPasswordPolicy(this);
|
|
const passwordPolicy = new PasswordPolicyImpl(response);
|
|
if (this.tenantId === null) {
|
|
this._projectPasswordPolicy = passwordPolicy;
|
|
}
|
|
else {
|
|
this._tenantPasswordPolicies[this.tenantId] = passwordPolicy;
|
|
}
|
|
}
|
|
_getPersistence() {
|
|
return this.assertedPersistence.persistence.type;
|
|
}
|
|
_updateErrorMap(errorMap) {
|
|
this._errorFactory = new util.ErrorFactory('auth', 'Firebase', errorMap());
|
|
}
|
|
onAuthStateChanged(nextOrObserver, error, completed) {
|
|
return this.registerStateListener(this.authStateSubscription, nextOrObserver, error, completed);
|
|
}
|
|
beforeAuthStateChanged(callback, onAbort) {
|
|
return this.beforeStateQueue.pushCallback(callback, onAbort);
|
|
}
|
|
onIdTokenChanged(nextOrObserver, error, completed) {
|
|
return this.registerStateListener(this.idTokenSubscription, nextOrObserver, error, completed);
|
|
}
|
|
authStateReady() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.currentUser) {
|
|
resolve();
|
|
}
|
|
else {
|
|
const unsubscribe = this.onAuthStateChanged(() => {
|
|
unsubscribe();
|
|
resolve();
|
|
}, reject);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Revokes the given access token. Currently only supports Apple OAuth access tokens.
|
|
*/
|
|
async revokeAccessToken(token) {
|
|
if (this.currentUser) {
|
|
const idToken = await this.currentUser.getIdToken();
|
|
// Generalize this to accept other providers once supported.
|
|
const request = {
|
|
providerId: 'apple.com',
|
|
tokenType: "ACCESS_TOKEN" /* TokenType.ACCESS_TOKEN */,
|
|
token,
|
|
idToken
|
|
};
|
|
if (this.tenantId != null) {
|
|
request.tenantId = this.tenantId;
|
|
}
|
|
await revokeToken(this, request);
|
|
}
|
|
}
|
|
toJSON() {
|
|
var _a;
|
|
return {
|
|
apiKey: this.config.apiKey,
|
|
authDomain: this.config.authDomain,
|
|
appName: this.name,
|
|
currentUser: (_a = this._currentUser) === null || _a === void 0 ? void 0 : _a.toJSON()
|
|
};
|
|
}
|
|
async _setRedirectUser(user, popupRedirectResolver) {
|
|
const redirectManager = await this.getOrInitRedirectPersistenceManager(popupRedirectResolver);
|
|
return user === null
|
|
? redirectManager.removeCurrentUser()
|
|
: redirectManager.setCurrentUser(user);
|
|
}
|
|
async getOrInitRedirectPersistenceManager(popupRedirectResolver) {
|
|
if (!this.redirectPersistenceManager) {
|
|
const resolver = (popupRedirectResolver && _getInstance(popupRedirectResolver)) ||
|
|
this._popupRedirectResolver;
|
|
_assert(resolver, this, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
this.redirectPersistenceManager = await PersistenceUserManager.create(this, [_getInstance(resolver._redirectPersistence)], "redirectUser" /* KeyName.REDIRECT_USER */);
|
|
this.redirectUser =
|
|
await this.redirectPersistenceManager.getCurrentUser();
|
|
}
|
|
return this.redirectPersistenceManager;
|
|
}
|
|
async _redirectUserForId(id) {
|
|
var _a, _b;
|
|
// Make sure we've cleared any pending persistence actions if we're not in
|
|
// the initializer
|
|
if (this._isInitialized) {
|
|
await this.queue(async () => { });
|
|
}
|
|
if (((_a = this._currentUser) === null || _a === void 0 ? void 0 : _a._redirectEventId) === id) {
|
|
return this._currentUser;
|
|
}
|
|
if (((_b = this.redirectUser) === null || _b === void 0 ? void 0 : _b._redirectEventId) === id) {
|
|
return this.redirectUser;
|
|
}
|
|
return null;
|
|
}
|
|
async _persistUserIfCurrent(user) {
|
|
if (user === this.currentUser) {
|
|
return this.queue(async () => this.directlySetCurrentUser(user));
|
|
}
|
|
}
|
|
/** Notifies listeners only if the user is current */
|
|
_notifyListenersIfCurrent(user) {
|
|
if (user === this.currentUser) {
|
|
this.notifyAuthListeners();
|
|
}
|
|
}
|
|
_key() {
|
|
return `${this.config.authDomain}:${this.config.apiKey}:${this.name}`;
|
|
}
|
|
_startProactiveRefresh() {
|
|
this.isProactiveRefreshEnabled = true;
|
|
if (this.currentUser) {
|
|
this._currentUser._startProactiveRefresh();
|
|
}
|
|
}
|
|
_stopProactiveRefresh() {
|
|
this.isProactiveRefreshEnabled = false;
|
|
if (this.currentUser) {
|
|
this._currentUser._stopProactiveRefresh();
|
|
}
|
|
}
|
|
/** Returns the current user cast as the internal type */
|
|
get _currentUser() {
|
|
return this.currentUser;
|
|
}
|
|
notifyAuthListeners() {
|
|
var _a, _b;
|
|
if (!this._isInitialized) {
|
|
return;
|
|
}
|
|
this.idTokenSubscription.next(this.currentUser);
|
|
const currentUid = (_b = (_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.uid) !== null && _b !== void 0 ? _b : null;
|
|
if (this.lastNotifiedUid !== currentUid) {
|
|
this.lastNotifiedUid = currentUid;
|
|
this.authStateSubscription.next(this.currentUser);
|
|
}
|
|
}
|
|
registerStateListener(subscription, nextOrObserver, error, completed) {
|
|
if (this._deleted) {
|
|
return () => { };
|
|
}
|
|
const cb = typeof nextOrObserver === 'function'
|
|
? nextOrObserver
|
|
: nextOrObserver.next.bind(nextOrObserver);
|
|
let isUnsubscribed = false;
|
|
const promise = this._isInitialized
|
|
? Promise.resolve()
|
|
: this._initializationPromise;
|
|
_assert(promise, this, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
// The callback needs to be called asynchronously per the spec.
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
promise.then(() => {
|
|
if (isUnsubscribed) {
|
|
return;
|
|
}
|
|
cb(this.currentUser);
|
|
});
|
|
if (typeof nextOrObserver === 'function') {
|
|
const unsubscribe = subscription.addObserver(nextOrObserver, error, completed);
|
|
return () => {
|
|
isUnsubscribed = true;
|
|
unsubscribe();
|
|
};
|
|
}
|
|
else {
|
|
const unsubscribe = subscription.addObserver(nextOrObserver);
|
|
return () => {
|
|
isUnsubscribed = true;
|
|
unsubscribe();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Unprotected (from race conditions) method to set the current user. This
|
|
* should only be called from within a queued callback. This is necessary
|
|
* because the queue shouldn't rely on another queued callback.
|
|
*/
|
|
async directlySetCurrentUser(user) {
|
|
if (this.currentUser && this.currentUser !== user) {
|
|
this._currentUser._stopProactiveRefresh();
|
|
}
|
|
if (user && this.isProactiveRefreshEnabled) {
|
|
user._startProactiveRefresh();
|
|
}
|
|
this.currentUser = user;
|
|
if (user) {
|
|
await this.assertedPersistence.setCurrentUser(user);
|
|
}
|
|
else {
|
|
await this.assertedPersistence.removeCurrentUser();
|
|
}
|
|
}
|
|
queue(action) {
|
|
// In case something errors, the callback still should be called in order
|
|
// to keep the promise chain alive
|
|
this.operations = this.operations.then(action, action);
|
|
return this.operations;
|
|
}
|
|
get assertedPersistence() {
|
|
_assert(this.persistenceManager, this, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
return this.persistenceManager;
|
|
}
|
|
_logFramework(framework) {
|
|
if (!framework || this.frameworks.includes(framework)) {
|
|
return;
|
|
}
|
|
this.frameworks.push(framework);
|
|
// Sort alphabetically so that "FirebaseCore-web,FirebaseUI-web" and
|
|
// "FirebaseUI-web,FirebaseCore-web" aren't viewed as different.
|
|
this.frameworks.sort();
|
|
this.clientVersion = _getClientVersion(this.config.clientPlatform, this._getFrameworks());
|
|
}
|
|
_getFrameworks() {
|
|
return this.frameworks;
|
|
}
|
|
async _getAdditionalHeaders() {
|
|
var _a;
|
|
// Additional headers on every request
|
|
const headers = {
|
|
["X-Client-Version" /* HttpHeader.X_CLIENT_VERSION */]: this.clientVersion
|
|
};
|
|
if (this.app.options.appId) {
|
|
headers["X-Firebase-gmpid" /* HttpHeader.X_FIREBASE_GMPID */] = this.app.options.appId;
|
|
}
|
|
// If the heartbeat service exists, add the heartbeat string
|
|
const heartbeatsHeader = await ((_a = this.heartbeatServiceProvider
|
|
.getImmediate({
|
|
optional: true
|
|
})) === null || _a === void 0 ? void 0 : _a.getHeartbeatsHeader());
|
|
if (heartbeatsHeader) {
|
|
headers["X-Firebase-Client" /* HttpHeader.X_FIREBASE_CLIENT */] = heartbeatsHeader;
|
|
}
|
|
// If the App Check service exists, add the App Check token in the headers
|
|
const appCheckToken = await this._getAppCheckToken();
|
|
if (appCheckToken) {
|
|
headers["X-Firebase-AppCheck" /* HttpHeader.X_FIREBASE_APP_CHECK */] = appCheckToken;
|
|
}
|
|
return headers;
|
|
}
|
|
async _getAppCheckToken() {
|
|
var _a;
|
|
const appCheckTokenResult = await ((_a = this.appCheckServiceProvider
|
|
.getImmediate({ optional: true })) === null || _a === void 0 ? void 0 : _a.getToken());
|
|
if (appCheckTokenResult === null || appCheckTokenResult === void 0 ? void 0 : appCheckTokenResult.error) {
|
|
// Context: appCheck.getToken() will never throw even if an error happened.
|
|
// In the error case, a dummy token will be returned along with an error field describing
|
|
// the error. In general, we shouldn't care about the error condition and just use
|
|
// the token (actual or dummy) to send requests.
|
|
_logWarn(`Error while retrieving App Check token: ${appCheckTokenResult.error}`);
|
|
}
|
|
return appCheckTokenResult === null || appCheckTokenResult === void 0 ? void 0 : appCheckTokenResult.token;
|
|
}
|
|
}
|
|
/**
|
|
* Method to be used to cast down to our private implementation of Auth.
|
|
* It will also handle unwrapping from the compat type if necessary
|
|
*
|
|
* @param auth Auth object passed in from developer
|
|
*/
|
|
function _castAuth(auth) {
|
|
return util.getModularInstance(auth);
|
|
}
|
|
/** Helper class to wrap subscriber logic */
|
|
class Subscription {
|
|
constructor(auth) {
|
|
this.auth = auth;
|
|
this.observer = null;
|
|
this.addObserver = util.createSubscribe(observer => (this.observer = observer));
|
|
}
|
|
get next() {
|
|
_assert(this.observer, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
return this.observer.next.bind(this.observer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
let externalJSProvider = {
|
|
async loadJS() {
|
|
throw new Error('Unable to load external scripts');
|
|
},
|
|
recaptchaV2Script: '',
|
|
recaptchaEnterpriseScript: '',
|
|
gapiScript: ''
|
|
};
|
|
function _setExternalJSProvider(p) {
|
|
externalJSProvider = p;
|
|
}
|
|
function _loadJS(url) {
|
|
return externalJSProvider.loadJS(url);
|
|
}
|
|
function _recaptchaV2ScriptUrl() {
|
|
return externalJSProvider.recaptchaV2Script;
|
|
}
|
|
function _recaptchaEnterpriseScriptUrl() {
|
|
return externalJSProvider.recaptchaEnterpriseScript;
|
|
}
|
|
function _gapiScriptUrl() {
|
|
return externalJSProvider.gapiScript;
|
|
}
|
|
function _generateCallbackName(prefix) {
|
|
return `__${prefix}${Math.floor(Math.random() * 1000000)}`;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const _SOLVE_TIME_MS = 500;
|
|
const _EXPIRATION_TIME_MS = 60000;
|
|
const _WIDGET_ID_START = 1000000000000;
|
|
class MockReCaptcha {
|
|
constructor(auth) {
|
|
this.auth = auth;
|
|
this.counter = _WIDGET_ID_START;
|
|
this._widgets = new Map();
|
|
}
|
|
render(container, parameters) {
|
|
const id = this.counter;
|
|
this._widgets.set(id, new MockWidget(container, this.auth.name, parameters || {}));
|
|
this.counter++;
|
|
return id;
|
|
}
|
|
reset(optWidgetId) {
|
|
var _a;
|
|
const id = optWidgetId || _WIDGET_ID_START;
|
|
void ((_a = this._widgets.get(id)) === null || _a === void 0 ? void 0 : _a.delete());
|
|
this._widgets.delete(id);
|
|
}
|
|
getResponse(optWidgetId) {
|
|
var _a;
|
|
const id = optWidgetId || _WIDGET_ID_START;
|
|
return ((_a = this._widgets.get(id)) === null || _a === void 0 ? void 0 : _a.getResponse()) || '';
|
|
}
|
|
async execute(optWidgetId) {
|
|
var _a;
|
|
const id = optWidgetId || _WIDGET_ID_START;
|
|
void ((_a = this._widgets.get(id)) === null || _a === void 0 ? void 0 : _a.execute());
|
|
return '';
|
|
}
|
|
}
|
|
class MockGreCAPTCHATopLevel {
|
|
constructor() {
|
|
this.enterprise = new MockGreCAPTCHA();
|
|
}
|
|
ready(callback) {
|
|
callback();
|
|
}
|
|
execute(
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_siteKey, _options) {
|
|
return Promise.resolve('token');
|
|
}
|
|
render(
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_container, _parameters) {
|
|
return '';
|
|
}
|
|
}
|
|
class MockGreCAPTCHA {
|
|
ready(callback) {
|
|
callback();
|
|
}
|
|
execute(
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_siteKey, _options) {
|
|
return Promise.resolve('token');
|
|
}
|
|
render(
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_container, _parameters) {
|
|
return '';
|
|
}
|
|
}
|
|
class MockWidget {
|
|
constructor(containerOrId, appName, params) {
|
|
this.params = params;
|
|
this.timerId = null;
|
|
this.deleted = false;
|
|
this.responseToken = null;
|
|
this.clickHandler = () => {
|
|
this.execute();
|
|
};
|
|
const container = typeof containerOrId === 'string'
|
|
? document.getElementById(containerOrId)
|
|
: containerOrId;
|
|
_assert(container, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */, { appName });
|
|
this.container = container;
|
|
this.isVisible = this.params.size !== 'invisible';
|
|
if (this.isVisible) {
|
|
this.execute();
|
|
}
|
|
else {
|
|
this.container.addEventListener('click', this.clickHandler);
|
|
}
|
|
}
|
|
getResponse() {
|
|
this.checkIfDeleted();
|
|
return this.responseToken;
|
|
}
|
|
delete() {
|
|
this.checkIfDeleted();
|
|
this.deleted = true;
|
|
if (this.timerId) {
|
|
clearTimeout(this.timerId);
|
|
this.timerId = null;
|
|
}
|
|
this.container.removeEventListener('click', this.clickHandler);
|
|
}
|
|
execute() {
|
|
this.checkIfDeleted();
|
|
if (this.timerId) {
|
|
return;
|
|
}
|
|
this.timerId = window.setTimeout(() => {
|
|
this.responseToken = generateRandomAlphaNumericString(50);
|
|
const { callback, 'expired-callback': expiredCallback } = this.params;
|
|
if (callback) {
|
|
try {
|
|
callback(this.responseToken);
|
|
}
|
|
catch (e) { }
|
|
}
|
|
this.timerId = window.setTimeout(() => {
|
|
this.timerId = null;
|
|
this.responseToken = null;
|
|
if (expiredCallback) {
|
|
try {
|
|
expiredCallback();
|
|
}
|
|
catch (e) { }
|
|
}
|
|
if (this.isVisible) {
|
|
this.execute();
|
|
}
|
|
}, _EXPIRATION_TIME_MS);
|
|
}, _SOLVE_TIME_MS);
|
|
}
|
|
checkIfDeleted() {
|
|
if (this.deleted) {
|
|
throw new Error('reCAPTCHA mock was already deleted!');
|
|
}
|
|
}
|
|
}
|
|
function generateRandomAlphaNumericString(len) {
|
|
const chars = [];
|
|
const allowedChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
for (let i = 0; i < len; i++) {
|
|
chars.push(allowedChars.charAt(Math.floor(Math.random() * allowedChars.length)));
|
|
}
|
|
return chars.join('');
|
|
}
|
|
|
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
const RECAPTCHA_ENTERPRISE_VERIFIER_TYPE = 'recaptcha-enterprise';
|
|
const FAKE_TOKEN = 'NO_RECAPTCHA';
|
|
class RecaptchaEnterpriseVerifier {
|
|
/**
|
|
*
|
|
* @param authExtern - The corresponding Firebase {@link Auth} instance.
|
|
*
|
|
*/
|
|
constructor(authExtern) {
|
|
/**
|
|
* Identifies the type of application verifier (e.g. "recaptcha-enterprise").
|
|
*/
|
|
this.type = RECAPTCHA_ENTERPRISE_VERIFIER_TYPE;
|
|
this.auth = _castAuth(authExtern);
|
|
}
|
|
/**
|
|
* Executes the verification process.
|
|
*
|
|
* @returns A Promise for a token that can be used to assert the validity of a request.
|
|
*/
|
|
async verify(action = 'verify', forceRefresh = false) {
|
|
async function retrieveSiteKey(auth) {
|
|
if (!forceRefresh) {
|
|
if (auth.tenantId == null && auth._agentRecaptchaConfig != null) {
|
|
return auth._agentRecaptchaConfig.siteKey;
|
|
}
|
|
if (auth.tenantId != null &&
|
|
auth._tenantRecaptchaConfigs[auth.tenantId] !== undefined) {
|
|
return auth._tenantRecaptchaConfigs[auth.tenantId].siteKey;
|
|
}
|
|
}
|
|
return new Promise(async (resolve, reject) => {
|
|
getRecaptchaConfig(auth, {
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */,
|
|
version: "RECAPTCHA_ENTERPRISE" /* RecaptchaVersion.ENTERPRISE */
|
|
})
|
|
.then(response => {
|
|
if (response.recaptchaKey === undefined) {
|
|
reject(new Error('recaptcha Enterprise site key undefined'));
|
|
}
|
|
else {
|
|
const config = new RecaptchaConfig(response);
|
|
if (auth.tenantId == null) {
|
|
auth._agentRecaptchaConfig = config;
|
|
}
|
|
else {
|
|
auth._tenantRecaptchaConfigs[auth.tenantId] = config;
|
|
}
|
|
return resolve(config.siteKey);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
function retrieveRecaptchaToken(siteKey, resolve, reject) {
|
|
const grecaptcha = window.grecaptcha;
|
|
if (isEnterprise(grecaptcha)) {
|
|
grecaptcha.enterprise.ready(() => {
|
|
grecaptcha.enterprise
|
|
.execute(siteKey, { action })
|
|
.then(token => {
|
|
resolve(token);
|
|
})
|
|
.catch(() => {
|
|
resolve(FAKE_TOKEN);
|
|
});
|
|
});
|
|
}
|
|
else {
|
|
reject(Error('No reCAPTCHA enterprise script loaded.'));
|
|
}
|
|
}
|
|
// Returns Promise for a mock token when appVerificationDisabledForTesting is true.
|
|
if (this.auth.settings.appVerificationDisabledForTesting) {
|
|
const mockRecaptcha = new MockGreCAPTCHATopLevel();
|
|
return mockRecaptcha.execute('siteKey', { action: 'verify' });
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
retrieveSiteKey(this.auth)
|
|
.then(siteKey => {
|
|
if (!forceRefresh && isEnterprise(window.grecaptcha)) {
|
|
retrieveRecaptchaToken(siteKey, resolve, reject);
|
|
}
|
|
else {
|
|
if (typeof window === 'undefined') {
|
|
reject(new Error('RecaptchaVerifier is only supported in browser'));
|
|
return;
|
|
}
|
|
let url = _recaptchaEnterpriseScriptUrl();
|
|
if (url.length !== 0) {
|
|
url += siteKey;
|
|
}
|
|
_loadJS(url)
|
|
.then(() => {
|
|
retrieveRecaptchaToken(siteKey, resolve, reject);
|
|
})
|
|
.catch(error => {
|
|
reject(error);
|
|
});
|
|
}
|
|
})
|
|
.catch(error => {
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
async function injectRecaptchaFields(auth, request, action, isCaptchaResp = false, isFakeToken = false) {
|
|
const verifier = new RecaptchaEnterpriseVerifier(auth);
|
|
let captchaResponse;
|
|
if (isFakeToken) {
|
|
captchaResponse = FAKE_TOKEN;
|
|
}
|
|
else {
|
|
try {
|
|
captchaResponse = await verifier.verify(action);
|
|
}
|
|
catch (error) {
|
|
captchaResponse = await verifier.verify(action, true);
|
|
}
|
|
}
|
|
const newRequest = Object.assign({}, request);
|
|
if (action === "mfaSmsEnrollment" /* RecaptchaActionName.MFA_SMS_ENROLLMENT */ ||
|
|
action === "mfaSmsSignIn" /* RecaptchaActionName.MFA_SMS_SIGNIN */) {
|
|
if ('phoneEnrollmentInfo' in newRequest) {
|
|
const phoneNumber = newRequest.phoneEnrollmentInfo.phoneNumber;
|
|
const recaptchaToken = newRequest.phoneEnrollmentInfo.recaptchaToken;
|
|
Object.assign(newRequest, {
|
|
'phoneEnrollmentInfo': {
|
|
phoneNumber,
|
|
recaptchaToken,
|
|
captchaResponse,
|
|
'clientType': "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */,
|
|
'recaptchaVersion': "RECAPTCHA_ENTERPRISE" /* RecaptchaVersion.ENTERPRISE */
|
|
}
|
|
});
|
|
}
|
|
else if ('phoneSignInInfo' in newRequest) {
|
|
const recaptchaToken = newRequest.phoneSignInInfo.recaptchaToken;
|
|
Object.assign(newRequest, {
|
|
'phoneSignInInfo': {
|
|
recaptchaToken,
|
|
captchaResponse,
|
|
'clientType': "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */,
|
|
'recaptchaVersion': "RECAPTCHA_ENTERPRISE" /* RecaptchaVersion.ENTERPRISE */
|
|
}
|
|
});
|
|
}
|
|
return newRequest;
|
|
}
|
|
if (!isCaptchaResp) {
|
|
Object.assign(newRequest, { captchaResponse });
|
|
}
|
|
else {
|
|
Object.assign(newRequest, { 'captchaResp': captchaResponse });
|
|
}
|
|
Object.assign(newRequest, { 'clientType': "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */ });
|
|
Object.assign(newRequest, {
|
|
'recaptchaVersion': "RECAPTCHA_ENTERPRISE" /* RecaptchaVersion.ENTERPRISE */
|
|
});
|
|
return newRequest;
|
|
}
|
|
async function handleRecaptchaFlow(authInstance, request, actionName, actionMethod, recaptchaAuthProvider) {
|
|
var _a, _b;
|
|
if (recaptchaAuthProvider === "EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */) {
|
|
if ((_a = authInstance
|
|
._getRecaptchaConfig()) === null || _a === void 0 ? void 0 : _a.isProviderEnabled("EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */)) {
|
|
const requestWithRecaptcha = await injectRecaptchaFields(authInstance, request, actionName, actionName === "getOobCode" /* RecaptchaActionName.GET_OOB_CODE */);
|
|
return actionMethod(authInstance, requestWithRecaptcha);
|
|
}
|
|
else {
|
|
return actionMethod(authInstance, request).catch(async (error) => {
|
|
if (error.code === `auth/${"missing-recaptcha-token" /* AuthErrorCode.MISSING_RECAPTCHA_TOKEN */}`) {
|
|
console.log(`${actionName} is protected by reCAPTCHA Enterprise for this project. Automatically triggering the reCAPTCHA flow and restarting the flow.`);
|
|
const requestWithRecaptcha = await injectRecaptchaFields(authInstance, request, actionName, actionName === "getOobCode" /* RecaptchaActionName.GET_OOB_CODE */);
|
|
return actionMethod(authInstance, requestWithRecaptcha);
|
|
}
|
|
else {
|
|
return Promise.reject(error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else if (recaptchaAuthProvider === "PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */) {
|
|
if ((_b = authInstance
|
|
._getRecaptchaConfig()) === null || _b === void 0 ? void 0 : _b.isProviderEnabled("PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */)) {
|
|
const requestWithRecaptcha = await injectRecaptchaFields(authInstance, request, actionName);
|
|
return actionMethod(authInstance, requestWithRecaptcha).catch(async (error) => {
|
|
var _a;
|
|
if (((_a = authInstance
|
|
._getRecaptchaConfig()) === null || _a === void 0 ? void 0 : _a.getProviderEnforcementState("PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */)) === "AUDIT" /* EnforcementState.AUDIT */) {
|
|
// AUDIT mode
|
|
if (error.code === `auth/${"missing-recaptcha-token" /* AuthErrorCode.MISSING_RECAPTCHA_TOKEN */}` ||
|
|
error.code === `auth/${"invalid-app-credential" /* AuthErrorCode.INVALID_APP_CREDENTIAL */}`) {
|
|
console.log(`Failed to verify with reCAPTCHA Enterprise. Automatically triggering the reCAPTCHA v2 flow to complete the ${actionName} flow.`);
|
|
// reCAPTCHA Enterprise token is missing or reCAPTCHA Enterprise token
|
|
// check fails.
|
|
// Fallback to reCAPTCHA v2 flow.
|
|
const requestWithRecaptchaFields = await injectRecaptchaFields(authInstance, request, actionName, false, // isCaptchaResp
|
|
true // isFakeToken
|
|
);
|
|
// This will call the PhoneApiCaller to fetch and inject reCAPTCHA v2 token.
|
|
return actionMethod(authInstance, requestWithRecaptchaFields);
|
|
}
|
|
}
|
|
// ENFORCE mode or AUDIT mode with any other error.
|
|
return Promise.reject(error);
|
|
});
|
|
}
|
|
else {
|
|
// Do reCAPTCHA v2 flow.
|
|
const requestWithRecaptchaFields = await injectRecaptchaFields(authInstance, request, actionName, false, // isCaptchaResp
|
|
true // isFakeToken
|
|
);
|
|
// This will call the PhoneApiCaller to fetch and inject v2 token.
|
|
return actionMethod(authInstance, requestWithRecaptchaFields);
|
|
}
|
|
}
|
|
else {
|
|
return Promise.reject(recaptchaAuthProvider + ' provider is not supported.');
|
|
}
|
|
}
|
|
async function _initializeRecaptchaConfig(auth) {
|
|
const authInternal = _castAuth(auth);
|
|
const response = await getRecaptchaConfig(authInternal, {
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */,
|
|
version: "RECAPTCHA_ENTERPRISE" /* RecaptchaVersion.ENTERPRISE */
|
|
});
|
|
const config = new RecaptchaConfig(response);
|
|
if (authInternal.tenantId == null) {
|
|
authInternal._agentRecaptchaConfig = config;
|
|
}
|
|
else {
|
|
authInternal._tenantRecaptchaConfigs[authInternal.tenantId] = config;
|
|
}
|
|
if (config.isAnyProviderEnabled()) {
|
|
const verifier = new RecaptchaEnterpriseVerifier(authInternal);
|
|
void verifier.verify();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Initializes an {@link Auth} instance with fine-grained control over
|
|
* {@link Dependencies}.
|
|
*
|
|
* @remarks
|
|
*
|
|
* This function allows more control over the {@link Auth} instance than
|
|
* {@link getAuth}. `getAuth` uses platform-specific defaults to supply
|
|
* the {@link Dependencies}. In general, `getAuth` is the easiest way to
|
|
* initialize Auth and works for most use cases. Use `initializeAuth` if you
|
|
* need control over which persistence layer is used, or to minimize bundle
|
|
* size if you're not using either `signInWithPopup` or `signInWithRedirect`.
|
|
*
|
|
* For example, if your app only uses anonymous accounts and you only want
|
|
* accounts saved for the current session, initialize `Auth` with:
|
|
*
|
|
* ```js
|
|
* const auth = initializeAuth(app, {
|
|
* persistence: browserSessionPersistence,
|
|
* popupRedirectResolver: undefined,
|
|
* });
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
function initializeAuth(app$1, deps) {
|
|
const provider = app._getProvider(app$1, 'auth');
|
|
if (provider.isInitialized()) {
|
|
const auth = provider.getImmediate();
|
|
const initialOptions = provider.getOptions();
|
|
if (util.deepEqual(initialOptions, deps !== null && deps !== void 0 ? deps : {})) {
|
|
return auth;
|
|
}
|
|
else {
|
|
_fail(auth, "already-initialized" /* AuthErrorCode.ALREADY_INITIALIZED */);
|
|
}
|
|
}
|
|
const auth = provider.initialize({ options: deps });
|
|
return auth;
|
|
}
|
|
function _initializeAuthInstance(auth, deps) {
|
|
const persistence = (deps === null || deps === void 0 ? void 0 : deps.persistence) || [];
|
|
const hierarchy = (Array.isArray(persistence) ? persistence : [persistence]).map(_getInstance);
|
|
if (deps === null || deps === void 0 ? void 0 : deps.errorMap) {
|
|
auth._updateErrorMap(deps.errorMap);
|
|
}
|
|
// This promise is intended to float; auth initialization happens in the
|
|
// background, meanwhile the auth object may be used by the app.
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
auth._initializeWithPersistence(hierarchy, deps === null || deps === void 0 ? void 0 : deps.popupRedirectResolver);
|
|
}
|
|
|
|
/**
|
|
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
|
|
* Firebase Auth services.
|
|
*
|
|
* @remarks
|
|
* This must be called synchronously immediately following the first call to
|
|
* {@link initializeAuth}. Do not use with production credentials as emulator
|
|
* traffic is not encrypted.
|
|
*
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
|
|
* ```
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param url - The URL at which the emulator is running (eg, 'http://localhost:9099').
|
|
* @param options - Optional. `options.disableWarnings` defaults to `false`. Set it to
|
|
* `true` to disable the warning banner attached to the DOM.
|
|
*
|
|
* @public
|
|
*/
|
|
function connectAuthEmulator(auth, url, options) {
|
|
const authInternal = _castAuth(auth);
|
|
_assert(authInternal._canInitEmulator, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
_assert(/^https?:\/\//.test(url), authInternal, "invalid-emulator-scheme" /* AuthErrorCode.INVALID_EMULATOR_SCHEME */);
|
|
const disableWarnings = !!(options === null || options === void 0 ? void 0 : options.disableWarnings);
|
|
const protocol = extractProtocol(url);
|
|
const { host, port } = extractHostAndPort(url);
|
|
const portStr = port === null ? '' : `:${port}`;
|
|
// Always replace path with "/" (even if input url had no path at all, or had a different one).
|
|
authInternal.config.emulator = { url: `${protocol}//${host}${portStr}/` };
|
|
authInternal.settings.appVerificationDisabledForTesting = true;
|
|
authInternal.emulatorConfig = Object.freeze({
|
|
host,
|
|
port,
|
|
protocol: protocol.replace(':', ''),
|
|
options: Object.freeze({ disableWarnings })
|
|
});
|
|
if (!disableWarnings) {
|
|
emitEmulatorWarning();
|
|
}
|
|
}
|
|
function extractProtocol(url) {
|
|
const protocolEnd = url.indexOf(':');
|
|
return protocolEnd < 0 ? '' : url.substr(0, protocolEnd + 1);
|
|
}
|
|
function extractHostAndPort(url) {
|
|
const protocol = extractProtocol(url);
|
|
const authority = /(\/\/)?([^?#/]+)/.exec(url.substr(protocol.length)); // Between // and /, ? or #.
|
|
if (!authority) {
|
|
return { host: '', port: null };
|
|
}
|
|
const hostAndPort = authority[2].split('@').pop() || ''; // Strip out "username:password@".
|
|
const bracketedIPv6 = /^(\[[^\]]+\])(:|$)/.exec(hostAndPort);
|
|
if (bracketedIPv6) {
|
|
const host = bracketedIPv6[1];
|
|
return { host, port: parsePort(hostAndPort.substr(host.length + 1)) };
|
|
}
|
|
else {
|
|
const [host, port] = hostAndPort.split(':');
|
|
return { host, port: parsePort(port) };
|
|
}
|
|
}
|
|
function parsePort(portStr) {
|
|
if (!portStr) {
|
|
return null;
|
|
}
|
|
const port = Number(portStr);
|
|
if (isNaN(port)) {
|
|
return null;
|
|
}
|
|
return port;
|
|
}
|
|
function emitEmulatorWarning() {
|
|
function attachBanner() {
|
|
const el = document.createElement('p');
|
|
const sty = el.style;
|
|
el.innerText =
|
|
'Running in emulator mode. Do not use with production credentials.';
|
|
sty.position = 'fixed';
|
|
sty.width = '100%';
|
|
sty.backgroundColor = '#ffffff';
|
|
sty.border = '.1em solid #000000';
|
|
sty.color = '#b50000';
|
|
sty.bottom = '0px';
|
|
sty.left = '0px';
|
|
sty.margin = '0px';
|
|
sty.zIndex = '10000';
|
|
sty.textAlign = 'center';
|
|
el.classList.add('firebase-emulator-warning');
|
|
document.body.appendChild(el);
|
|
}
|
|
if (typeof console !== 'undefined' && typeof console.info === 'function') {
|
|
console.info('WARNING: You are using the Auth Emulator,' +
|
|
' which is intended for local testing only. Do not use with' +
|
|
' production credentials.');
|
|
}
|
|
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
if (document.readyState === 'loading') {
|
|
window.addEventListener('DOMContentLoaded', attachBanner);
|
|
}
|
|
else {
|
|
attachBanner();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Interface that represents the credentials returned by an {@link AuthProvider}.
|
|
*
|
|
* @remarks
|
|
* Implementations specify the details about each auth provider's credential requirements.
|
|
*
|
|
* @public
|
|
*/
|
|
class AuthCredential {
|
|
/** @internal */
|
|
constructor(
|
|
/**
|
|
* The authentication provider ID for the credential.
|
|
*
|
|
* @remarks
|
|
* For example, 'facebook.com', or 'google.com'.
|
|
*/
|
|
providerId,
|
|
/**
|
|
* The authentication sign in method for the credential.
|
|
*
|
|
* @remarks
|
|
* For example, {@link SignInMethod}.EMAIL_PASSWORD, or
|
|
* {@link SignInMethod}.EMAIL_LINK. This corresponds to the sign-in method
|
|
* identifier as returned in {@link fetchSignInMethodsForEmail}.
|
|
*/
|
|
signInMethod) {
|
|
this.providerId = providerId;
|
|
this.signInMethod = signInMethod;
|
|
}
|
|
/**
|
|
* Returns a JSON-serializable representation of this object.
|
|
*
|
|
* @returns a JSON-serializable representation of this object.
|
|
*/
|
|
toJSON() {
|
|
return debugFail('not implemented');
|
|
}
|
|
/** @internal */
|
|
_getIdTokenResponse(_auth) {
|
|
return debugFail('not implemented');
|
|
}
|
|
/** @internal */
|
|
_linkToIdToken(_auth, _idToken) {
|
|
return debugFail('not implemented');
|
|
}
|
|
/** @internal */
|
|
_getReauthenticationResolver(_auth) {
|
|
return debugFail('not implemented');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function resetPassword(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:resetPassword" /* Endpoint.RESET_PASSWORD */, _addTidIfNecessary(auth, request));
|
|
}
|
|
async function updateEmailPassword(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:update" /* Endpoint.SET_ACCOUNT_INFO */, request);
|
|
}
|
|
// Used for linking an email/password account to an existing idToken. Uses the same request/response
|
|
// format as updateEmailPassword.
|
|
async function linkEmailPassword(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signUp" /* Endpoint.SIGN_UP */, request);
|
|
}
|
|
async function applyActionCode$1(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:update" /* Endpoint.SET_ACCOUNT_INFO */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function signInWithPassword(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithPassword" /* Endpoint.SIGN_IN_WITH_PASSWORD */, _addTidIfNecessary(auth, request));
|
|
}
|
|
async function sendOobCode(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:sendOobCode" /* Endpoint.SEND_OOB_CODE */, _addTidIfNecessary(auth, request));
|
|
}
|
|
async function sendEmailVerification$1(auth, request) {
|
|
return sendOobCode(auth, request);
|
|
}
|
|
async function sendPasswordResetEmail$1(auth, request) {
|
|
return sendOobCode(auth, request);
|
|
}
|
|
async function sendSignInLinkToEmail$1(auth, request) {
|
|
return sendOobCode(auth, request);
|
|
}
|
|
async function verifyAndChangeEmail(auth, request) {
|
|
return sendOobCode(auth, request);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function signInWithEmailLink$1(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithEmailLink" /* Endpoint.SIGN_IN_WITH_EMAIL_LINK */, _addTidIfNecessary(auth, request));
|
|
}
|
|
async function signInWithEmailLinkForLinking(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithEmailLink" /* Endpoint.SIGN_IN_WITH_EMAIL_LINK */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Interface that represents the credentials returned by {@link EmailAuthProvider} for
|
|
* {@link ProviderId}.PASSWORD
|
|
*
|
|
* @remarks
|
|
* Covers both {@link SignInMethod}.EMAIL_PASSWORD and
|
|
* {@link SignInMethod}.EMAIL_LINK.
|
|
*
|
|
* @public
|
|
*/
|
|
class EmailAuthCredential extends AuthCredential {
|
|
/** @internal */
|
|
constructor(
|
|
/** @internal */
|
|
_email,
|
|
/** @internal */
|
|
_password, signInMethod,
|
|
/** @internal */
|
|
_tenantId = null) {
|
|
super("password" /* ProviderId.PASSWORD */, signInMethod);
|
|
this._email = _email;
|
|
this._password = _password;
|
|
this._tenantId = _tenantId;
|
|
}
|
|
/** @internal */
|
|
static _fromEmailAndPassword(email, password) {
|
|
return new EmailAuthCredential(email, password, "password" /* SignInMethod.EMAIL_PASSWORD */);
|
|
}
|
|
/** @internal */
|
|
static _fromEmailAndCode(email, oobCode, tenantId = null) {
|
|
return new EmailAuthCredential(email, oobCode, "emailLink" /* SignInMethod.EMAIL_LINK */, tenantId);
|
|
}
|
|
/** {@inheritdoc AuthCredential.toJSON} */
|
|
toJSON() {
|
|
return {
|
|
email: this._email,
|
|
password: this._password,
|
|
signInMethod: this.signInMethod,
|
|
tenantId: this._tenantId
|
|
};
|
|
}
|
|
/**
|
|
* Static method to deserialize a JSON representation of an object into an {@link AuthCredential}.
|
|
*
|
|
* @param json - Either `object` or the stringified representation of the object. When string is
|
|
* provided, `JSON.parse` would be called first.
|
|
*
|
|
* @returns If the JSON input does not represent an {@link AuthCredential}, null is returned.
|
|
*/
|
|
static fromJSON(json) {
|
|
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
|
if ((obj === null || obj === void 0 ? void 0 : obj.email) && (obj === null || obj === void 0 ? void 0 : obj.password)) {
|
|
if (obj.signInMethod === "password" /* SignInMethod.EMAIL_PASSWORD */) {
|
|
return this._fromEmailAndPassword(obj.email, obj.password);
|
|
}
|
|
else if (obj.signInMethod === "emailLink" /* SignInMethod.EMAIL_LINK */) {
|
|
return this._fromEmailAndCode(obj.email, obj.password, obj.tenantId);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/** @internal */
|
|
async _getIdTokenResponse(auth) {
|
|
switch (this.signInMethod) {
|
|
case "password" /* SignInMethod.EMAIL_PASSWORD */:
|
|
const request = {
|
|
returnSecureToken: true,
|
|
email: this._email,
|
|
password: this._password,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
};
|
|
return handleRecaptchaFlow(auth, request, "signInWithPassword" /* RecaptchaActionName.SIGN_IN_WITH_PASSWORD */, signInWithPassword, "EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */);
|
|
case "emailLink" /* SignInMethod.EMAIL_LINK */:
|
|
return signInWithEmailLink$1(auth, {
|
|
email: this._email,
|
|
oobCode: this._password
|
|
});
|
|
default:
|
|
_fail(auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
}
|
|
}
|
|
/** @internal */
|
|
async _linkToIdToken(auth, idToken) {
|
|
switch (this.signInMethod) {
|
|
case "password" /* SignInMethod.EMAIL_PASSWORD */:
|
|
const request = {
|
|
idToken,
|
|
returnSecureToken: true,
|
|
email: this._email,
|
|
password: this._password,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
};
|
|
return handleRecaptchaFlow(auth, request, "signUpPassword" /* RecaptchaActionName.SIGN_UP_PASSWORD */, linkEmailPassword, "EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */);
|
|
case "emailLink" /* SignInMethod.EMAIL_LINK */:
|
|
return signInWithEmailLinkForLinking(auth, {
|
|
idToken,
|
|
email: this._email,
|
|
oobCode: this._password
|
|
});
|
|
default:
|
|
_fail(auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
}
|
|
}
|
|
/** @internal */
|
|
_getReauthenticationResolver(auth) {
|
|
return this._getIdTokenResponse(auth);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function signInWithIdp(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithIdp" /* Endpoint.SIGN_IN_WITH_IDP */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const IDP_REQUEST_URI$1 = 'http://localhost';
|
|
/**
|
|
* Represents the OAuth credentials returned by an {@link OAuthProvider}.
|
|
*
|
|
* @remarks
|
|
* Implementations specify the details about each auth provider's credential requirements.
|
|
*
|
|
* @public
|
|
*/
|
|
class OAuthCredential extends AuthCredential {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.pendingToken = null;
|
|
}
|
|
/** @internal */
|
|
static _fromParams(params) {
|
|
const cred = new OAuthCredential(params.providerId, params.signInMethod);
|
|
if (params.idToken || params.accessToken) {
|
|
// OAuth 2 and either ID token or access token.
|
|
if (params.idToken) {
|
|
cred.idToken = params.idToken;
|
|
}
|
|
if (params.accessToken) {
|
|
cred.accessToken = params.accessToken;
|
|
}
|
|
// Add nonce if available and no pendingToken is present.
|
|
if (params.nonce && !params.pendingToken) {
|
|
cred.nonce = params.nonce;
|
|
}
|
|
if (params.pendingToken) {
|
|
cred.pendingToken = params.pendingToken;
|
|
}
|
|
}
|
|
else if (params.oauthToken && params.oauthTokenSecret) {
|
|
// OAuth 1 and OAuth token with token secret
|
|
cred.accessToken = params.oauthToken;
|
|
cred.secret = params.oauthTokenSecret;
|
|
}
|
|
else {
|
|
_fail("argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
}
|
|
return cred;
|
|
}
|
|
/** {@inheritdoc AuthCredential.toJSON} */
|
|
toJSON() {
|
|
return {
|
|
idToken: this.idToken,
|
|
accessToken: this.accessToken,
|
|
secret: this.secret,
|
|
nonce: this.nonce,
|
|
pendingToken: this.pendingToken,
|
|
providerId: this.providerId,
|
|
signInMethod: this.signInMethod
|
|
};
|
|
}
|
|
/**
|
|
* Static method to deserialize a JSON representation of an object into an
|
|
* {@link AuthCredential}.
|
|
*
|
|
* @param json - Input can be either Object or the stringified representation of the object.
|
|
* When string is provided, JSON.parse would be called first.
|
|
*
|
|
* @returns If the JSON input does not represent an {@link AuthCredential}, null is returned.
|
|
*/
|
|
static fromJSON(json) {
|
|
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
|
const { providerId, signInMethod } = obj, rest = tslib.__rest(obj, ["providerId", "signInMethod"]);
|
|
if (!providerId || !signInMethod) {
|
|
return null;
|
|
}
|
|
const cred = new OAuthCredential(providerId, signInMethod);
|
|
cred.idToken = rest.idToken || undefined;
|
|
cred.accessToken = rest.accessToken || undefined;
|
|
cred.secret = rest.secret;
|
|
cred.nonce = rest.nonce;
|
|
cred.pendingToken = rest.pendingToken || null;
|
|
return cred;
|
|
}
|
|
/** @internal */
|
|
_getIdTokenResponse(auth) {
|
|
const request = this.buildRequest();
|
|
return signInWithIdp(auth, request);
|
|
}
|
|
/** @internal */
|
|
_linkToIdToken(auth, idToken) {
|
|
const request = this.buildRequest();
|
|
request.idToken = idToken;
|
|
return signInWithIdp(auth, request);
|
|
}
|
|
/** @internal */
|
|
_getReauthenticationResolver(auth) {
|
|
const request = this.buildRequest();
|
|
request.autoCreate = false;
|
|
return signInWithIdp(auth, request);
|
|
}
|
|
buildRequest() {
|
|
const request = {
|
|
requestUri: IDP_REQUEST_URI$1,
|
|
returnSecureToken: true
|
|
};
|
|
if (this.pendingToken) {
|
|
request.pendingToken = this.pendingToken;
|
|
}
|
|
else {
|
|
const postBody = {};
|
|
if (this.idToken) {
|
|
postBody['id_token'] = this.idToken;
|
|
}
|
|
if (this.accessToken) {
|
|
postBody['access_token'] = this.accessToken;
|
|
}
|
|
if (this.secret) {
|
|
postBody['oauth_token_secret'] = this.secret;
|
|
}
|
|
postBody['providerId'] = this.providerId;
|
|
if (this.nonce && !this.pendingToken) {
|
|
postBody['nonce'] = this.nonce;
|
|
}
|
|
request.postBody = util.querystring(postBody);
|
|
}
|
|
return request;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function sendPhoneVerificationCode(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:sendVerificationCode" /* Endpoint.SEND_VERIFICATION_CODE */, _addTidIfNecessary(auth, request));
|
|
}
|
|
async function signInWithPhoneNumber$1(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithPhoneNumber" /* Endpoint.SIGN_IN_WITH_PHONE_NUMBER */, _addTidIfNecessary(auth, request));
|
|
}
|
|
async function linkWithPhoneNumber$1(auth, request) {
|
|
const response = await _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithPhoneNumber" /* Endpoint.SIGN_IN_WITH_PHONE_NUMBER */, _addTidIfNecessary(auth, request));
|
|
if (response.temporaryProof) {
|
|
throw _makeTaggedError(auth, "account-exists-with-different-credential" /* AuthErrorCode.NEED_CONFIRMATION */, response);
|
|
}
|
|
return response;
|
|
}
|
|
const VERIFY_PHONE_NUMBER_FOR_EXISTING_ERROR_MAP_ = {
|
|
["USER_NOT_FOUND" /* ServerError.USER_NOT_FOUND */]: "user-not-found" /* AuthErrorCode.USER_DELETED */
|
|
};
|
|
async function verifyPhoneNumberForExisting(auth, request) {
|
|
const apiRequest = Object.assign(Object.assign({}, request), { operation: 'REAUTH' });
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithPhoneNumber" /* Endpoint.SIGN_IN_WITH_PHONE_NUMBER */, _addTidIfNecessary(auth, apiRequest), VERIFY_PHONE_NUMBER_FOR_EXISTING_ERROR_MAP_);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Represents the credentials returned by {@link PhoneAuthProvider}.
|
|
*
|
|
* @public
|
|
*/
|
|
class PhoneAuthCredential extends AuthCredential {
|
|
constructor(params) {
|
|
super("phone" /* ProviderId.PHONE */, "phone" /* SignInMethod.PHONE */);
|
|
this.params = params;
|
|
}
|
|
/** @internal */
|
|
static _fromVerification(verificationId, verificationCode) {
|
|
return new PhoneAuthCredential({ verificationId, verificationCode });
|
|
}
|
|
/** @internal */
|
|
static _fromTokenResponse(phoneNumber, temporaryProof) {
|
|
return new PhoneAuthCredential({ phoneNumber, temporaryProof });
|
|
}
|
|
/** @internal */
|
|
_getIdTokenResponse(auth) {
|
|
return signInWithPhoneNumber$1(auth, this._makeVerificationRequest());
|
|
}
|
|
/** @internal */
|
|
_linkToIdToken(auth, idToken) {
|
|
return linkWithPhoneNumber$1(auth, Object.assign({ idToken }, this._makeVerificationRequest()));
|
|
}
|
|
/** @internal */
|
|
_getReauthenticationResolver(auth) {
|
|
return verifyPhoneNumberForExisting(auth, this._makeVerificationRequest());
|
|
}
|
|
/** @internal */
|
|
_makeVerificationRequest() {
|
|
const { temporaryProof, phoneNumber, verificationId, verificationCode } = this.params;
|
|
if (temporaryProof && phoneNumber) {
|
|
return { temporaryProof, phoneNumber };
|
|
}
|
|
return {
|
|
sessionInfo: verificationId,
|
|
code: verificationCode
|
|
};
|
|
}
|
|
/** {@inheritdoc AuthCredential.toJSON} */
|
|
toJSON() {
|
|
const obj = {
|
|
providerId: this.providerId
|
|
};
|
|
if (this.params.phoneNumber) {
|
|
obj.phoneNumber = this.params.phoneNumber;
|
|
}
|
|
if (this.params.temporaryProof) {
|
|
obj.temporaryProof = this.params.temporaryProof;
|
|
}
|
|
if (this.params.verificationCode) {
|
|
obj.verificationCode = this.params.verificationCode;
|
|
}
|
|
if (this.params.verificationId) {
|
|
obj.verificationId = this.params.verificationId;
|
|
}
|
|
return obj;
|
|
}
|
|
/** Generates a phone credential based on a plain object or a JSON string. */
|
|
static fromJSON(json) {
|
|
if (typeof json === 'string') {
|
|
json = JSON.parse(json);
|
|
}
|
|
const { verificationId, verificationCode, phoneNumber, temporaryProof } = json;
|
|
if (!verificationCode &&
|
|
!verificationId &&
|
|
!phoneNumber &&
|
|
!temporaryProof) {
|
|
return null;
|
|
}
|
|
return new PhoneAuthCredential({
|
|
verificationId,
|
|
verificationCode,
|
|
phoneNumber,
|
|
temporaryProof
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Maps the mode string in action code URL to Action Code Info operation.
|
|
*
|
|
* @param mode
|
|
*/
|
|
function parseMode(mode) {
|
|
switch (mode) {
|
|
case 'recoverEmail':
|
|
return "RECOVER_EMAIL" /* ActionCodeOperation.RECOVER_EMAIL */;
|
|
case 'resetPassword':
|
|
return "PASSWORD_RESET" /* ActionCodeOperation.PASSWORD_RESET */;
|
|
case 'signIn':
|
|
return "EMAIL_SIGNIN" /* ActionCodeOperation.EMAIL_SIGNIN */;
|
|
case 'verifyEmail':
|
|
return "VERIFY_EMAIL" /* ActionCodeOperation.VERIFY_EMAIL */;
|
|
case 'verifyAndChangeEmail':
|
|
return "VERIFY_AND_CHANGE_EMAIL" /* ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL */;
|
|
case 'revertSecondFactorAddition':
|
|
return "REVERT_SECOND_FACTOR_ADDITION" /* ActionCodeOperation.REVERT_SECOND_FACTOR_ADDITION */;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Helper to parse FDL links
|
|
*
|
|
* @param url
|
|
*/
|
|
function parseDeepLink(url) {
|
|
const link = util.querystringDecode(util.extractQuerystring(url))['link'];
|
|
// Double link case (automatic redirect).
|
|
const doubleDeepLink = link
|
|
? util.querystringDecode(util.extractQuerystring(link))['deep_link_id']
|
|
: null;
|
|
// iOS custom scheme links.
|
|
const iOSDeepLink = util.querystringDecode(util.extractQuerystring(url))['deep_link_id'];
|
|
const iOSDoubleDeepLink = iOSDeepLink
|
|
? util.querystringDecode(util.extractQuerystring(iOSDeepLink))['link']
|
|
: null;
|
|
return iOSDoubleDeepLink || iOSDeepLink || doubleDeepLink || link || url;
|
|
}
|
|
/**
|
|
* A utility class to parse email action URLs such as password reset, email verification,
|
|
* email link sign in, etc.
|
|
*
|
|
* @public
|
|
*/
|
|
class ActionCodeURL {
|
|
/**
|
|
* @param actionLink - The link from which to extract the URL.
|
|
* @returns The {@link ActionCodeURL} object, or null if the link is invalid.
|
|
*
|
|
* @internal
|
|
*/
|
|
constructor(actionLink) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
const searchParams = util.querystringDecode(util.extractQuerystring(actionLink));
|
|
const apiKey = (_a = searchParams["apiKey" /* QueryField.API_KEY */]) !== null && _a !== void 0 ? _a : null;
|
|
const code = (_b = searchParams["oobCode" /* QueryField.CODE */]) !== null && _b !== void 0 ? _b : null;
|
|
const operation = parseMode((_c = searchParams["mode" /* QueryField.MODE */]) !== null && _c !== void 0 ? _c : null);
|
|
// Validate API key, code and mode.
|
|
_assert(apiKey && code && operation, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
this.apiKey = apiKey;
|
|
this.operation = operation;
|
|
this.code = code;
|
|
this.continueUrl = (_d = searchParams["continueUrl" /* QueryField.CONTINUE_URL */]) !== null && _d !== void 0 ? _d : null;
|
|
this.languageCode = (_e = searchParams["languageCode" /* QueryField.LANGUAGE_CODE */]) !== null && _e !== void 0 ? _e : null;
|
|
this.tenantId = (_f = searchParams["tenantId" /* QueryField.TENANT_ID */]) !== null && _f !== void 0 ? _f : null;
|
|
}
|
|
/**
|
|
* Parses the email action link string and returns an {@link ActionCodeURL} if the link is valid,
|
|
* otherwise returns null.
|
|
*
|
|
* @param link - The email action link string.
|
|
* @returns The {@link ActionCodeURL} object, or null if the link is invalid.
|
|
*
|
|
* @public
|
|
*/
|
|
static parseLink(link) {
|
|
const actionLink = parseDeepLink(link);
|
|
try {
|
|
return new ActionCodeURL(actionLink);
|
|
}
|
|
catch (_a) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Parses the email action link string and returns an {@link ActionCodeURL} if
|
|
* the link is valid, otherwise returns null.
|
|
*
|
|
* @public
|
|
*/
|
|
function parseActionCodeURL(link) {
|
|
return ActionCodeURL.parseLink(link);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Provider for generating {@link EmailAuthCredential}.
|
|
*
|
|
* @public
|
|
*/
|
|
class EmailAuthProvider {
|
|
constructor() {
|
|
/**
|
|
* Always set to {@link ProviderId}.PASSWORD, even for email link.
|
|
*/
|
|
this.providerId = EmailAuthProvider.PROVIDER_ID;
|
|
}
|
|
/**
|
|
* Initialize an {@link AuthCredential} using an email and password.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const authCredential = EmailAuthProvider.credential(email, password);
|
|
* const userCredential = await signInWithCredential(auth, authCredential);
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const userCredential = await signInWithEmailAndPassword(auth, email, password);
|
|
* ```
|
|
*
|
|
* @param email - Email address.
|
|
* @param password - User account password.
|
|
* @returns The auth provider credential.
|
|
*/
|
|
static credential(email, password) {
|
|
return EmailAuthCredential._fromEmailAndPassword(email, password);
|
|
}
|
|
/**
|
|
* Initialize an {@link AuthCredential} using an email and an email link after a sign in with
|
|
* email link operation.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const authCredential = EmailAuthProvider.credentialWithLink(auth, email, emailLink);
|
|
* const userCredential = await signInWithCredential(auth, authCredential);
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* await sendSignInLinkToEmail(auth, email);
|
|
* // Obtain emailLink from user.
|
|
* const userCredential = await signInWithEmailLink(auth, email, emailLink);
|
|
* ```
|
|
*
|
|
* @param auth - The {@link Auth} instance used to verify the link.
|
|
* @param email - Email address.
|
|
* @param emailLink - Sign-in email link.
|
|
* @returns - The auth provider credential.
|
|
*/
|
|
static credentialWithLink(email, emailLink) {
|
|
const actionCodeUrl = ActionCodeURL.parseLink(emailLink);
|
|
_assert(actionCodeUrl, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
return EmailAuthCredential._fromEmailAndCode(email, actionCodeUrl.code, actionCodeUrl.tenantId);
|
|
}
|
|
}
|
|
/**
|
|
* Always set to {@link ProviderId}.PASSWORD, even for email link.
|
|
*/
|
|
EmailAuthProvider.PROVIDER_ID = "password" /* ProviderId.PASSWORD */;
|
|
/**
|
|
* Always set to {@link SignInMethod}.EMAIL_PASSWORD.
|
|
*/
|
|
EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD = "password" /* SignInMethod.EMAIL_PASSWORD */;
|
|
/**
|
|
* Always set to {@link SignInMethod}.EMAIL_LINK.
|
|
*/
|
|
EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD = "emailLink" /* SignInMethod.EMAIL_LINK */;
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* The base class for all Federated providers (OAuth (including OIDC), SAML).
|
|
*
|
|
* This class is not meant to be instantiated directly.
|
|
*
|
|
* @public
|
|
*/
|
|
class FederatedAuthProvider {
|
|
/**
|
|
* Constructor for generic OAuth providers.
|
|
*
|
|
* @param providerId - Provider for which credentials should be generated.
|
|
*/
|
|
constructor(providerId) {
|
|
this.providerId = providerId;
|
|
/** @internal */
|
|
this.defaultLanguageCode = null;
|
|
/** @internal */
|
|
this.customParameters = {};
|
|
}
|
|
/**
|
|
* Set the language gode.
|
|
*
|
|
* @param languageCode - language code
|
|
*/
|
|
setDefaultLanguage(languageCode) {
|
|
this.defaultLanguageCode = languageCode;
|
|
}
|
|
/**
|
|
* Sets the OAuth custom parameters to pass in an OAuth request for popup and redirect sign-in
|
|
* operations.
|
|
*
|
|
* @remarks
|
|
* For a detailed list, check the reserved required OAuth 2.0 parameters such as `client_id`,
|
|
* `redirect_uri`, `scope`, `response_type`, and `state` are not allowed and will be ignored.
|
|
*
|
|
* @param customOAuthParameters - The custom OAuth parameters to pass in the OAuth request.
|
|
*/
|
|
setCustomParameters(customOAuthParameters) {
|
|
this.customParameters = customOAuthParameters;
|
|
return this;
|
|
}
|
|
/**
|
|
* Retrieve the current list of {@link CustomParameters}.
|
|
*/
|
|
getCustomParameters() {
|
|
return this.customParameters;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Common code to all OAuth providers. This is separate from the
|
|
* {@link OAuthProvider} so that child providers (like
|
|
* {@link GoogleAuthProvider}) don't inherit the `credential` instance method.
|
|
* Instead, they rely on a static `credential` method.
|
|
*/
|
|
class BaseOAuthProvider extends FederatedAuthProvider {
|
|
constructor() {
|
|
super(...arguments);
|
|
/** @internal */
|
|
this.scopes = [];
|
|
}
|
|
/**
|
|
* Add an OAuth scope to the credential.
|
|
*
|
|
* @param scope - Provider OAuth scope to add.
|
|
*/
|
|
addScope(scope) {
|
|
// If not already added, add scope to list.
|
|
if (!this.scopes.includes(scope)) {
|
|
this.scopes.push(scope);
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* Retrieve the current list of OAuth scopes.
|
|
*/
|
|
getScopes() {
|
|
return [...this.scopes];
|
|
}
|
|
}
|
|
/**
|
|
* Provider for generating generic {@link OAuthCredential}.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a redirect.
|
|
* const provider = new OAuthProvider('google.com');
|
|
* // Start a sign in process for an unauthenticated user.
|
|
* provider.addScope('profile');
|
|
* provider.addScope('email');
|
|
* await signInWithRedirect(auth, provider);
|
|
* // This will trigger a full page redirect away from your app
|
|
*
|
|
* // After returning from the redirect when your app initializes you can obtain the result
|
|
* const result = await getRedirectResult(auth);
|
|
* if (result) {
|
|
* // This is the signed-in user
|
|
* const user = result.user;
|
|
* // This gives you a OAuth Access Token for the provider.
|
|
* const credential = provider.credentialFromResult(auth, result);
|
|
* const token = credential.accessToken;
|
|
* }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a popup.
|
|
* const provider = new OAuthProvider('google.com');
|
|
* provider.addScope('profile');
|
|
* provider.addScope('email');
|
|
* const result = await signInWithPopup(auth, provider);
|
|
*
|
|
* // The signed-in user info.
|
|
* const user = result.user;
|
|
* // This gives you a OAuth Access Token for the provider.
|
|
* const credential = provider.credentialFromResult(auth, result);
|
|
* const token = credential.accessToken;
|
|
* ```
|
|
* @public
|
|
*/
|
|
class OAuthProvider extends BaseOAuthProvider {
|
|
/**
|
|
* Creates an {@link OAuthCredential} from a JSON string or a plain object.
|
|
* @param json - A plain object or a JSON string
|
|
*/
|
|
static credentialFromJSON(json) {
|
|
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
|
_assert('providerId' in obj && 'signInMethod' in obj, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
return OAuthCredential._fromParams(obj);
|
|
}
|
|
/**
|
|
* Creates a {@link OAuthCredential} from a generic OAuth provider's access token or ID token.
|
|
*
|
|
* @remarks
|
|
* The raw nonce is required when an ID token with a nonce field is provided. The SHA-256 hash of
|
|
* the raw nonce must match the nonce field in the ID token.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // `googleUser` from the onsuccess Google Sign In callback.
|
|
* // Initialize a generate OAuth provider with a `google.com` providerId.
|
|
* const provider = new OAuthProvider('google.com');
|
|
* const credential = provider.credential({
|
|
* idToken: googleUser.getAuthResponse().id_token,
|
|
* });
|
|
* const result = await signInWithCredential(credential);
|
|
* ```
|
|
*
|
|
* @param params - Either the options object containing the ID token, access token and raw nonce
|
|
* or the ID token string.
|
|
*/
|
|
credential(params) {
|
|
return this._credential(Object.assign(Object.assign({}, params), { nonce: params.rawNonce }));
|
|
}
|
|
/** An internal credential method that accepts more permissive options */
|
|
_credential(params) {
|
|
_assert(params.idToken || params.accessToken, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
// For OAuthCredential, sign in method is same as providerId.
|
|
return OAuthCredential._fromParams(Object.assign(Object.assign({}, params), { providerId: this.providerId, signInMethod: this.providerId }));
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
return OAuthProvider.oauthCredentialFromTaggedObject(userCredential);
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was
|
|
* thrown during a sign-in, link, or reauthenticate operation.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return OAuthProvider.oauthCredentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
static oauthCredentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse) {
|
|
return null;
|
|
}
|
|
const { oauthIdToken, oauthAccessToken, oauthTokenSecret, pendingToken, nonce, providerId } = tokenResponse;
|
|
if (!oauthAccessToken &&
|
|
!oauthTokenSecret &&
|
|
!oauthIdToken &&
|
|
!pendingToken) {
|
|
return null;
|
|
}
|
|
if (!providerId) {
|
|
return null;
|
|
}
|
|
try {
|
|
return new OAuthProvider(providerId)._credential({
|
|
idToken: oauthIdToken,
|
|
accessToken: oauthAccessToken,
|
|
nonce,
|
|
pendingToken
|
|
});
|
|
}
|
|
catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Provider for generating an {@link OAuthCredential} for {@link ProviderId}.FACEBOOK.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a redirect.
|
|
* const provider = new FacebookAuthProvider();
|
|
* // Start a sign in process for an unauthenticated user.
|
|
* provider.addScope('user_birthday');
|
|
* await signInWithRedirect(auth, provider);
|
|
* // This will trigger a full page redirect away from your app
|
|
*
|
|
* // After returning from the redirect when your app initializes you can obtain the result
|
|
* const result = await getRedirectResult(auth);
|
|
* if (result) {
|
|
* // This is the signed-in user
|
|
* const user = result.user;
|
|
* // This gives you a Facebook Access Token.
|
|
* const credential = FacebookAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a popup.
|
|
* const provider = new FacebookAuthProvider();
|
|
* provider.addScope('user_birthday');
|
|
* const result = await signInWithPopup(auth, provider);
|
|
*
|
|
* // The signed-in user info.
|
|
* const user = result.user;
|
|
* // This gives you a Facebook Access Token.
|
|
* const credential = FacebookAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
class FacebookAuthProvider extends BaseOAuthProvider {
|
|
constructor() {
|
|
super("facebook.com" /* ProviderId.FACEBOOK */);
|
|
}
|
|
/**
|
|
* Creates a credential for Facebook.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // `event` from the Facebook auth.authResponseChange callback.
|
|
* const credential = FacebookAuthProvider.credential(event.authResponse.accessToken);
|
|
* const result = await signInWithCredential(credential);
|
|
* ```
|
|
*
|
|
* @param accessToken - Facebook access token.
|
|
*/
|
|
static credential(accessToken) {
|
|
return OAuthCredential._fromParams({
|
|
providerId: FacebookAuthProvider.PROVIDER_ID,
|
|
signInMethod: FacebookAuthProvider.FACEBOOK_SIGN_IN_METHOD,
|
|
accessToken
|
|
});
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
return FacebookAuthProvider.credentialFromTaggedObject(userCredential);
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was
|
|
* thrown during a sign-in, link, or reauthenticate operation.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return FacebookAuthProvider.credentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
static credentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse || !('oauthAccessToken' in tokenResponse)) {
|
|
return null;
|
|
}
|
|
if (!tokenResponse.oauthAccessToken) {
|
|
return null;
|
|
}
|
|
try {
|
|
return FacebookAuthProvider.credential(tokenResponse.oauthAccessToken);
|
|
}
|
|
catch (_a) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
/** Always set to {@link SignInMethod}.FACEBOOK. */
|
|
FacebookAuthProvider.FACEBOOK_SIGN_IN_METHOD = "facebook.com" /* SignInMethod.FACEBOOK */;
|
|
/** Always set to {@link ProviderId}.FACEBOOK. */
|
|
FacebookAuthProvider.PROVIDER_ID = "facebook.com" /* ProviderId.FACEBOOK */;
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Provider for generating an {@link OAuthCredential} for {@link ProviderId}.GOOGLE.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a redirect.
|
|
* const provider = new GoogleAuthProvider();
|
|
* // Start a sign in process for an unauthenticated user.
|
|
* provider.addScope('profile');
|
|
* provider.addScope('email');
|
|
* await signInWithRedirect(auth, provider);
|
|
* // This will trigger a full page redirect away from your app
|
|
*
|
|
* // After returning from the redirect when your app initializes you can obtain the result
|
|
* const result = await getRedirectResult(auth);
|
|
* if (result) {
|
|
* // This is the signed-in user
|
|
* const user = result.user;
|
|
* // This gives you a Google Access Token.
|
|
* const credential = GoogleAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a popup.
|
|
* const provider = new GoogleAuthProvider();
|
|
* provider.addScope('profile');
|
|
* provider.addScope('email');
|
|
* const result = await signInWithPopup(auth, provider);
|
|
*
|
|
* // The signed-in user info.
|
|
* const user = result.user;
|
|
* // This gives you a Google Access Token.
|
|
* const credential = GoogleAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
class GoogleAuthProvider extends BaseOAuthProvider {
|
|
constructor() {
|
|
super("google.com" /* ProviderId.GOOGLE */);
|
|
this.addScope('profile');
|
|
}
|
|
/**
|
|
* Creates a credential for Google. At least one of ID token and access token is required.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // \`googleUser\` from the onsuccess Google Sign In callback.
|
|
* const credential = GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token);
|
|
* const result = await signInWithCredential(credential);
|
|
* ```
|
|
*
|
|
* @param idToken - Google ID token.
|
|
* @param accessToken - Google access token.
|
|
*/
|
|
static credential(idToken, accessToken) {
|
|
return OAuthCredential._fromParams({
|
|
providerId: GoogleAuthProvider.PROVIDER_ID,
|
|
signInMethod: GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD,
|
|
idToken,
|
|
accessToken
|
|
});
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
return GoogleAuthProvider.credentialFromTaggedObject(userCredential);
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was
|
|
* thrown during a sign-in, link, or reauthenticate operation.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return GoogleAuthProvider.credentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
static credentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse) {
|
|
return null;
|
|
}
|
|
const { oauthIdToken, oauthAccessToken } = tokenResponse;
|
|
if (!oauthIdToken && !oauthAccessToken) {
|
|
// This could be an oauth 1 credential or a phone credential
|
|
return null;
|
|
}
|
|
try {
|
|
return GoogleAuthProvider.credential(oauthIdToken, oauthAccessToken);
|
|
}
|
|
catch (_a) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
/** Always set to {@link SignInMethod}.GOOGLE. */
|
|
GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD = "google.com" /* SignInMethod.GOOGLE */;
|
|
/** Always set to {@link ProviderId}.GOOGLE. */
|
|
GoogleAuthProvider.PROVIDER_ID = "google.com" /* ProviderId.GOOGLE */;
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Provider for generating an {@link OAuthCredential} for {@link ProviderId}.GITHUB.
|
|
*
|
|
* @remarks
|
|
* GitHub requires an OAuth 2.0 redirect, so you can either handle the redirect directly, or use
|
|
* the {@link signInWithPopup} handler:
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a redirect.
|
|
* const provider = new GithubAuthProvider();
|
|
* // Start a sign in process for an unauthenticated user.
|
|
* provider.addScope('repo');
|
|
* await signInWithRedirect(auth, provider);
|
|
* // This will trigger a full page redirect away from your app
|
|
*
|
|
* // After returning from the redirect when your app initializes you can obtain the result
|
|
* const result = await getRedirectResult(auth);
|
|
* if (result) {
|
|
* // This is the signed-in user
|
|
* const user = result.user;
|
|
* // This gives you a GitHub Access Token.
|
|
* const credential = GithubAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a popup.
|
|
* const provider = new GithubAuthProvider();
|
|
* provider.addScope('repo');
|
|
* const result = await signInWithPopup(auth, provider);
|
|
*
|
|
* // The signed-in user info.
|
|
* const user = result.user;
|
|
* // This gives you a GitHub Access Token.
|
|
* const credential = GithubAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* ```
|
|
* @public
|
|
*/
|
|
class GithubAuthProvider extends BaseOAuthProvider {
|
|
constructor() {
|
|
super("github.com" /* ProviderId.GITHUB */);
|
|
}
|
|
/**
|
|
* Creates a credential for GitHub.
|
|
*
|
|
* @param accessToken - GitHub access token.
|
|
*/
|
|
static credential(accessToken) {
|
|
return OAuthCredential._fromParams({
|
|
providerId: GithubAuthProvider.PROVIDER_ID,
|
|
signInMethod: GithubAuthProvider.GITHUB_SIGN_IN_METHOD,
|
|
accessToken
|
|
});
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
return GithubAuthProvider.credentialFromTaggedObject(userCredential);
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was
|
|
* thrown during a sign-in, link, or reauthenticate operation.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return GithubAuthProvider.credentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
static credentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse || !('oauthAccessToken' in tokenResponse)) {
|
|
return null;
|
|
}
|
|
if (!tokenResponse.oauthAccessToken) {
|
|
return null;
|
|
}
|
|
try {
|
|
return GithubAuthProvider.credential(tokenResponse.oauthAccessToken);
|
|
}
|
|
catch (_a) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
/** Always set to {@link SignInMethod}.GITHUB. */
|
|
GithubAuthProvider.GITHUB_SIGN_IN_METHOD = "github.com" /* SignInMethod.GITHUB */;
|
|
/** Always set to {@link ProviderId}.GITHUB. */
|
|
GithubAuthProvider.PROVIDER_ID = "github.com" /* ProviderId.GITHUB */;
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const IDP_REQUEST_URI = 'http://localhost';
|
|
/**
|
|
* @public
|
|
*/
|
|
class SAMLAuthCredential extends AuthCredential {
|
|
/** @internal */
|
|
constructor(providerId, pendingToken) {
|
|
super(providerId, providerId);
|
|
this.pendingToken = pendingToken;
|
|
}
|
|
/** @internal */
|
|
_getIdTokenResponse(auth) {
|
|
const request = this.buildRequest();
|
|
return signInWithIdp(auth, request);
|
|
}
|
|
/** @internal */
|
|
_linkToIdToken(auth, idToken) {
|
|
const request = this.buildRequest();
|
|
request.idToken = idToken;
|
|
return signInWithIdp(auth, request);
|
|
}
|
|
/** @internal */
|
|
_getReauthenticationResolver(auth) {
|
|
const request = this.buildRequest();
|
|
request.autoCreate = false;
|
|
return signInWithIdp(auth, request);
|
|
}
|
|
/** {@inheritdoc AuthCredential.toJSON} */
|
|
toJSON() {
|
|
return {
|
|
signInMethod: this.signInMethod,
|
|
providerId: this.providerId,
|
|
pendingToken: this.pendingToken
|
|
};
|
|
}
|
|
/**
|
|
* Static method to deserialize a JSON representation of an object into an
|
|
* {@link AuthCredential}.
|
|
*
|
|
* @param json - Input can be either Object or the stringified representation of the object.
|
|
* When string is provided, JSON.parse would be called first.
|
|
*
|
|
* @returns If the JSON input does not represent an {@link AuthCredential}, null is returned.
|
|
*/
|
|
static fromJSON(json) {
|
|
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
|
const { providerId, signInMethod, pendingToken } = obj;
|
|
if (!providerId ||
|
|
!signInMethod ||
|
|
!pendingToken ||
|
|
providerId !== signInMethod) {
|
|
return null;
|
|
}
|
|
return new SAMLAuthCredential(providerId, pendingToken);
|
|
}
|
|
/**
|
|
* Helper static method to avoid exposing the constructor to end users.
|
|
*
|
|
* @internal
|
|
*/
|
|
static _create(providerId, pendingToken) {
|
|
return new SAMLAuthCredential(providerId, pendingToken);
|
|
}
|
|
buildRequest() {
|
|
return {
|
|
requestUri: IDP_REQUEST_URI,
|
|
returnSecureToken: true,
|
|
pendingToken: this.pendingToken
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const SAML_PROVIDER_PREFIX = 'saml.';
|
|
/**
|
|
* An {@link AuthProvider} for SAML.
|
|
*
|
|
* @public
|
|
*/
|
|
class SAMLAuthProvider extends FederatedAuthProvider {
|
|
/**
|
|
* Constructor. The providerId must start with "saml."
|
|
* @param providerId - SAML provider ID.
|
|
*/
|
|
constructor(providerId) {
|
|
_assert(providerId.startsWith(SAML_PROVIDER_PREFIX), "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
super(providerId);
|
|
}
|
|
/**
|
|
* Generates an {@link AuthCredential} from a {@link UserCredential} after a
|
|
* successful SAML flow completes.
|
|
*
|
|
* @remarks
|
|
*
|
|
* For example, to get an {@link AuthCredential}, you could write the
|
|
* following code:
|
|
*
|
|
* ```js
|
|
* const userCredential = await signInWithPopup(auth, samlProvider);
|
|
* const credential = SAMLAuthProvider.credentialFromResult(userCredential);
|
|
* ```
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
return SAMLAuthProvider.samlCredentialFromTaggedObject(userCredential);
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was
|
|
* thrown during a sign-in, link, or reauthenticate operation.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return SAMLAuthProvider.samlCredentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
/**
|
|
* Creates an {@link AuthCredential} from a JSON string or a plain object.
|
|
* @param json - A plain object or a JSON string
|
|
*/
|
|
static credentialFromJSON(json) {
|
|
const credential = SAMLAuthCredential.fromJSON(json);
|
|
_assert(credential, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
return credential;
|
|
}
|
|
static samlCredentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse) {
|
|
return null;
|
|
}
|
|
const { pendingToken, providerId } = tokenResponse;
|
|
if (!pendingToken || !providerId) {
|
|
return null;
|
|
}
|
|
try {
|
|
return SAMLAuthCredential._create(providerId, pendingToken);
|
|
}
|
|
catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Provider for generating an {@link OAuthCredential} for {@link ProviderId}.TWITTER.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a redirect.
|
|
* const provider = new TwitterAuthProvider();
|
|
* // Start a sign in process for an unauthenticated user.
|
|
* await signInWithRedirect(auth, provider);
|
|
* // This will trigger a full page redirect away from your app
|
|
*
|
|
* // After returning from the redirect when your app initializes you can obtain the result
|
|
* const result = await getRedirectResult(auth);
|
|
* if (result) {
|
|
* // This is the signed-in user
|
|
* const user = result.user;
|
|
* // This gives you a Twitter Access Token and Secret.
|
|
* const credential = TwitterAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* const secret = credential.secret;
|
|
* }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // Sign in using a popup.
|
|
* const provider = new TwitterAuthProvider();
|
|
* const result = await signInWithPopup(auth, provider);
|
|
*
|
|
* // The signed-in user info.
|
|
* const user = result.user;
|
|
* // This gives you a Twitter Access Token and Secret.
|
|
* const credential = TwitterAuthProvider.credentialFromResult(result);
|
|
* const token = credential.accessToken;
|
|
* const secret = credential.secret;
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
class TwitterAuthProvider extends BaseOAuthProvider {
|
|
constructor() {
|
|
super("twitter.com" /* ProviderId.TWITTER */);
|
|
}
|
|
/**
|
|
* Creates a credential for Twitter.
|
|
*
|
|
* @param token - Twitter access token.
|
|
* @param secret - Twitter secret.
|
|
*/
|
|
static credential(token, secret) {
|
|
return OAuthCredential._fromParams({
|
|
providerId: TwitterAuthProvider.PROVIDER_ID,
|
|
signInMethod: TwitterAuthProvider.TWITTER_SIGN_IN_METHOD,
|
|
oauthToken: token,
|
|
oauthTokenSecret: secret
|
|
});
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
return TwitterAuthProvider.credentialFromTaggedObject(userCredential);
|
|
}
|
|
/**
|
|
* Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was
|
|
* thrown during a sign-in, link, or reauthenticate operation.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return TwitterAuthProvider.credentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
static credentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse) {
|
|
return null;
|
|
}
|
|
const { oauthAccessToken, oauthTokenSecret } = tokenResponse;
|
|
if (!oauthAccessToken || !oauthTokenSecret) {
|
|
return null;
|
|
}
|
|
try {
|
|
return TwitterAuthProvider.credential(oauthAccessToken, oauthTokenSecret);
|
|
}
|
|
catch (_a) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
/** Always set to {@link SignInMethod}.TWITTER. */
|
|
TwitterAuthProvider.TWITTER_SIGN_IN_METHOD = "twitter.com" /* SignInMethod.TWITTER */;
|
|
/** Always set to {@link ProviderId}.TWITTER. */
|
|
TwitterAuthProvider.PROVIDER_ID = "twitter.com" /* ProviderId.TWITTER */;
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function signUp(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signUp" /* Endpoint.SIGN_UP */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class UserCredentialImpl {
|
|
constructor(params) {
|
|
this.user = params.user;
|
|
this.providerId = params.providerId;
|
|
this._tokenResponse = params._tokenResponse;
|
|
this.operationType = params.operationType;
|
|
}
|
|
static async _fromIdTokenResponse(auth, operationType, idTokenResponse, isAnonymous = false) {
|
|
const user = await UserImpl._fromIdTokenResponse(auth, idTokenResponse, isAnonymous);
|
|
const providerId = providerIdForResponse(idTokenResponse);
|
|
const userCred = new UserCredentialImpl({
|
|
user,
|
|
providerId,
|
|
_tokenResponse: idTokenResponse,
|
|
operationType
|
|
});
|
|
return userCred;
|
|
}
|
|
static async _forOperation(user, operationType, response) {
|
|
await user._updateTokensIfNecessary(response, /* reload */ true);
|
|
const providerId = providerIdForResponse(response);
|
|
return new UserCredentialImpl({
|
|
user,
|
|
providerId,
|
|
_tokenResponse: response,
|
|
operationType
|
|
});
|
|
}
|
|
}
|
|
function providerIdForResponse(response) {
|
|
if (response.providerId) {
|
|
return response.providerId;
|
|
}
|
|
if ('phoneNumber' in response) {
|
|
return "phone" /* ProviderId.PHONE */;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Asynchronously signs in as an anonymous user.
|
|
*
|
|
* @remarks
|
|
* If there is already an anonymous user signed in, that user will be returned; otherwise, a
|
|
* new anonymous user identity will be created and returned.
|
|
*
|
|
* This method is not supported by {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
*
|
|
* @public
|
|
*/
|
|
async function signInAnonymously(auth) {
|
|
var _a;
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const authInternal = _castAuth(auth);
|
|
await authInternal._initializationPromise;
|
|
if ((_a = authInternal.currentUser) === null || _a === void 0 ? void 0 : _a.isAnonymous) {
|
|
// If an anonymous user is already signed in, no need to sign them in again.
|
|
return new UserCredentialImpl({
|
|
user: authInternal.currentUser,
|
|
providerId: null,
|
|
operationType: "signIn" /* OperationType.SIGN_IN */
|
|
});
|
|
}
|
|
const response = await signUp(authInternal, {
|
|
returnSecureToken: true
|
|
});
|
|
const userCredential = await UserCredentialImpl._fromIdTokenResponse(authInternal, "signIn" /* OperationType.SIGN_IN */, response, true);
|
|
await authInternal._updateCurrentUser(userCredential.user);
|
|
return userCredential;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class MultiFactorError extends util.FirebaseError {
|
|
constructor(auth, error, operationType, user) {
|
|
var _a;
|
|
super(error.code, error.message);
|
|
this.operationType = operationType;
|
|
this.user = user;
|
|
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
Object.setPrototypeOf(this, MultiFactorError.prototype);
|
|
this.customData = {
|
|
appName: auth.name,
|
|
tenantId: (_a = auth.tenantId) !== null && _a !== void 0 ? _a : undefined,
|
|
_serverResponse: error.customData._serverResponse,
|
|
operationType
|
|
};
|
|
}
|
|
static _fromErrorAndOperation(auth, error, operationType, user) {
|
|
return new MultiFactorError(auth, error, operationType, user);
|
|
}
|
|
}
|
|
function _processCredentialSavingMfaContextIfNecessary(auth, operationType, credential, user) {
|
|
const idTokenProvider = operationType === "reauthenticate" /* OperationType.REAUTHENTICATE */
|
|
? credential._getReauthenticationResolver(auth)
|
|
: credential._getIdTokenResponse(auth);
|
|
return idTokenProvider.catch(error => {
|
|
if (error.code === `auth/${"multi-factor-auth-required" /* AuthErrorCode.MFA_REQUIRED */}`) {
|
|
throw MultiFactorError._fromErrorAndOperation(auth, error, operationType, user);
|
|
}
|
|
throw error;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Takes a set of UserInfo provider data and converts it to a set of names
|
|
*/
|
|
function providerDataAsNames(providerData) {
|
|
return new Set(providerData
|
|
.map(({ providerId }) => providerId)
|
|
.filter(pid => !!pid));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Unlinks a provider from a user account.
|
|
*
|
|
* @param user - The user.
|
|
* @param providerId - The provider to unlink.
|
|
*
|
|
* @public
|
|
*/
|
|
async function unlink(user, providerId) {
|
|
const userInternal = util.getModularInstance(user);
|
|
await _assertLinkedStatus(true, userInternal, providerId);
|
|
const { providerUserInfo } = await deleteLinkedAccounts(userInternal.auth, {
|
|
idToken: await userInternal.getIdToken(),
|
|
deleteProvider: [providerId]
|
|
});
|
|
const providersLeft = providerDataAsNames(providerUserInfo || []);
|
|
userInternal.providerData = userInternal.providerData.filter(pd => providersLeft.has(pd.providerId));
|
|
if (!providersLeft.has("phone" /* ProviderId.PHONE */)) {
|
|
userInternal.phoneNumber = null;
|
|
}
|
|
await userInternal.auth._persistUserIfCurrent(userInternal);
|
|
return userInternal;
|
|
}
|
|
async function _link(user, credential, bypassAuthState = false) {
|
|
const response = await _logoutIfInvalidated(user, credential._linkToIdToken(user.auth, await user.getIdToken()), bypassAuthState);
|
|
return UserCredentialImpl._forOperation(user, "link" /* OperationType.LINK */, response);
|
|
}
|
|
async function _assertLinkedStatus(expected, user, provider) {
|
|
await _reloadWithoutSaving(user);
|
|
const providerIds = providerDataAsNames(user.providerData);
|
|
const code = expected === false
|
|
? "provider-already-linked" /* AuthErrorCode.PROVIDER_ALREADY_LINKED */
|
|
: "no-such-provider" /* AuthErrorCode.NO_SUCH_PROVIDER */;
|
|
_assert(providerIds.has(provider) === expected, user.auth, code);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function _reauthenticate(user, credential, bypassAuthState = false) {
|
|
const { auth } = user;
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const operationType = "reauthenticate" /* OperationType.REAUTHENTICATE */;
|
|
try {
|
|
const response = await _logoutIfInvalidated(user, _processCredentialSavingMfaContextIfNecessary(auth, operationType, credential, user), bypassAuthState);
|
|
_assert(response.idToken, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const parsed = _parseToken(response.idToken);
|
|
_assert(parsed, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const { sub: localId } = parsed;
|
|
_assert(user.uid === localId, auth, "user-mismatch" /* AuthErrorCode.USER_MISMATCH */);
|
|
return UserCredentialImpl._forOperation(user, operationType, response);
|
|
}
|
|
catch (e) {
|
|
// Convert user deleted error into user mismatch
|
|
if ((e === null || e === void 0 ? void 0 : e.code) === `auth/${"user-not-found" /* AuthErrorCode.USER_DELETED */}`) {
|
|
_fail(auth, "user-mismatch" /* AuthErrorCode.USER_MISMATCH */);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function _signInWithCredential(auth, credential, bypassAuthState = false) {
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const operationType = "signIn" /* OperationType.SIGN_IN */;
|
|
const response = await _processCredentialSavingMfaContextIfNecessary(auth, operationType, credential);
|
|
const userCredential = await UserCredentialImpl._fromIdTokenResponse(auth, operationType, response);
|
|
if (!bypassAuthState) {
|
|
await auth._updateCurrentUser(userCredential.user);
|
|
}
|
|
return userCredential;
|
|
}
|
|
/**
|
|
* Asynchronously signs in with the given credentials.
|
|
*
|
|
* @remarks
|
|
* An {@link AuthProvider} can be used to generate the credential.
|
|
*
|
|
* This method is not supported by {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param credential - The auth credential.
|
|
*
|
|
* @public
|
|
*/
|
|
async function signInWithCredential(auth, credential) {
|
|
return _signInWithCredential(_castAuth(auth), credential);
|
|
}
|
|
/**
|
|
* Links the user account with the given credentials.
|
|
*
|
|
* @remarks
|
|
* An {@link AuthProvider} can be used to generate the credential.
|
|
*
|
|
* @param user - The user.
|
|
* @param credential - The auth credential.
|
|
*
|
|
* @public
|
|
*/
|
|
async function linkWithCredential(user, credential) {
|
|
const userInternal = util.getModularInstance(user);
|
|
await _assertLinkedStatus(false, userInternal, credential.providerId);
|
|
return _link(userInternal, credential);
|
|
}
|
|
/**
|
|
* Re-authenticates a user using a fresh credential.
|
|
*
|
|
* @remarks
|
|
* Use before operations such as {@link updatePassword} that require tokens from recent sign-in
|
|
* attempts. This method can be used to recover from a `CREDENTIAL_TOO_OLD_LOGIN_AGAIN` error
|
|
* or a `TOKEN_EXPIRED` error.
|
|
*
|
|
* This method is not supported on any {@link User} signed in by {@link Auth} instances
|
|
* created with a {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param user - The user.
|
|
* @param credential - The auth credential.
|
|
*
|
|
* @public
|
|
*/
|
|
async function reauthenticateWithCredential(user, credential) {
|
|
return _reauthenticate(util.getModularInstance(user), credential);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function signInWithCustomToken$1(auth, request) {
|
|
return _performSignInRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:signInWithCustomToken" /* Endpoint.SIGN_IN_WITH_CUSTOM_TOKEN */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Asynchronously signs in using a custom token.
|
|
*
|
|
* @remarks
|
|
* Custom tokens are used to integrate Firebase Auth with existing auth systems, and must
|
|
* be generated by an auth backend using the
|
|
* {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createcustomtoken | createCustomToken}
|
|
* method in the {@link https://firebase.google.com/docs/auth/admin | Admin SDK} .
|
|
*
|
|
* Fails with an error if the token is invalid, expired, or not accepted by the Firebase Auth service.
|
|
*
|
|
* This method is not supported by {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param customToken - The custom token to sign in with.
|
|
*
|
|
* @public
|
|
*/
|
|
async function signInWithCustomToken(auth, customToken) {
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const authInternal = _castAuth(auth);
|
|
const response = await signInWithCustomToken$1(authInternal, {
|
|
token: customToken,
|
|
returnSecureToken: true
|
|
});
|
|
const cred = await UserCredentialImpl._fromIdTokenResponse(authInternal, "signIn" /* OperationType.SIGN_IN */, response);
|
|
await authInternal._updateCurrentUser(cred.user);
|
|
return cred;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class MultiFactorInfoImpl {
|
|
constructor(factorId, response) {
|
|
this.factorId = factorId;
|
|
this.uid = response.mfaEnrollmentId;
|
|
this.enrollmentTime = new Date(response.enrolledAt).toUTCString();
|
|
this.displayName = response.displayName;
|
|
}
|
|
static _fromServerResponse(auth, enrollment) {
|
|
if ('phoneInfo' in enrollment) {
|
|
return PhoneMultiFactorInfoImpl._fromServerResponse(auth, enrollment);
|
|
}
|
|
else if ('totpInfo' in enrollment) {
|
|
return TotpMultiFactorInfoImpl._fromServerResponse(auth, enrollment);
|
|
}
|
|
return _fail(auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
}
|
|
}
|
|
class PhoneMultiFactorInfoImpl extends MultiFactorInfoImpl {
|
|
constructor(response) {
|
|
super("phone" /* FactorId.PHONE */, response);
|
|
this.phoneNumber = response.phoneInfo;
|
|
}
|
|
static _fromServerResponse(_auth, enrollment) {
|
|
return new PhoneMultiFactorInfoImpl(enrollment);
|
|
}
|
|
}
|
|
class TotpMultiFactorInfoImpl extends MultiFactorInfoImpl {
|
|
constructor(response) {
|
|
super("totp" /* FactorId.TOTP */, response);
|
|
}
|
|
static _fromServerResponse(_auth, enrollment) {
|
|
return new TotpMultiFactorInfoImpl(enrollment);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _setActionCodeSettingsOnRequest(auth, request, actionCodeSettings) {
|
|
var _a;
|
|
_assert(((_a = actionCodeSettings.url) === null || _a === void 0 ? void 0 : _a.length) > 0, auth, "invalid-continue-uri" /* AuthErrorCode.INVALID_CONTINUE_URI */);
|
|
_assert(typeof actionCodeSettings.dynamicLinkDomain === 'undefined' ||
|
|
actionCodeSettings.dynamicLinkDomain.length > 0, auth, "invalid-dynamic-link-domain" /* AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN */);
|
|
request.continueUrl = actionCodeSettings.url;
|
|
request.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
|
|
request.canHandleCodeInApp = actionCodeSettings.handleCodeInApp;
|
|
if (actionCodeSettings.iOS) {
|
|
_assert(actionCodeSettings.iOS.bundleId.length > 0, auth, "missing-ios-bundle-id" /* AuthErrorCode.MISSING_IOS_BUNDLE_ID */);
|
|
request.iOSBundleId = actionCodeSettings.iOS.bundleId;
|
|
}
|
|
if (actionCodeSettings.android) {
|
|
_assert(actionCodeSettings.android.packageName.length > 0, auth, "missing-android-pkg-name" /* AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME */);
|
|
request.androidInstallApp = actionCodeSettings.android.installApp;
|
|
request.androidMinimumVersionCode =
|
|
actionCodeSettings.android.minimumVersion;
|
|
request.androidPackageName = actionCodeSettings.android.packageName;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Updates the password policy cached in the {@link Auth} instance if a policy is already
|
|
* cached for the project or tenant.
|
|
*
|
|
* @remarks
|
|
* We only fetch the password policy if the password did not meet policy requirements and
|
|
* there is an existing policy cached. A developer must call validatePassword at least
|
|
* once for the cache to be automatically updated.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
*
|
|
* @private
|
|
*/
|
|
async function recachePasswordPolicy(auth) {
|
|
const authInternal = _castAuth(auth);
|
|
if (authInternal._getPasswordPolicyInternal()) {
|
|
await authInternal._updatePasswordPolicy();
|
|
}
|
|
}
|
|
/**
|
|
* Sends a password reset email to the given email address. This method does not throw an error when
|
|
* there's no user account with the given email address and
|
|
* {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection}
|
|
* is enabled.
|
|
*
|
|
* @remarks
|
|
* To complete the password reset, call {@link confirmPasswordReset} with the code supplied in
|
|
* the email sent to the user, along with the new password specified by the user.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const actionCodeSettings = {
|
|
* url: 'https://www.example.com/?email=user@example.com',
|
|
* iOS: {
|
|
* bundleId: 'com.example.ios'
|
|
* },
|
|
* android: {
|
|
* packageName: 'com.example.android',
|
|
* installApp: true,
|
|
* minimumVersion: '12'
|
|
* },
|
|
* handleCodeInApp: true
|
|
* };
|
|
* await sendPasswordResetEmail(auth, 'user@example.com', actionCodeSettings);
|
|
* // Obtain code from user.
|
|
* await confirmPasswordReset('user@example.com', code);
|
|
* ```
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param email - The user's email address.
|
|
* @param actionCodeSettings - The {@link ActionCodeSettings}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function sendPasswordResetEmail(auth, email, actionCodeSettings) {
|
|
const authInternal = _castAuth(auth);
|
|
const request = {
|
|
requestType: "PASSWORD_RESET" /* ActionCodeOperation.PASSWORD_RESET */,
|
|
email,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
};
|
|
if (actionCodeSettings) {
|
|
_setActionCodeSettingsOnRequest(authInternal, request, actionCodeSettings);
|
|
}
|
|
await handleRecaptchaFlow(authInternal, request, "getOobCode" /* RecaptchaActionName.GET_OOB_CODE */, sendPasswordResetEmail$1, "EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */);
|
|
}
|
|
/**
|
|
* Completes the password reset process, given a confirmation code and new password.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param oobCode - A confirmation code sent to the user.
|
|
* @param newPassword - The new password.
|
|
*
|
|
* @public
|
|
*/
|
|
async function confirmPasswordReset(auth, oobCode, newPassword) {
|
|
await resetPassword(util.getModularInstance(auth), {
|
|
oobCode,
|
|
newPassword
|
|
})
|
|
.catch(async (error) => {
|
|
if (error.code ===
|
|
`auth/${"password-does-not-meet-requirements" /* AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS */}`) {
|
|
void recachePasswordPolicy(auth);
|
|
}
|
|
throw error;
|
|
});
|
|
// Do not return the email.
|
|
}
|
|
/**
|
|
* Applies a verification code sent to the user by email or other out-of-band mechanism.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param oobCode - A verification code sent to the user.
|
|
*
|
|
* @public
|
|
*/
|
|
async function applyActionCode(auth, oobCode) {
|
|
await applyActionCode$1(util.getModularInstance(auth), { oobCode });
|
|
}
|
|
/**
|
|
* Checks a verification code sent to the user by email or other out-of-band mechanism.
|
|
*
|
|
* @returns metadata about the code.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param oobCode - A verification code sent to the user.
|
|
*
|
|
* @public
|
|
*/
|
|
async function checkActionCode(auth, oobCode) {
|
|
const authModular = util.getModularInstance(auth);
|
|
const response = await resetPassword(authModular, { oobCode });
|
|
// Email could be empty only if the request type is EMAIL_SIGNIN or
|
|
// VERIFY_AND_CHANGE_EMAIL.
|
|
// New email should not be empty if the request type is
|
|
// VERIFY_AND_CHANGE_EMAIL.
|
|
// Multi-factor info could not be empty if the request type is
|
|
// REVERT_SECOND_FACTOR_ADDITION.
|
|
const operation = response.requestType;
|
|
_assert(operation, authModular, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
switch (operation) {
|
|
case "EMAIL_SIGNIN" /* ActionCodeOperation.EMAIL_SIGNIN */:
|
|
break;
|
|
case "VERIFY_AND_CHANGE_EMAIL" /* ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL */:
|
|
_assert(response.newEmail, authModular, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
break;
|
|
case "REVERT_SECOND_FACTOR_ADDITION" /* ActionCodeOperation.REVERT_SECOND_FACTOR_ADDITION */:
|
|
_assert(response.mfaInfo, authModular, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
// fall through
|
|
default:
|
|
_assert(response.email, authModular, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
}
|
|
// The multi-factor info for revert second factor addition
|
|
let multiFactorInfo = null;
|
|
if (response.mfaInfo) {
|
|
multiFactorInfo = MultiFactorInfoImpl._fromServerResponse(_castAuth(authModular), response.mfaInfo);
|
|
}
|
|
return {
|
|
data: {
|
|
email: (response.requestType === "VERIFY_AND_CHANGE_EMAIL" /* ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL */
|
|
? response.newEmail
|
|
: response.email) || null,
|
|
previousEmail: (response.requestType === "VERIFY_AND_CHANGE_EMAIL" /* ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL */
|
|
? response.email
|
|
: response.newEmail) || null,
|
|
multiFactorInfo
|
|
},
|
|
operation
|
|
};
|
|
}
|
|
/**
|
|
* Checks a password reset code sent to the user by email or other out-of-band mechanism.
|
|
*
|
|
* @returns the user's email address if valid.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param code - A verification code sent to the user.
|
|
*
|
|
* @public
|
|
*/
|
|
async function verifyPasswordResetCode(auth, code) {
|
|
const { data } = await checkActionCode(util.getModularInstance(auth), code);
|
|
// Email should always be present since a code was sent to it
|
|
return data.email;
|
|
}
|
|
/**
|
|
* Creates a new user account associated with the specified email address and password.
|
|
*
|
|
* @remarks
|
|
* On successful creation of the user account, this user will also be signed in to your application.
|
|
*
|
|
* User account creation can fail if the account already exists or the password is invalid.
|
|
*
|
|
* This method is not supported on {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* Note: The email address acts as a unique identifier for the user and enables an email-based
|
|
* password reset. This function will create a new user account and set the initial user password.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param email - The user's email address.
|
|
* @param password - The user's chosen password.
|
|
*
|
|
* @public
|
|
*/
|
|
async function createUserWithEmailAndPassword(auth, email, password) {
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const authInternal = _castAuth(auth);
|
|
const request = {
|
|
returnSecureToken: true,
|
|
email,
|
|
password,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
};
|
|
const signUpResponse = handleRecaptchaFlow(authInternal, request, "signUpPassword" /* RecaptchaActionName.SIGN_UP_PASSWORD */, signUp, "EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */);
|
|
const response = await signUpResponse.catch(error => {
|
|
if (error.code === `auth/${"password-does-not-meet-requirements" /* AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS */}`) {
|
|
void recachePasswordPolicy(auth);
|
|
}
|
|
throw error;
|
|
});
|
|
const userCredential = await UserCredentialImpl._fromIdTokenResponse(authInternal, "signIn" /* OperationType.SIGN_IN */, response);
|
|
await authInternal._updateCurrentUser(userCredential.user);
|
|
return userCredential;
|
|
}
|
|
/**
|
|
* Asynchronously signs in using an email and password.
|
|
*
|
|
* @remarks
|
|
* Fails with an error if the email address and password do not match. When
|
|
* {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection}
|
|
* is enabled, this method fails with "auth/invalid-credential" in case of an invalid
|
|
* email/password.
|
|
*
|
|
* This method is not supported on {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* Note: The user's password is NOT the password used to access the user's email account. The
|
|
* email address serves as a unique identifier for the user, and the password is used to access
|
|
* the user's account in your Firebase project. See also: {@link createUserWithEmailAndPassword}.
|
|
*
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param email - The users email address.
|
|
* @param password - The users password.
|
|
*
|
|
* @public
|
|
*/
|
|
function signInWithEmailAndPassword(auth, email, password) {
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
return signInWithCredential(util.getModularInstance(auth), EmailAuthProvider.credential(email, password)).catch(async (error) => {
|
|
if (error.code === `auth/${"password-does-not-meet-requirements" /* AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS */}`) {
|
|
void recachePasswordPolicy(auth);
|
|
}
|
|
throw error;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Sends a sign-in email link to the user with the specified email.
|
|
*
|
|
* @remarks
|
|
* The sign-in operation has to always be completed in the app unlike other out of band email
|
|
* actions (password reset and email verifications). This is because, at the end of the flow,
|
|
* the user is expected to be signed in and their Auth state persisted within the app.
|
|
*
|
|
* To complete sign in with the email link, call {@link signInWithEmailLink} with the email
|
|
* address and the email link supplied in the email sent to the user.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const actionCodeSettings = {
|
|
* url: 'https://www.example.com/?email=user@example.com',
|
|
* iOS: {
|
|
* bundleId: 'com.example.ios'
|
|
* },
|
|
* android: {
|
|
* packageName: 'com.example.android',
|
|
* installApp: true,
|
|
* minimumVersion: '12'
|
|
* },
|
|
* handleCodeInApp: true
|
|
* };
|
|
* await sendSignInLinkToEmail(auth, 'user@example.com', actionCodeSettings);
|
|
* // Obtain emailLink from the user.
|
|
* if(isSignInWithEmailLink(auth, emailLink)) {
|
|
* await signInWithEmailLink(auth, 'user@example.com', emailLink);
|
|
* }
|
|
* ```
|
|
*
|
|
* @param authInternal - The {@link Auth} instance.
|
|
* @param email - The user's email address.
|
|
* @param actionCodeSettings - The {@link ActionCodeSettings}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function sendSignInLinkToEmail(auth, email, actionCodeSettings) {
|
|
const authInternal = _castAuth(auth);
|
|
const request = {
|
|
requestType: "EMAIL_SIGNIN" /* ActionCodeOperation.EMAIL_SIGNIN */,
|
|
email,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
};
|
|
function setActionCodeSettings(request, actionCodeSettings) {
|
|
_assert(actionCodeSettings.handleCodeInApp, authInternal, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
if (actionCodeSettings) {
|
|
_setActionCodeSettingsOnRequest(authInternal, request, actionCodeSettings);
|
|
}
|
|
}
|
|
setActionCodeSettings(request, actionCodeSettings);
|
|
await handleRecaptchaFlow(authInternal, request, "getOobCode" /* RecaptchaActionName.GET_OOB_CODE */, sendSignInLinkToEmail$1, "EMAIL_PASSWORD_PROVIDER" /* RecaptchaAuthProvider.EMAIL_PASSWORD_PROVIDER */);
|
|
}
|
|
/**
|
|
* Checks if an incoming link is a sign-in with email link suitable for {@link signInWithEmailLink}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param emailLink - The link sent to the user's email address.
|
|
*
|
|
* @public
|
|
*/
|
|
function isSignInWithEmailLink(auth, emailLink) {
|
|
const actionCodeUrl = ActionCodeURL.parseLink(emailLink);
|
|
return (actionCodeUrl === null || actionCodeUrl === void 0 ? void 0 : actionCodeUrl.operation) === "EMAIL_SIGNIN" /* ActionCodeOperation.EMAIL_SIGNIN */;
|
|
}
|
|
/**
|
|
* Asynchronously signs in using an email and sign-in email link.
|
|
*
|
|
* @remarks
|
|
* If no link is passed, the link is inferred from the current URL.
|
|
*
|
|
* Fails with an error if the email address is invalid or OTP in email link expires.
|
|
*
|
|
* This method is not supported by {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* Note: Confirm the link is a sign-in email link before calling this method firebase.auth.Auth.isSignInWithEmailLink.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const actionCodeSettings = {
|
|
* url: 'https://www.example.com/?email=user@example.com',
|
|
* iOS: {
|
|
* bundleId: 'com.example.ios'
|
|
* },
|
|
* android: {
|
|
* packageName: 'com.example.android',
|
|
* installApp: true,
|
|
* minimumVersion: '12'
|
|
* },
|
|
* handleCodeInApp: true
|
|
* };
|
|
* await sendSignInLinkToEmail(auth, 'user@example.com', actionCodeSettings);
|
|
* // Obtain emailLink from the user.
|
|
* if(isSignInWithEmailLink(auth, emailLink)) {
|
|
* await signInWithEmailLink(auth, 'user@example.com', emailLink);
|
|
* }
|
|
* ```
|
|
*
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param email - The user's email address.
|
|
* @param emailLink - The link sent to the user's email address.
|
|
*
|
|
* @public
|
|
*/
|
|
async function signInWithEmailLink(auth, email, emailLink) {
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const authModular = util.getModularInstance(auth);
|
|
const credential = EmailAuthProvider.credentialWithLink(email, emailLink || _getCurrentUrl());
|
|
// Check if the tenant ID in the email link matches the tenant ID on Auth
|
|
// instance.
|
|
_assert(credential._tenantId === (authModular.tenantId || null), authModular, "tenant-id-mismatch" /* AuthErrorCode.TENANT_ID_MISMATCH */);
|
|
return signInWithCredential(authModular, credential);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function createAuthUri(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:createAuthUri" /* Endpoint.CREATE_AUTH_URI */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Gets the list of possible sign in methods for the given email address. This method returns an
|
|
* empty list when
|
|
* {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection}
|
|
* is enabled, irrespective of the number of authentication methods available for the given email.
|
|
*
|
|
* @remarks
|
|
* This is useful to differentiate methods of sign-in for the same provider, eg.
|
|
* {@link EmailAuthProvider} which has 2 methods of sign-in,
|
|
* {@link SignInMethod}.EMAIL_PASSWORD and
|
|
* {@link SignInMethod}.EMAIL_LINK.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param email - The user's email address.
|
|
*
|
|
* Deprecated. Migrating off of this method is recommended as a security best-practice.
|
|
* Learn more in the Identity Platform documentation for
|
|
* {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection}.
|
|
* @public
|
|
*/
|
|
async function fetchSignInMethodsForEmail(auth, email) {
|
|
// createAuthUri returns an error if continue URI is not http or https.
|
|
// For environments like Cordova, Chrome extensions, native frameworks, file
|
|
// systems, etc, use http://localhost as continue URL.
|
|
const continueUri = _isHttpOrHttps() ? _getCurrentUrl() : 'http://localhost';
|
|
const request = {
|
|
identifier: email,
|
|
continueUri
|
|
};
|
|
const { signinMethods } = await createAuthUri(util.getModularInstance(auth), request);
|
|
return signinMethods || [];
|
|
}
|
|
/**
|
|
* Sends a verification email to a user.
|
|
*
|
|
* @remarks
|
|
* The verification process is completed by calling {@link applyActionCode}.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const actionCodeSettings = {
|
|
* url: 'https://www.example.com/?email=user@example.com',
|
|
* iOS: {
|
|
* bundleId: 'com.example.ios'
|
|
* },
|
|
* android: {
|
|
* packageName: 'com.example.android',
|
|
* installApp: true,
|
|
* minimumVersion: '12'
|
|
* },
|
|
* handleCodeInApp: true
|
|
* };
|
|
* await sendEmailVerification(user, actionCodeSettings);
|
|
* // Obtain code from the user.
|
|
* await applyActionCode(auth, code);
|
|
* ```
|
|
*
|
|
* @param user - The user.
|
|
* @param actionCodeSettings - The {@link ActionCodeSettings}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function sendEmailVerification(user, actionCodeSettings) {
|
|
const userInternal = util.getModularInstance(user);
|
|
const idToken = await user.getIdToken();
|
|
const request = {
|
|
requestType: "VERIFY_EMAIL" /* ActionCodeOperation.VERIFY_EMAIL */,
|
|
idToken
|
|
};
|
|
if (actionCodeSettings) {
|
|
_setActionCodeSettingsOnRequest(userInternal.auth, request, actionCodeSettings);
|
|
}
|
|
const { email } = await sendEmailVerification$1(userInternal.auth, request);
|
|
if (email !== user.email) {
|
|
await user.reload();
|
|
}
|
|
}
|
|
/**
|
|
* Sends a verification email to a new email address.
|
|
*
|
|
* @remarks
|
|
* The user's email will be updated to the new one after being verified.
|
|
*
|
|
* If you have a custom email action handler, you can complete the verification process by calling
|
|
* {@link applyActionCode}.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const actionCodeSettings = {
|
|
* url: 'https://www.example.com/?email=user@example.com',
|
|
* iOS: {
|
|
* bundleId: 'com.example.ios'
|
|
* },
|
|
* android: {
|
|
* packageName: 'com.example.android',
|
|
* installApp: true,
|
|
* minimumVersion: '12'
|
|
* },
|
|
* handleCodeInApp: true
|
|
* };
|
|
* await verifyBeforeUpdateEmail(user, 'newemail@example.com', actionCodeSettings);
|
|
* // Obtain code from the user.
|
|
* await applyActionCode(auth, code);
|
|
* ```
|
|
*
|
|
* @param user - The user.
|
|
* @param newEmail - The new email address to be verified before update.
|
|
* @param actionCodeSettings - The {@link ActionCodeSettings}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function verifyBeforeUpdateEmail(user, newEmail, actionCodeSettings) {
|
|
const userInternal = util.getModularInstance(user);
|
|
const idToken = await user.getIdToken();
|
|
const request = {
|
|
requestType: "VERIFY_AND_CHANGE_EMAIL" /* ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL */,
|
|
idToken,
|
|
newEmail
|
|
};
|
|
if (actionCodeSettings) {
|
|
_setActionCodeSettingsOnRequest(userInternal.auth, request, actionCodeSettings);
|
|
}
|
|
const { email } = await verifyAndChangeEmail(userInternal.auth, request);
|
|
if (email !== user.email) {
|
|
// If the local copy of the email on user is outdated, reload the
|
|
// user.
|
|
await user.reload();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
async function updateProfile$1(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v1/accounts:update" /* Endpoint.SET_ACCOUNT_INFO */, request);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Updates a user's profile data.
|
|
*
|
|
* @param user - The user.
|
|
* @param profile - The profile's `displayName` and `photoURL` to update.
|
|
*
|
|
* @public
|
|
*/
|
|
async function updateProfile(user, { displayName, photoURL: photoUrl }) {
|
|
if (displayName === undefined && photoUrl === undefined) {
|
|
return;
|
|
}
|
|
const userInternal = util.getModularInstance(user);
|
|
const idToken = await userInternal.getIdToken();
|
|
const profileRequest = {
|
|
idToken,
|
|
displayName,
|
|
photoUrl,
|
|
returnSecureToken: true
|
|
};
|
|
const response = await _logoutIfInvalidated(userInternal, updateProfile$1(userInternal.auth, profileRequest));
|
|
userInternal.displayName = response.displayName || null;
|
|
userInternal.photoURL = response.photoUrl || null;
|
|
// Update the password provider as well
|
|
const passwordProvider = userInternal.providerData.find(({ providerId }) => providerId === "password" /* ProviderId.PASSWORD */);
|
|
if (passwordProvider) {
|
|
passwordProvider.displayName = userInternal.displayName;
|
|
passwordProvider.photoURL = userInternal.photoURL;
|
|
}
|
|
await userInternal._updateTokensIfNecessary(response);
|
|
}
|
|
/**
|
|
* Updates the user's email address.
|
|
*
|
|
* @remarks
|
|
* An email will be sent to the original email address (if it was set) that allows to revoke the
|
|
* email address change, in order to protect them from account hijacking.
|
|
*
|
|
* This method is not supported on any {@link User} signed in by {@link Auth} instances
|
|
* created with a {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* Important: this is a security sensitive operation that requires the user to have recently signed
|
|
* in. If this requirement isn't met, ask the user to authenticate again and then call
|
|
* {@link reauthenticateWithCredential}.
|
|
*
|
|
* @param user - The user.
|
|
* @param newEmail - The new email address.
|
|
*
|
|
* Throws "auth/operation-not-allowed" error when
|
|
* {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection}
|
|
* is enabled.
|
|
* Deprecated - Use {@link verifyBeforeUpdateEmail} instead.
|
|
*
|
|
* @public
|
|
*/
|
|
function updateEmail(user, newEmail) {
|
|
const userInternal = util.getModularInstance(user);
|
|
if (app._isFirebaseServerApp(userInternal.auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(userInternal.auth));
|
|
}
|
|
return updateEmailOrPassword(userInternal, newEmail, null);
|
|
}
|
|
/**
|
|
* Updates the user's password.
|
|
*
|
|
* @remarks
|
|
* Important: this is a security sensitive operation that requires the user to have recently signed
|
|
* in. If this requirement isn't met, ask the user to authenticate again and then call
|
|
* {@link reauthenticateWithCredential}.
|
|
*
|
|
* @param user - The user.
|
|
* @param newPassword - The new password.
|
|
*
|
|
* @public
|
|
*/
|
|
function updatePassword(user, newPassword) {
|
|
return updateEmailOrPassword(util.getModularInstance(user), null, newPassword);
|
|
}
|
|
async function updateEmailOrPassword(user, email, password) {
|
|
const { auth } = user;
|
|
const idToken = await user.getIdToken();
|
|
const request = {
|
|
idToken,
|
|
returnSecureToken: true
|
|
};
|
|
if (email) {
|
|
request.email = email;
|
|
}
|
|
if (password) {
|
|
request.password = password;
|
|
}
|
|
const response = await _logoutIfInvalidated(user, updateEmailPassword(auth, request));
|
|
await user._updateTokensIfNecessary(response, /* reload */ true);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Parse the `AdditionalUserInfo` from the ID token response.
|
|
*
|
|
*/
|
|
function _fromIdTokenResponse(idTokenResponse) {
|
|
var _a, _b;
|
|
if (!idTokenResponse) {
|
|
return null;
|
|
}
|
|
const { providerId } = idTokenResponse;
|
|
const profile = idTokenResponse.rawUserInfo
|
|
? JSON.parse(idTokenResponse.rawUserInfo)
|
|
: {};
|
|
const isNewUser = idTokenResponse.isNewUser ||
|
|
idTokenResponse.kind === "identitytoolkit#SignupNewUserResponse" /* IdTokenResponseKind.SignupNewUser */;
|
|
if (!providerId && (idTokenResponse === null || idTokenResponse === void 0 ? void 0 : idTokenResponse.idToken)) {
|
|
const signInProvider = (_b = (_a = _parseToken(idTokenResponse.idToken)) === null || _a === void 0 ? void 0 : _a.firebase) === null || _b === void 0 ? void 0 : _b['sign_in_provider'];
|
|
if (signInProvider) {
|
|
const filteredProviderId = signInProvider !== "anonymous" /* ProviderId.ANONYMOUS */ &&
|
|
signInProvider !== "custom" /* ProviderId.CUSTOM */
|
|
? signInProvider
|
|
: null;
|
|
// Uses generic class in accordance with the legacy SDK.
|
|
return new GenericAdditionalUserInfo(isNewUser, filteredProviderId);
|
|
}
|
|
}
|
|
if (!providerId) {
|
|
return null;
|
|
}
|
|
switch (providerId) {
|
|
case "facebook.com" /* ProviderId.FACEBOOK */:
|
|
return new FacebookAdditionalUserInfo(isNewUser, profile);
|
|
case "github.com" /* ProviderId.GITHUB */:
|
|
return new GithubAdditionalUserInfo(isNewUser, profile);
|
|
case "google.com" /* ProviderId.GOOGLE */:
|
|
return new GoogleAdditionalUserInfo(isNewUser, profile);
|
|
case "twitter.com" /* ProviderId.TWITTER */:
|
|
return new TwitterAdditionalUserInfo(isNewUser, profile, idTokenResponse.screenName || null);
|
|
case "custom" /* ProviderId.CUSTOM */:
|
|
case "anonymous" /* ProviderId.ANONYMOUS */:
|
|
return new GenericAdditionalUserInfo(isNewUser, null);
|
|
default:
|
|
return new GenericAdditionalUserInfo(isNewUser, providerId, profile);
|
|
}
|
|
}
|
|
class GenericAdditionalUserInfo {
|
|
constructor(isNewUser, providerId, profile = {}) {
|
|
this.isNewUser = isNewUser;
|
|
this.providerId = providerId;
|
|
this.profile = profile;
|
|
}
|
|
}
|
|
class FederatedAdditionalUserInfoWithUsername extends GenericAdditionalUserInfo {
|
|
constructor(isNewUser, providerId, profile, username) {
|
|
super(isNewUser, providerId, profile);
|
|
this.username = username;
|
|
}
|
|
}
|
|
class FacebookAdditionalUserInfo extends GenericAdditionalUserInfo {
|
|
constructor(isNewUser, profile) {
|
|
super(isNewUser, "facebook.com" /* ProviderId.FACEBOOK */, profile);
|
|
}
|
|
}
|
|
class GithubAdditionalUserInfo extends FederatedAdditionalUserInfoWithUsername {
|
|
constructor(isNewUser, profile) {
|
|
super(isNewUser, "github.com" /* ProviderId.GITHUB */, profile, typeof (profile === null || profile === void 0 ? void 0 : profile.login) === 'string' ? profile === null || profile === void 0 ? void 0 : profile.login : null);
|
|
}
|
|
}
|
|
class GoogleAdditionalUserInfo extends GenericAdditionalUserInfo {
|
|
constructor(isNewUser, profile) {
|
|
super(isNewUser, "google.com" /* ProviderId.GOOGLE */, profile);
|
|
}
|
|
}
|
|
class TwitterAdditionalUserInfo extends FederatedAdditionalUserInfoWithUsername {
|
|
constructor(isNewUser, profile, screenName) {
|
|
super(isNewUser, "twitter.com" /* ProviderId.TWITTER */, profile, screenName);
|
|
}
|
|
}
|
|
/**
|
|
* Extracts provider specific {@link AdditionalUserInfo} for the given credential.
|
|
*
|
|
* @param userCredential - The user credential.
|
|
*
|
|
* @public
|
|
*/
|
|
function getAdditionalUserInfo(userCredential) {
|
|
const { user, _tokenResponse } = userCredential;
|
|
if (user.isAnonymous && !_tokenResponse) {
|
|
// Handle the special case where signInAnonymously() gets called twice.
|
|
// No network call is made so there's nothing to actually fill this in
|
|
return {
|
|
providerId: null,
|
|
isNewUser: false,
|
|
profile: null
|
|
};
|
|
}
|
|
return _fromIdTokenResponse(_tokenResponse);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
// Non-optional auth methods.
|
|
/**
|
|
* Changes the type of persistence on the {@link Auth} instance for the currently saved
|
|
* `Auth` session and applies this type of persistence for future sign-in requests, including
|
|
* sign-in with redirect requests.
|
|
*
|
|
* @remarks
|
|
* This makes it easy for a user signing in to specify whether their session should be
|
|
* remembered or not. It also makes it easier to never persist the `Auth` state for applications
|
|
* that are shared by other users or have sensitive data.
|
|
*
|
|
* This method does not work in a Node.js environment or with {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* setPersistence(auth, browserSessionPersistence);
|
|
* ```
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param persistence - The {@link Persistence} to use.
|
|
* @returns A `Promise` that resolves once the persistence change has completed
|
|
*
|
|
* @public
|
|
*/
|
|
function setPersistence(auth, persistence) {
|
|
return util.getModularInstance(auth).setPersistence(persistence);
|
|
}
|
|
/**
|
|
* Loads the reCAPTCHA configuration into the `Auth` instance.
|
|
*
|
|
* @remarks
|
|
* This will load the reCAPTCHA config, which indicates whether the reCAPTCHA
|
|
* verification flow should be triggered for each auth provider, into the
|
|
* current Auth session.
|
|
*
|
|
* If initializeRecaptchaConfig() is not invoked, the auth flow will always start
|
|
* without reCAPTCHA verification. If the provider is configured to require reCAPTCHA
|
|
* verification, the SDK will transparently load the reCAPTCHA config and restart the
|
|
* auth flows.
|
|
*
|
|
* Thus, by calling this optional method, you will reduce the latency of future auth flows.
|
|
* Loading the reCAPTCHA config early will also enhance the signal collected by reCAPTCHA.
|
|
*
|
|
* This method does not work in a Node.js environment.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* initializeRecaptchaConfig(auth);
|
|
* ```
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
*
|
|
* @public
|
|
*/
|
|
function initializeRecaptchaConfig(auth) {
|
|
return _initializeRecaptchaConfig(auth);
|
|
}
|
|
/**
|
|
* Validates the password against the password policy configured for the project or tenant.
|
|
*
|
|
* @remarks
|
|
* If no tenant ID is set on the `Auth` instance, then this method will use the password
|
|
* policy configured for the project. Otherwise, this method will use the policy configured
|
|
* for the tenant. If a password policy has not been configured, then the default policy
|
|
* configured for all projects will be used.
|
|
*
|
|
* If an auth flow fails because a submitted password does not meet the password policy
|
|
* requirements and this method has previously been called, then this method will use the
|
|
* most recent policy available when called again.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* validatePassword(auth, 'some-password');
|
|
* ```
|
|
*
|
|
* @param auth The {@link Auth} instance.
|
|
* @param password The password to validate.
|
|
*
|
|
* @public
|
|
*/
|
|
async function validatePassword(auth, password) {
|
|
const authInternal = _castAuth(auth);
|
|
return authInternal.validatePassword(password);
|
|
}
|
|
/**
|
|
* Adds an observer for changes to the signed-in user's ID token.
|
|
*
|
|
* @remarks
|
|
* This includes sign-in, sign-out, and token refresh events.
|
|
* This will not be triggered automatically upon ID token expiration. Use {@link User.getIdToken} to refresh the ID token.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param nextOrObserver - callback triggered on change.
|
|
* @param error - Deprecated. This callback is never triggered. Errors
|
|
* on signing in/out can be caught in promises returned from
|
|
* sign-in/sign-out functions.
|
|
* @param completed - Deprecated. This callback is never triggered.
|
|
*
|
|
* @public
|
|
*/
|
|
function onIdTokenChanged(auth, nextOrObserver, error, completed) {
|
|
return util.getModularInstance(auth).onIdTokenChanged(nextOrObserver, error, completed);
|
|
}
|
|
/**
|
|
* Adds a blocking callback that runs before an auth state change
|
|
* sets a new user.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param callback - callback triggered before new user value is set.
|
|
* If this throws, it blocks the user from being set.
|
|
* @param onAbort - callback triggered if a later `beforeAuthStateChanged()`
|
|
* callback throws, allowing you to undo any side effects.
|
|
*/
|
|
function beforeAuthStateChanged(auth, callback, onAbort) {
|
|
return util.getModularInstance(auth).beforeAuthStateChanged(callback, onAbort);
|
|
}
|
|
/**
|
|
* Adds an observer for changes to the user's sign-in state.
|
|
*
|
|
* @remarks
|
|
* To keep the old behavior, see {@link onIdTokenChanged}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param nextOrObserver - callback triggered on change.
|
|
* @param error - Deprecated. This callback is never triggered. Errors
|
|
* on signing in/out can be caught in promises returned from
|
|
* sign-in/sign-out functions.
|
|
* @param completed - Deprecated. This callback is never triggered.
|
|
*
|
|
* @public
|
|
*/
|
|
function onAuthStateChanged(auth, nextOrObserver, error, completed) {
|
|
return util.getModularInstance(auth).onAuthStateChanged(nextOrObserver, error, completed);
|
|
}
|
|
/**
|
|
* Sets the current language to the default device/browser preference.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
*
|
|
* @public
|
|
*/
|
|
function useDeviceLanguage(auth) {
|
|
util.getModularInstance(auth).useDeviceLanguage();
|
|
}
|
|
/**
|
|
* Asynchronously sets the provided user as {@link Auth.currentUser} on the
|
|
* {@link Auth} instance.
|
|
*
|
|
* @remarks
|
|
* A new instance copy of the user provided will be made and set as currentUser.
|
|
*
|
|
* This will trigger {@link onAuthStateChanged} and {@link onIdTokenChanged} listeners
|
|
* like other sign in methods.
|
|
*
|
|
* The operation fails with an error if the user to be updated belongs to a different Firebase
|
|
* project.
|
|
*
|
|
* This method is not supported by {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param user - The new {@link User}.
|
|
*
|
|
* @public
|
|
*/
|
|
function updateCurrentUser(auth, user) {
|
|
return util.getModularInstance(auth).updateCurrentUser(user);
|
|
}
|
|
/**
|
|
* Signs out the current user.
|
|
*
|
|
* @remarks
|
|
* This method is not supported by {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
*
|
|
* @public
|
|
*/
|
|
function signOut(auth) {
|
|
return util.getModularInstance(auth).signOut();
|
|
}
|
|
/**
|
|
* Revokes the given access token. Currently only supports Apple OAuth access tokens.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param token - The Apple OAuth access token.
|
|
*
|
|
* @public
|
|
*/
|
|
function revokeAccessToken(auth, token) {
|
|
const authInternal = _castAuth(auth);
|
|
return authInternal.revokeAccessToken(token);
|
|
}
|
|
/**
|
|
* Deletes and signs out the user.
|
|
*
|
|
* @remarks
|
|
* Important: this is a security-sensitive operation that requires the user to have recently
|
|
* signed in. If this requirement isn't met, ask the user to authenticate again and then call
|
|
* {@link reauthenticateWithCredential}.
|
|
*
|
|
* @param user - The user.
|
|
*
|
|
* @public
|
|
*/
|
|
async function deleteUser(user) {
|
|
return util.getModularInstance(user).delete();
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class MultiFactorSessionImpl {
|
|
constructor(type, credential, user) {
|
|
this.type = type;
|
|
this.credential = credential;
|
|
this.user = user;
|
|
}
|
|
static _fromIdtoken(idToken, user) {
|
|
return new MultiFactorSessionImpl("enroll" /* MultiFactorSessionType.ENROLL */, idToken, user);
|
|
}
|
|
static _fromMfaPendingCredential(mfaPendingCredential) {
|
|
return new MultiFactorSessionImpl("signin" /* MultiFactorSessionType.SIGN_IN */, mfaPendingCredential);
|
|
}
|
|
toJSON() {
|
|
const key = this.type === "enroll" /* MultiFactorSessionType.ENROLL */
|
|
? 'idToken'
|
|
: 'pendingCredential';
|
|
return {
|
|
multiFactorSession: {
|
|
[key]: this.credential
|
|
}
|
|
};
|
|
}
|
|
static fromJSON(obj) {
|
|
var _a, _b;
|
|
if (obj === null || obj === void 0 ? void 0 : obj.multiFactorSession) {
|
|
if ((_a = obj.multiFactorSession) === null || _a === void 0 ? void 0 : _a.pendingCredential) {
|
|
return MultiFactorSessionImpl._fromMfaPendingCredential(obj.multiFactorSession.pendingCredential);
|
|
}
|
|
else if ((_b = obj.multiFactorSession) === null || _b === void 0 ? void 0 : _b.idToken) {
|
|
return MultiFactorSessionImpl._fromIdtoken(obj.multiFactorSession.idToken);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class MultiFactorResolverImpl {
|
|
constructor(session, hints, signInResolver) {
|
|
this.session = session;
|
|
this.hints = hints;
|
|
this.signInResolver = signInResolver;
|
|
}
|
|
/** @internal */
|
|
static _fromError(authExtern, error) {
|
|
const auth = _castAuth(authExtern);
|
|
const serverResponse = error.customData._serverResponse;
|
|
const hints = (serverResponse.mfaInfo || []).map(enrollment => MultiFactorInfoImpl._fromServerResponse(auth, enrollment));
|
|
_assert(serverResponse.mfaPendingCredential, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const session = MultiFactorSessionImpl._fromMfaPendingCredential(serverResponse.mfaPendingCredential);
|
|
return new MultiFactorResolverImpl(session, hints, async (assertion) => {
|
|
const mfaResponse = await assertion._process(auth, session);
|
|
// Clear out the unneeded fields from the old login response
|
|
delete serverResponse.mfaInfo;
|
|
delete serverResponse.mfaPendingCredential;
|
|
// Use in the new token & refresh token in the old response
|
|
const idTokenResponse = Object.assign(Object.assign({}, serverResponse), { idToken: mfaResponse.idToken, refreshToken: mfaResponse.refreshToken });
|
|
// TODO: we should collapse this switch statement into UserCredentialImpl._forOperation and have it support the SIGN_IN case
|
|
switch (error.operationType) {
|
|
case "signIn" /* OperationType.SIGN_IN */:
|
|
const userCredential = await UserCredentialImpl._fromIdTokenResponse(auth, error.operationType, idTokenResponse);
|
|
await auth._updateCurrentUser(userCredential.user);
|
|
return userCredential;
|
|
case "reauthenticate" /* OperationType.REAUTHENTICATE */:
|
|
_assert(error.user, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
return UserCredentialImpl._forOperation(error.user, error.operationType, idTokenResponse);
|
|
default:
|
|
_fail(auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
}
|
|
});
|
|
}
|
|
async resolveSignIn(assertionExtern) {
|
|
const assertion = assertionExtern;
|
|
return this.signInResolver(assertion);
|
|
}
|
|
}
|
|
/**
|
|
* Provides a {@link MultiFactorResolver} suitable for completion of a
|
|
* multi-factor flow.
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param error - The {@link MultiFactorError} raised during a sign-in, or
|
|
* reauthentication operation.
|
|
*
|
|
* @public
|
|
*/
|
|
function getMultiFactorResolver(auth, error) {
|
|
var _a;
|
|
const authModular = util.getModularInstance(auth);
|
|
const errorInternal = error;
|
|
_assert(error.customData.operationType, authModular, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
_assert((_a = errorInternal.customData._serverResponse) === null || _a === void 0 ? void 0 : _a.mfaPendingCredential, authModular, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
return MultiFactorResolverImpl._fromError(authModular, errorInternal);
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function startEnrollPhoneMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaEnrollment:start" /* Endpoint.START_MFA_ENROLLMENT */, _addTidIfNecessary(auth, request));
|
|
}
|
|
function finalizeEnrollPhoneMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaEnrollment:finalize" /* Endpoint.FINALIZE_MFA_ENROLLMENT */, _addTidIfNecessary(auth, request));
|
|
}
|
|
function startEnrollTotpMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaEnrollment:start" /* Endpoint.START_MFA_ENROLLMENT */, _addTidIfNecessary(auth, request));
|
|
}
|
|
function finalizeEnrollTotpMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaEnrollment:finalize" /* Endpoint.FINALIZE_MFA_ENROLLMENT */, _addTidIfNecessary(auth, request));
|
|
}
|
|
function withdrawMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaEnrollment:withdraw" /* Endpoint.WITHDRAW_MFA */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
class MultiFactorUserImpl {
|
|
constructor(user) {
|
|
this.user = user;
|
|
this.enrolledFactors = [];
|
|
user._onReload(userInfo => {
|
|
if (userInfo.mfaInfo) {
|
|
this.enrolledFactors = userInfo.mfaInfo.map(enrollment => MultiFactorInfoImpl._fromServerResponse(user.auth, enrollment));
|
|
}
|
|
});
|
|
}
|
|
static _fromUser(user) {
|
|
return new MultiFactorUserImpl(user);
|
|
}
|
|
async getSession() {
|
|
return MultiFactorSessionImpl._fromIdtoken(await this.user.getIdToken(), this.user);
|
|
}
|
|
async enroll(assertionExtern, displayName) {
|
|
const assertion = assertionExtern;
|
|
const session = (await this.getSession());
|
|
const finalizeMfaResponse = await _logoutIfInvalidated(this.user, assertion._process(this.user.auth, session, displayName));
|
|
// New tokens will be issued after enrollment of the new second factors.
|
|
// They need to be updated on the user.
|
|
await this.user._updateTokensIfNecessary(finalizeMfaResponse);
|
|
// The user needs to be reloaded to get the new multi-factor information
|
|
// from server. USER_RELOADED event will be triggered and `enrolledFactors`
|
|
// will be updated.
|
|
return this.user.reload();
|
|
}
|
|
async unenroll(infoOrUid) {
|
|
const mfaEnrollmentId = typeof infoOrUid === 'string' ? infoOrUid : infoOrUid.uid;
|
|
const idToken = await this.user.getIdToken();
|
|
try {
|
|
const idTokenResponse = await _logoutIfInvalidated(this.user, withdrawMfa(this.user.auth, {
|
|
idToken,
|
|
mfaEnrollmentId
|
|
}));
|
|
// Remove the second factor from the user's list.
|
|
this.enrolledFactors = this.enrolledFactors.filter(({ uid }) => uid !== mfaEnrollmentId);
|
|
// Depending on whether the backend decided to revoke the user's session,
|
|
// the tokenResponse may be empty. If the tokens were not updated (and they
|
|
// are now invalid), reloading the user will discover this and invalidate
|
|
// the user's state accordingly.
|
|
await this.user._updateTokensIfNecessary(idTokenResponse);
|
|
await this.user.reload();
|
|
}
|
|
catch (e) {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
const multiFactorUserCache = new WeakMap();
|
|
/**
|
|
* The {@link MultiFactorUser} corresponding to the user.
|
|
*
|
|
* @remarks
|
|
* This is used to access all multi-factor properties and operations related to the user.
|
|
*
|
|
* @param user - The user.
|
|
*
|
|
* @public
|
|
*/
|
|
function multiFactor(user) {
|
|
const userModular = util.getModularInstance(user);
|
|
if (!multiFactorUserCache.has(userModular)) {
|
|
multiFactorUserCache.set(userModular, MultiFactorUserImpl._fromUser(userModular));
|
|
}
|
|
return multiFactorUserCache.get(userModular);
|
|
}
|
|
|
|
var name = "@firebase/auth";
|
|
var version = "1.8.0";
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class AuthInterop {
|
|
constructor(auth) {
|
|
this.auth = auth;
|
|
this.internalListeners = new Map();
|
|
}
|
|
getUid() {
|
|
var _a;
|
|
this.assertAuthConfigured();
|
|
return ((_a = this.auth.currentUser) === null || _a === void 0 ? void 0 : _a.uid) || null;
|
|
}
|
|
async getToken(forceRefresh) {
|
|
this.assertAuthConfigured();
|
|
await this.auth._initializationPromise;
|
|
if (!this.auth.currentUser) {
|
|
return null;
|
|
}
|
|
const accessToken = await this.auth.currentUser.getIdToken(forceRefresh);
|
|
return { accessToken };
|
|
}
|
|
addAuthTokenListener(listener) {
|
|
this.assertAuthConfigured();
|
|
if (this.internalListeners.has(listener)) {
|
|
return;
|
|
}
|
|
const unsubscribe = this.auth.onIdTokenChanged(user => {
|
|
listener((user === null || user === void 0 ? void 0 : user.stsTokenManager.accessToken) || null);
|
|
});
|
|
this.internalListeners.set(listener, unsubscribe);
|
|
this.updateProactiveRefresh();
|
|
}
|
|
removeAuthTokenListener(listener) {
|
|
this.assertAuthConfigured();
|
|
const unsubscribe = this.internalListeners.get(listener);
|
|
if (!unsubscribe) {
|
|
return;
|
|
}
|
|
this.internalListeners.delete(listener);
|
|
unsubscribe();
|
|
this.updateProactiveRefresh();
|
|
}
|
|
assertAuthConfigured() {
|
|
_assert(this.auth._initializationPromise, "dependent-sdk-initialized-before-auth" /* AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH */);
|
|
}
|
|
updateProactiveRefresh() {
|
|
if (this.internalListeners.size > 0) {
|
|
this.auth._startProactiveRefresh();
|
|
}
|
|
else {
|
|
this.auth._stopProactiveRefresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function getVersionForPlatform(clientPlatform) {
|
|
switch (clientPlatform) {
|
|
case "Node" /* ClientPlatform.NODE */:
|
|
return 'node';
|
|
case "ReactNative" /* ClientPlatform.REACT_NATIVE */:
|
|
return 'rn';
|
|
case "Worker" /* ClientPlatform.WORKER */:
|
|
return 'webworker';
|
|
case "Cordova" /* ClientPlatform.CORDOVA */:
|
|
return 'cordova';
|
|
case "WebExtension" /* ClientPlatform.WEB_EXTENSION */:
|
|
return 'web-extension';
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
/** @internal */
|
|
function registerAuth(clientPlatform) {
|
|
app._registerComponent(new component.Component("auth" /* _ComponentName.AUTH */, (container, { options: deps }) => {
|
|
const app = container.getProvider('app').getImmediate();
|
|
const heartbeatServiceProvider = container.getProvider('heartbeat');
|
|
const appCheckServiceProvider = container.getProvider('app-check-internal');
|
|
const { apiKey, authDomain } = app.options;
|
|
_assert(apiKey && !apiKey.includes(':'), "invalid-api-key" /* AuthErrorCode.INVALID_API_KEY */, { appName: app.name });
|
|
const config = {
|
|
apiKey,
|
|
authDomain,
|
|
clientPlatform,
|
|
apiHost: "identitytoolkit.googleapis.com" /* DefaultConfig.API_HOST */,
|
|
tokenApiHost: "securetoken.googleapis.com" /* DefaultConfig.TOKEN_API_HOST */,
|
|
apiScheme: "https" /* DefaultConfig.API_SCHEME */,
|
|
sdkClientVersion: _getClientVersion(clientPlatform)
|
|
};
|
|
const authInstance = new AuthImpl(app, heartbeatServiceProvider, appCheckServiceProvider, config);
|
|
_initializeAuthInstance(authInstance, deps);
|
|
return authInstance;
|
|
}, "PUBLIC" /* ComponentType.PUBLIC */)
|
|
/**
|
|
* Auth can only be initialized by explicitly calling getAuth() or initializeAuth()
|
|
* For why we do this, See go/firebase-next-auth-init
|
|
*/
|
|
.setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */)
|
|
/**
|
|
* Because all firebase products that depend on auth depend on auth-internal directly,
|
|
* we need to initialize auth-internal after auth is initialized to make it available to other firebase products.
|
|
*/
|
|
.setInstanceCreatedCallback((container, _instanceIdentifier, _instance) => {
|
|
const authInternalProvider = container.getProvider("auth-internal" /* _ComponentName.AUTH_INTERNAL */);
|
|
authInternalProvider.initialize();
|
|
}));
|
|
app._registerComponent(new component.Component("auth-internal" /* _ComponentName.AUTH_INTERNAL */, container => {
|
|
const auth = _castAuth(container.getProvider("auth" /* _ComponentName.AUTH */).getImmediate());
|
|
return (auth => new AuthInterop(auth))(auth);
|
|
}, "PRIVATE" /* ComponentType.PRIVATE */).setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */));
|
|
app.registerVersion(name, version, getVersionForPlatform(clientPlatform));
|
|
// BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
|
|
app.registerVersion(name, version, 'cjs2017');
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2021 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* An enum of factors that may be used for multifactor authentication.
|
|
*
|
|
* @public
|
|
*/
|
|
const FactorId = {
|
|
/** Phone as second factor */
|
|
PHONE: 'phone',
|
|
TOTP: 'totp'
|
|
};
|
|
/**
|
|
* Enumeration of supported providers.
|
|
*
|
|
* @public
|
|
*/
|
|
const ProviderId = {
|
|
/** Facebook provider ID */
|
|
FACEBOOK: 'facebook.com',
|
|
/** GitHub provider ID */
|
|
GITHUB: 'github.com',
|
|
/** Google provider ID */
|
|
GOOGLE: 'google.com',
|
|
/** Password provider */
|
|
PASSWORD: 'password',
|
|
/** Phone provider */
|
|
PHONE: 'phone',
|
|
/** Twitter provider ID */
|
|
TWITTER: 'twitter.com'
|
|
};
|
|
/**
|
|
* Enumeration of supported sign-in methods.
|
|
*
|
|
* @public
|
|
*/
|
|
const SignInMethod = {
|
|
/** Email link sign in method */
|
|
EMAIL_LINK: 'emailLink',
|
|
/** Email/password sign in method */
|
|
EMAIL_PASSWORD: 'password',
|
|
/** Facebook sign in method */
|
|
FACEBOOK: 'facebook.com',
|
|
/** GitHub sign in method */
|
|
GITHUB: 'github.com',
|
|
/** Google sign in method */
|
|
GOOGLE: 'google.com',
|
|
/** Phone sign in method */
|
|
PHONE: 'phone',
|
|
/** Twitter sign in method */
|
|
TWITTER: 'twitter.com'
|
|
};
|
|
/**
|
|
* Enumeration of supported operation types.
|
|
*
|
|
* @public
|
|
*/
|
|
const OperationType = {
|
|
/** Operation involving linking an additional provider to an already signed-in user. */
|
|
LINK: 'link',
|
|
/** Operation involving using a provider to reauthenticate an already signed-in user. */
|
|
REAUTHENTICATE: 'reauthenticate',
|
|
/** Operation involving signing in a user. */
|
|
SIGN_IN: 'signIn'
|
|
};
|
|
/**
|
|
* An enumeration of the possible email action types.
|
|
*
|
|
* @public
|
|
*/
|
|
const ActionCodeOperation = {
|
|
/** The email link sign-in action. */
|
|
EMAIL_SIGNIN: 'EMAIL_SIGNIN',
|
|
/** The password reset action. */
|
|
PASSWORD_RESET: 'PASSWORD_RESET',
|
|
/** The email revocation action. */
|
|
RECOVER_EMAIL: 'RECOVER_EMAIL',
|
|
/** The revert second factor addition email action. */
|
|
REVERT_SECOND_FACTOR_ADDITION: 'REVERT_SECOND_FACTOR_ADDITION',
|
|
/** The revert second factor addition email action. */
|
|
VERIFY_AND_CHANGE_EMAIL: 'VERIFY_AND_CHANGE_EMAIL',
|
|
/** The email verification action. */
|
|
VERIFY_EMAIL: 'VERIFY_EMAIL'
|
|
};
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function startSignInPhoneMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaSignIn:start" /* Endpoint.START_MFA_SIGN_IN */, _addTidIfNecessary(auth, request));
|
|
}
|
|
function finalizeSignInPhoneMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaSignIn:finalize" /* Endpoint.FINALIZE_MFA_SIGN_IN */, _addTidIfNecessary(auth, request));
|
|
}
|
|
function finalizeSignInTotpMfa(auth, request) {
|
|
return _performApiRequest(auth, "POST" /* HttpMethod.POST */, "/v2/accounts/mfaSignIn:finalize" /* Endpoint.FINALIZE_MFA_SIGN_IN */, _addTidIfNecessary(auth, request));
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Lazy accessor for window, since the compat layer won't tree shake this out,
|
|
* we need to make sure not to mess with window unless we have to
|
|
*/
|
|
function _window() {
|
|
return window;
|
|
}
|
|
function _setWindowLocation(url) {
|
|
_window().location.href = url;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
function _isWorker() {
|
|
return (typeof _window()['WorkerGlobalScope'] !== 'undefined' &&
|
|
typeof _window()['importScripts'] === 'function');
|
|
}
|
|
async function _getActiveServiceWorker() {
|
|
if (!(navigator === null || navigator === void 0 ? void 0 : navigator.serviceWorker)) {
|
|
return null;
|
|
}
|
|
try {
|
|
const registration = await navigator.serviceWorker.ready;
|
|
return registration.active;
|
|
}
|
|
catch (_a) {
|
|
return null;
|
|
}
|
|
}
|
|
function _getServiceWorkerController() {
|
|
var _a;
|
|
return ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.serviceWorker) === null || _a === void 0 ? void 0 : _a.controller) || null;
|
|
}
|
|
function _getWorkerGlobalScope() {
|
|
return _isWorker() ? self : null;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
// ReCaptcha will load using the same callback, so the callback function needs
|
|
// to be kept around
|
|
const _JSLOAD_CALLBACK = _generateCallbackName('rcb');
|
|
const NETWORK_TIMEOUT_DELAY = new Delay(30000, 60000);
|
|
/**
|
|
* Loader for the GReCaptcha library. There should only ever be one of this.
|
|
*/
|
|
class ReCaptchaLoaderImpl {
|
|
constructor() {
|
|
var _a;
|
|
this.hostLanguage = '';
|
|
this.counter = 0;
|
|
/**
|
|
* Check for `render()` method. `window.grecaptcha` will exist if the Enterprise
|
|
* version of the ReCAPTCHA script was loaded by someone else (e.g. App Check) but
|
|
* `window.grecaptcha.render()` will not. Another load will add it.
|
|
*/
|
|
this.librarySeparatelyLoaded = !!((_a = _window().grecaptcha) === null || _a === void 0 ? void 0 : _a.render);
|
|
}
|
|
load(auth, hl = '') {
|
|
_assert(isHostLanguageValid(hl), auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
if (this.shouldResolveImmediately(hl) && isV2(_window().grecaptcha)) {
|
|
return Promise.resolve(_window().grecaptcha);
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const networkTimeout = _window().setTimeout(() => {
|
|
reject(_createError(auth, "network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */));
|
|
}, NETWORK_TIMEOUT_DELAY.get());
|
|
_window()[_JSLOAD_CALLBACK] = () => {
|
|
_window().clearTimeout(networkTimeout);
|
|
delete _window()[_JSLOAD_CALLBACK];
|
|
const recaptcha = _window().grecaptcha;
|
|
if (!recaptcha || !isV2(recaptcha)) {
|
|
reject(_createError(auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */));
|
|
return;
|
|
}
|
|
// Wrap the recaptcha render function so that we know if the developer has
|
|
// called it separately
|
|
const render = recaptcha.render;
|
|
recaptcha.render = (container, params) => {
|
|
const widgetId = render(container, params);
|
|
this.counter++;
|
|
return widgetId;
|
|
};
|
|
this.hostLanguage = hl;
|
|
resolve(recaptcha);
|
|
};
|
|
const url = `${_recaptchaV2ScriptUrl()}?${util.querystring({
|
|
onload: _JSLOAD_CALLBACK,
|
|
render: 'explicit',
|
|
hl
|
|
})}`;
|
|
_loadJS(url).catch(() => {
|
|
clearTimeout(networkTimeout);
|
|
reject(_createError(auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */));
|
|
});
|
|
});
|
|
}
|
|
clearedOneInstance() {
|
|
this.counter--;
|
|
}
|
|
shouldResolveImmediately(hl) {
|
|
var _a;
|
|
// We can resolve immediately if:
|
|
// • grecaptcha is already defined AND (
|
|
// 1. the requested language codes are the same OR
|
|
// 2. there exists already a ReCaptcha on the page
|
|
// 3. the library was already loaded by the app
|
|
// In cases (2) and (3), we _can't_ reload as it would break the recaptchas
|
|
// that are already in the page
|
|
return (!!((_a = _window().grecaptcha) === null || _a === void 0 ? void 0 : _a.render) &&
|
|
(hl === this.hostLanguage ||
|
|
this.counter > 0 ||
|
|
this.librarySeparatelyLoaded));
|
|
}
|
|
}
|
|
function isHostLanguageValid(hl) {
|
|
return hl.length <= 6 && /^\s*[a-zA-Z0-9\-]*\s*$/.test(hl);
|
|
}
|
|
class MockReCaptchaLoaderImpl {
|
|
async load(auth) {
|
|
return new MockReCaptcha(auth);
|
|
}
|
|
clearedOneInstance() { }
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
const RECAPTCHA_VERIFIER_TYPE = 'recaptcha';
|
|
const DEFAULT_PARAMS = {
|
|
theme: 'light',
|
|
type: 'image'
|
|
};
|
|
/**
|
|
* An {@link https://www.google.com/recaptcha/ | reCAPTCHA}-based application verifier.
|
|
*
|
|
* @remarks
|
|
* `RecaptchaVerifier` does not work in a Node.js environment.
|
|
*
|
|
* @public
|
|
*/
|
|
class RecaptchaVerifier {
|
|
/**
|
|
* @param authExtern - The corresponding Firebase {@link Auth} instance.
|
|
*
|
|
* @param containerOrId - The reCAPTCHA container parameter.
|
|
*
|
|
* @remarks
|
|
* This has different meaning depending on whether the reCAPTCHA is hidden or visible. For a
|
|
* visible reCAPTCHA the container must be empty. If a string is used, it has to correspond to
|
|
* an element ID. The corresponding element must also must be in the DOM at the time of
|
|
* initialization.
|
|
*
|
|
* @param parameters - The optional reCAPTCHA parameters.
|
|
*
|
|
* @remarks
|
|
* Check the reCAPTCHA docs for a comprehensive list. All parameters are accepted except for
|
|
* the sitekey. Firebase Auth backend provisions a reCAPTCHA for each project and will
|
|
* configure this upon rendering. For an invisible reCAPTCHA, a size key must have the value
|
|
* 'invisible'.
|
|
*/
|
|
constructor(authExtern, containerOrId, parameters = Object.assign({}, DEFAULT_PARAMS)) {
|
|
this.parameters = parameters;
|
|
/**
|
|
* The application verifier type.
|
|
*
|
|
* @remarks
|
|
* For a reCAPTCHA verifier, this is 'recaptcha'.
|
|
*/
|
|
this.type = RECAPTCHA_VERIFIER_TYPE;
|
|
this.destroyed = false;
|
|
this.widgetId = null;
|
|
this.tokenChangeListeners = new Set();
|
|
this.renderPromise = null;
|
|
this.recaptcha = null;
|
|
this.auth = _castAuth(authExtern);
|
|
this.isInvisible = this.parameters.size === 'invisible';
|
|
_assert(typeof document !== 'undefined', this.auth, "operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */);
|
|
const container = typeof containerOrId === 'string'
|
|
? document.getElementById(containerOrId)
|
|
: containerOrId;
|
|
_assert(container, this.auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
this.container = container;
|
|
this.parameters.callback = this.makeTokenCallback(this.parameters.callback);
|
|
this._recaptchaLoader = this.auth.settings.appVerificationDisabledForTesting
|
|
? new MockReCaptchaLoaderImpl()
|
|
: new ReCaptchaLoaderImpl();
|
|
this.validateStartingState();
|
|
// TODO: Figure out if sdk version is needed
|
|
}
|
|
/**
|
|
* Waits for the user to solve the reCAPTCHA and resolves with the reCAPTCHA token.
|
|
*
|
|
* @returns A Promise for the reCAPTCHA token.
|
|
*/
|
|
async verify() {
|
|
this.assertNotDestroyed();
|
|
const id = await this.render();
|
|
const recaptcha = this.getAssertedRecaptcha();
|
|
const response = recaptcha.getResponse(id);
|
|
if (response) {
|
|
return response;
|
|
}
|
|
return new Promise(resolve => {
|
|
const tokenChange = (token) => {
|
|
if (!token) {
|
|
return; // Ignore token expirations.
|
|
}
|
|
this.tokenChangeListeners.delete(tokenChange);
|
|
resolve(token);
|
|
};
|
|
this.tokenChangeListeners.add(tokenChange);
|
|
if (this.isInvisible) {
|
|
recaptcha.execute(id);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Renders the reCAPTCHA widget on the page.
|
|
*
|
|
* @returns A Promise that resolves with the reCAPTCHA widget ID.
|
|
*/
|
|
render() {
|
|
try {
|
|
this.assertNotDestroyed();
|
|
}
|
|
catch (e) {
|
|
// This method returns a promise. Since it's not async (we want to return the
|
|
// _same_ promise if rendering is still occurring), the API surface should
|
|
// reject with the error rather than just throw
|
|
return Promise.reject(e);
|
|
}
|
|
if (this.renderPromise) {
|
|
return this.renderPromise;
|
|
}
|
|
this.renderPromise = this.makeRenderPromise().catch(e => {
|
|
this.renderPromise = null;
|
|
throw e;
|
|
});
|
|
return this.renderPromise;
|
|
}
|
|
/** @internal */
|
|
_reset() {
|
|
this.assertNotDestroyed();
|
|
if (this.widgetId !== null) {
|
|
this.getAssertedRecaptcha().reset(this.widgetId);
|
|
}
|
|
}
|
|
/**
|
|
* Clears the reCAPTCHA widget from the page and destroys the instance.
|
|
*/
|
|
clear() {
|
|
this.assertNotDestroyed();
|
|
this.destroyed = true;
|
|
this._recaptchaLoader.clearedOneInstance();
|
|
if (!this.isInvisible) {
|
|
this.container.childNodes.forEach(node => {
|
|
this.container.removeChild(node);
|
|
});
|
|
}
|
|
}
|
|
validateStartingState() {
|
|
_assert(!this.parameters.sitekey, this.auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
_assert(this.isInvisible || !this.container.hasChildNodes(), this.auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
_assert(typeof document !== 'undefined', this.auth, "operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */);
|
|
}
|
|
makeTokenCallback(existing) {
|
|
return token => {
|
|
this.tokenChangeListeners.forEach(listener => listener(token));
|
|
if (typeof existing === 'function') {
|
|
existing(token);
|
|
}
|
|
else if (typeof existing === 'string') {
|
|
const globalFunc = _window()[existing];
|
|
if (typeof globalFunc === 'function') {
|
|
globalFunc(token);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
assertNotDestroyed() {
|
|
_assert(!this.destroyed, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
}
|
|
async makeRenderPromise() {
|
|
await this.init();
|
|
if (!this.widgetId) {
|
|
let container = this.container;
|
|
if (!this.isInvisible) {
|
|
const guaranteedEmpty = document.createElement('div');
|
|
container.appendChild(guaranteedEmpty);
|
|
container = guaranteedEmpty;
|
|
}
|
|
this.widgetId = this.getAssertedRecaptcha().render(container, this.parameters);
|
|
}
|
|
return this.widgetId;
|
|
}
|
|
async init() {
|
|
_assert(_isHttpOrHttps() && !_isWorker(), this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
await domReady();
|
|
this.recaptcha = await this._recaptchaLoader.load(this.auth, this.auth.languageCode || undefined);
|
|
const siteKey = await getRecaptchaParams(this.auth);
|
|
_assert(siteKey, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
this.parameters.sitekey = siteKey;
|
|
}
|
|
getAssertedRecaptcha() {
|
|
_assert(this.recaptcha, this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
return this.recaptcha;
|
|
}
|
|
}
|
|
function domReady() {
|
|
let resolver = null;
|
|
return new Promise(resolve => {
|
|
if (document.readyState === 'complete') {
|
|
resolve();
|
|
return;
|
|
}
|
|
// Document not ready, wait for load before resolving.
|
|
// Save resolver, so we can remove listener in case it was externally
|
|
// cancelled.
|
|
resolver = () => resolve();
|
|
window.addEventListener('load', resolver);
|
|
}).catch(e => {
|
|
if (resolver) {
|
|
window.removeEventListener('load', resolver);
|
|
}
|
|
throw e;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
class ConfirmationResultImpl {
|
|
constructor(verificationId, onConfirmation) {
|
|
this.verificationId = verificationId;
|
|
this.onConfirmation = onConfirmation;
|
|
}
|
|
confirm(verificationCode) {
|
|
const authCredential = PhoneAuthCredential._fromVerification(this.verificationId, verificationCode);
|
|
return this.onConfirmation(authCredential);
|
|
}
|
|
}
|
|
/**
|
|
* Asynchronously signs in using a phone number.
|
|
*
|
|
* @remarks
|
|
* This method sends a code via SMS to the given
|
|
* phone number, and returns a {@link ConfirmationResult}. After the user
|
|
* provides the code sent to their phone, call {@link ConfirmationResult.confirm}
|
|
* with the code to sign the user in.
|
|
*
|
|
* For abuse prevention, this method requires a {@link ApplicationVerifier}.
|
|
* This SDK includes an implementation based on reCAPTCHA v2, {@link RecaptchaVerifier}.
|
|
* This function can work on other platforms that do not support the
|
|
* {@link RecaptchaVerifier} (like React Native), but you need to use a
|
|
* third-party {@link ApplicationVerifier} implementation.
|
|
*
|
|
* If you've enabled project-level reCAPTCHA Enterprise bot protection in
|
|
* Enforce mode, you can omit the {@link ApplicationVerifier}.
|
|
*
|
|
* This method does not work in a Node.js environment or with {@link Auth} instances created with a
|
|
* {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // 'recaptcha-container' is the ID of an element in the DOM.
|
|
* const applicationVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
|
|
* const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier);
|
|
* // Obtain a verificationCode from the user.
|
|
* const credential = await confirmationResult.confirm(verificationCode);
|
|
* ```
|
|
*
|
|
* @param auth - The {@link Auth} instance.
|
|
* @param phoneNumber - The user's phone number in E.164 format (e.g. +16505550101).
|
|
* @param appVerifier - The {@link ApplicationVerifier}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function signInWithPhoneNumber(auth, phoneNumber, appVerifier) {
|
|
if (app._isFirebaseServerApp(auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(auth));
|
|
}
|
|
const authInternal = _castAuth(auth);
|
|
const verificationId = await _verifyPhoneNumber(authInternal, phoneNumber, util.getModularInstance(appVerifier));
|
|
return new ConfirmationResultImpl(verificationId, cred => signInWithCredential(authInternal, cred));
|
|
}
|
|
/**
|
|
* Links the user account with the given phone number.
|
|
*
|
|
* @remarks
|
|
* This method does not work in a Node.js environment.
|
|
*
|
|
* @param user - The user.
|
|
* @param phoneNumber - The user's phone number in E.164 format (e.g. +16505550101).
|
|
* @param appVerifier - The {@link ApplicationVerifier}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function linkWithPhoneNumber(user, phoneNumber, appVerifier) {
|
|
const userInternal = util.getModularInstance(user);
|
|
await _assertLinkedStatus(false, userInternal, "phone" /* ProviderId.PHONE */);
|
|
const verificationId = await _verifyPhoneNumber(userInternal.auth, phoneNumber, util.getModularInstance(appVerifier));
|
|
return new ConfirmationResultImpl(verificationId, cred => linkWithCredential(userInternal, cred));
|
|
}
|
|
/**
|
|
* Re-authenticates a user using a fresh phone credential.
|
|
*
|
|
* @remarks
|
|
* Use before operations such as {@link updatePassword} that require tokens from recent sign-in attempts.
|
|
*
|
|
* This method does not work in a Node.js environment or on any {@link User} signed in by
|
|
* {@link Auth} instances created with a {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @param user - The user.
|
|
* @param phoneNumber - The user's phone number in E.164 format (e.g. +16505550101).
|
|
* @param appVerifier - The {@link ApplicationVerifier}.
|
|
*
|
|
* @public
|
|
*/
|
|
async function reauthenticateWithPhoneNumber(user, phoneNumber, appVerifier) {
|
|
const userInternal = util.getModularInstance(user);
|
|
if (app._isFirebaseServerApp(userInternal.auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(userInternal.auth));
|
|
}
|
|
const verificationId = await _verifyPhoneNumber(userInternal.auth, phoneNumber, util.getModularInstance(appVerifier));
|
|
return new ConfirmationResultImpl(verificationId, cred => reauthenticateWithCredential(userInternal, cred));
|
|
}
|
|
/**
|
|
* Returns a verification ID to be used in conjunction with the SMS code that is sent.
|
|
*
|
|
*/
|
|
async function _verifyPhoneNumber(auth, options, verifier) {
|
|
var _a;
|
|
if (!auth._getRecaptchaConfig()) {
|
|
try {
|
|
await _initializeRecaptchaConfig(auth);
|
|
}
|
|
catch (error) {
|
|
// If an error occurs while fetching the config, there is no way to know the enablement state
|
|
// of Phone provider, so we proceed with recaptcha V2 verification.
|
|
// The error is likely "recaptchaKey undefined", as reCAPTCHA Enterprise is not
|
|
// enabled for any provider.
|
|
console.log('Failed to initialize reCAPTCHA Enterprise config. Triggering the reCAPTCHA v2 verification.');
|
|
}
|
|
}
|
|
try {
|
|
let phoneInfoOptions;
|
|
if (typeof options === 'string') {
|
|
phoneInfoOptions = {
|
|
phoneNumber: options
|
|
};
|
|
}
|
|
else {
|
|
phoneInfoOptions = options;
|
|
}
|
|
if ('session' in phoneInfoOptions) {
|
|
const session = phoneInfoOptions.session;
|
|
if ('phoneNumber' in phoneInfoOptions) {
|
|
_assert(session.type === "enroll" /* MultiFactorSessionType.ENROLL */, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const startPhoneMfaEnrollmentRequest = {
|
|
idToken: session.credential,
|
|
phoneEnrollmentInfo: {
|
|
phoneNumber: phoneInfoOptions.phoneNumber,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
}
|
|
};
|
|
const startEnrollPhoneMfaActionCallback = async (authInstance, request) => {
|
|
// If reCAPTCHA Enterprise token is FAKE_TOKEN, fetch reCAPTCHA v2 token and inject into request.
|
|
if (request.phoneEnrollmentInfo.captchaResponse === FAKE_TOKEN) {
|
|
_assert((verifier === null || verifier === void 0 ? void 0 : verifier.type) === RECAPTCHA_VERIFIER_TYPE, authInstance, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
const requestWithRecaptchaV2 = await injectRecaptchaV2Token(authInstance, request, verifier);
|
|
return startEnrollPhoneMfa(authInstance, requestWithRecaptchaV2);
|
|
}
|
|
return startEnrollPhoneMfa(authInstance, request);
|
|
};
|
|
const startPhoneMfaEnrollmentResponse = handleRecaptchaFlow(auth, startPhoneMfaEnrollmentRequest, "mfaSmsEnrollment" /* RecaptchaActionName.MFA_SMS_ENROLLMENT */, startEnrollPhoneMfaActionCallback, "PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */);
|
|
const response = await startPhoneMfaEnrollmentResponse.catch(error => {
|
|
return Promise.reject(error);
|
|
});
|
|
return response.phoneSessionInfo.sessionInfo;
|
|
}
|
|
else {
|
|
_assert(session.type === "signin" /* MultiFactorSessionType.SIGN_IN */, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const mfaEnrollmentId = ((_a = phoneInfoOptions.multiFactorHint) === null || _a === void 0 ? void 0 : _a.uid) ||
|
|
phoneInfoOptions.multiFactorUid;
|
|
_assert(mfaEnrollmentId, auth, "missing-multi-factor-info" /* AuthErrorCode.MISSING_MFA_INFO */);
|
|
const startPhoneMfaSignInRequest = {
|
|
mfaPendingCredential: session.credential,
|
|
mfaEnrollmentId,
|
|
phoneSignInInfo: {
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
}
|
|
};
|
|
const startSignInPhoneMfaActionCallback = async (authInstance, request) => {
|
|
// If reCAPTCHA Enterprise token is FAKE_TOKEN, fetch reCAPTCHA v2 token and inject into request.
|
|
if (request.phoneSignInInfo.captchaResponse === FAKE_TOKEN) {
|
|
_assert((verifier === null || verifier === void 0 ? void 0 : verifier.type) === RECAPTCHA_VERIFIER_TYPE, authInstance, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
const requestWithRecaptchaV2 = await injectRecaptchaV2Token(authInstance, request, verifier);
|
|
return startSignInPhoneMfa(authInstance, requestWithRecaptchaV2);
|
|
}
|
|
return startSignInPhoneMfa(authInstance, request);
|
|
};
|
|
const startPhoneMfaSignInResponse = handleRecaptchaFlow(auth, startPhoneMfaSignInRequest, "mfaSmsSignIn" /* RecaptchaActionName.MFA_SMS_SIGNIN */, startSignInPhoneMfaActionCallback, "PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */);
|
|
const response = await startPhoneMfaSignInResponse.catch(error => {
|
|
return Promise.reject(error);
|
|
});
|
|
return response.phoneResponseInfo.sessionInfo;
|
|
}
|
|
}
|
|
else {
|
|
const sendPhoneVerificationCodeRequest = {
|
|
phoneNumber: phoneInfoOptions.phoneNumber,
|
|
clientType: "CLIENT_TYPE_WEB" /* RecaptchaClientType.WEB */
|
|
};
|
|
const sendPhoneVerificationCodeActionCallback = async (authInstance, request) => {
|
|
// If reCAPTCHA Enterprise token is FAKE_TOKEN, fetch reCAPTCHA v2 token and inject into request.
|
|
if (request.captchaResponse === FAKE_TOKEN) {
|
|
_assert((verifier === null || verifier === void 0 ? void 0 : verifier.type) === RECAPTCHA_VERIFIER_TYPE, authInstance, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
const requestWithRecaptchaV2 = await injectRecaptchaV2Token(authInstance, request, verifier);
|
|
return sendPhoneVerificationCode(authInstance, requestWithRecaptchaV2);
|
|
}
|
|
return sendPhoneVerificationCode(authInstance, request);
|
|
};
|
|
const sendPhoneVerificationCodeResponse = handleRecaptchaFlow(auth, sendPhoneVerificationCodeRequest, "sendVerificationCode" /* RecaptchaActionName.SEND_VERIFICATION_CODE */, sendPhoneVerificationCodeActionCallback, "PHONE_PROVIDER" /* RecaptchaAuthProvider.PHONE_PROVIDER */);
|
|
const response = await sendPhoneVerificationCodeResponse.catch(error => {
|
|
return Promise.reject(error);
|
|
});
|
|
return response.sessionInfo;
|
|
}
|
|
}
|
|
finally {
|
|
verifier === null || verifier === void 0 ? void 0 : verifier._reset();
|
|
}
|
|
}
|
|
/**
|
|
* Updates the user's phone number.
|
|
*
|
|
* @remarks
|
|
* This method does not work in a Node.js environment or on any {@link User} signed in by
|
|
* {@link Auth} instances created with a {@link @firebase/app#FirebaseServerApp}.
|
|
*
|
|
* @example
|
|
* ```
|
|
* // 'recaptcha-container' is the ID of an element in the DOM.
|
|
* const applicationVerifier = new RecaptchaVerifier('recaptcha-container');
|
|
* const provider = new PhoneAuthProvider(auth);
|
|
* const verificationId = await provider.verifyPhoneNumber('+16505550101', applicationVerifier);
|
|
* // Obtain the verificationCode from the user.
|
|
* const phoneCredential = PhoneAuthProvider.credential(verificationId, verificationCode);
|
|
* await updatePhoneNumber(user, phoneCredential);
|
|
* ```
|
|
*
|
|
* @param user - The user.
|
|
* @param credential - A credential authenticating the new phone number.
|
|
*
|
|
* @public
|
|
*/
|
|
async function updatePhoneNumber(user, credential) {
|
|
const userInternal = util.getModularInstance(user);
|
|
if (app._isFirebaseServerApp(userInternal.auth.app)) {
|
|
return Promise.reject(_serverAppCurrentUserOperationNotSupportedError(userInternal.auth));
|
|
}
|
|
await _link(userInternal, credential);
|
|
}
|
|
// Helper function that fetches and injects a reCAPTCHA v2 token into the request.
|
|
async function injectRecaptchaV2Token(auth, request, recaptchaV2Verifier) {
|
|
_assert(recaptchaV2Verifier.type === RECAPTCHA_VERIFIER_TYPE, auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
const recaptchaV2Token = await recaptchaV2Verifier.verify();
|
|
_assert(typeof recaptchaV2Token === 'string', auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
const newRequest = Object.assign({}, request);
|
|
if ('phoneEnrollmentInfo' in newRequest) {
|
|
const phoneNumber = newRequest.phoneEnrollmentInfo.phoneNumber;
|
|
const captchaResponse = newRequest.phoneEnrollmentInfo.captchaResponse;
|
|
const clientType = newRequest
|
|
.phoneEnrollmentInfo.clientType;
|
|
const recaptchaVersion = newRequest.phoneEnrollmentInfo.recaptchaVersion;
|
|
Object.assign(newRequest, {
|
|
'phoneEnrollmentInfo': {
|
|
phoneNumber,
|
|
recaptchaToken: recaptchaV2Token,
|
|
captchaResponse,
|
|
clientType,
|
|
recaptchaVersion
|
|
}
|
|
});
|
|
return newRequest;
|
|
}
|
|
else if ('phoneSignInInfo' in newRequest) {
|
|
const captchaResponse = newRequest.phoneSignInInfo.captchaResponse;
|
|
const clientType = newRequest
|
|
.phoneSignInInfo.clientType;
|
|
const recaptchaVersion = newRequest.phoneSignInInfo.recaptchaVersion;
|
|
Object.assign(newRequest, {
|
|
'phoneSignInInfo': {
|
|
recaptchaToken: recaptchaV2Token,
|
|
captchaResponse,
|
|
clientType,
|
|
recaptchaVersion
|
|
}
|
|
});
|
|
return newRequest;
|
|
}
|
|
else {
|
|
Object.assign(newRequest, { 'recaptchaToken': recaptchaV2Token });
|
|
return newRequest;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* Provider for generating an {@link PhoneAuthCredential}.
|
|
*
|
|
* @remarks
|
|
* `PhoneAuthProvider` does not work in a Node.js environment.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* // 'recaptcha-container' is the ID of an element in the DOM.
|
|
* const applicationVerifier = new RecaptchaVerifier('recaptcha-container');
|
|
* const provider = new PhoneAuthProvider(auth);
|
|
* const verificationId = await provider.verifyPhoneNumber('+16505550101', applicationVerifier);
|
|
* // Obtain the verificationCode from the user.
|
|
* const phoneCredential = PhoneAuthProvider.credential(verificationId, verificationCode);
|
|
* const userCredential = await signInWithCredential(auth, phoneCredential);
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
class PhoneAuthProvider {
|
|
/**
|
|
* @param auth - The Firebase {@link Auth} instance in which sign-ins should occur.
|
|
*
|
|
*/
|
|
constructor(auth) {
|
|
/** Always set to {@link ProviderId}.PHONE. */
|
|
this.providerId = PhoneAuthProvider.PROVIDER_ID;
|
|
this.auth = _castAuth(auth);
|
|
}
|
|
/**
|
|
*
|
|
* Starts a phone number authentication flow by sending a verification code to the given phone
|
|
* number.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const provider = new PhoneAuthProvider(auth);
|
|
* const verificationId = await provider.verifyPhoneNumber(phoneNumber, applicationVerifier);
|
|
* // Obtain verificationCode from the user.
|
|
* const authCredential = PhoneAuthProvider.credential(verificationId, verificationCode);
|
|
* const userCredential = await signInWithCredential(auth, authCredential);
|
|
* ```
|
|
*
|
|
* @example
|
|
* An alternative flow is provided using the `signInWithPhoneNumber` method.
|
|
* ```javascript
|
|
* const confirmationResult = signInWithPhoneNumber(auth, phoneNumber, applicationVerifier);
|
|
* // Obtain verificationCode from the user.
|
|
* const userCredential = confirmationResult.confirm(verificationCode);
|
|
* ```
|
|
*
|
|
* @param phoneInfoOptions - The user's {@link PhoneInfoOptions}. The phone number should be in
|
|
* E.164 format (e.g. +16505550101).
|
|
* @param applicationVerifier - An {@link ApplicationVerifier}, which prevents
|
|
* requests from unauthorized clients. This SDK includes an implementation
|
|
* based on reCAPTCHA v2, {@link RecaptchaVerifier}. If you've enabled
|
|
* reCAPTCHA Enterprise bot protection in Enforce mode, this parameter is
|
|
* optional; in all other configurations, the parameter is required.
|
|
*
|
|
* @returns A Promise for a verification ID that can be passed to
|
|
* {@link PhoneAuthProvider.credential} to identify this flow.
|
|
*/
|
|
verifyPhoneNumber(phoneOptions, applicationVerifier) {
|
|
return _verifyPhoneNumber(this.auth, phoneOptions, util.getModularInstance(applicationVerifier));
|
|
}
|
|
/**
|
|
* Creates a phone auth credential, given the verification ID from
|
|
* {@link PhoneAuthProvider.verifyPhoneNumber} and the code that was sent to the user's
|
|
* mobile device.
|
|
*
|
|
* @example
|
|
* ```javascript
|
|
* const provider = new PhoneAuthProvider(auth);
|
|
* const verificationId = provider.verifyPhoneNumber(phoneNumber, applicationVerifier);
|
|
* // Obtain verificationCode from the user.
|
|
* const authCredential = PhoneAuthProvider.credential(verificationId, verificationCode);
|
|
* const userCredential = signInWithCredential(auth, authCredential);
|
|
* ```
|
|
*
|
|
* @example
|
|
* An alternative flow is provided using the `signInWithPhoneNumber` method.
|
|
* ```javascript
|
|
* const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier);
|
|
* // Obtain verificationCode from the user.
|
|
* const userCredential = await confirmationResult.confirm(verificationCode);
|
|
* ```
|
|
*
|
|
* @param verificationId - The verification ID returned from {@link PhoneAuthProvider.verifyPhoneNumber}.
|
|
* @param verificationCode - The verification code sent to the user's mobile device.
|
|
*
|
|
* @returns The auth provider credential.
|
|
*/
|
|
static credential(verificationId, verificationCode) {
|
|
return PhoneAuthCredential._fromVerification(verificationId, verificationCode);
|
|
}
|
|
/**
|
|
* Generates an {@link AuthCredential} from a {@link UserCredential}.
|
|
* @param userCredential - The user credential.
|
|
*/
|
|
static credentialFromResult(userCredential) {
|
|
const credential = userCredential;
|
|
return PhoneAuthProvider.credentialFromTaggedObject(credential);
|
|
}
|
|
/**
|
|
* Returns an {@link AuthCredential} when passed an error.
|
|
*
|
|
* @remarks
|
|
*
|
|
* This method works for errors like
|
|
* `auth/account-exists-with-different-credentials`. This is useful for
|
|
* recovering when attempting to set a user's phone number but the number
|
|
* in question is already tied to another account. For example, the following
|
|
* code tries to update the current user's phone number, and if that
|
|
* fails, links the user with the account associated with that number:
|
|
*
|
|
* ```js
|
|
* const provider = new PhoneAuthProvider(auth);
|
|
* const verificationId = await provider.verifyPhoneNumber(number, verifier);
|
|
* try {
|
|
* const code = ''; // Prompt the user for the verification code
|
|
* await updatePhoneNumber(
|
|
* auth.currentUser,
|
|
* PhoneAuthProvider.credential(verificationId, code));
|
|
* } catch (e) {
|
|
* if ((e as FirebaseError)?.code === 'auth/account-exists-with-different-credential') {
|
|
* const cred = PhoneAuthProvider.credentialFromError(e);
|
|
* await linkWithCredential(auth.currentUser, cred);
|
|
* }
|
|
* }
|
|
*
|
|
* // At this point, auth.currentUser.phoneNumber === number.
|
|
* ```
|
|
*
|
|
* @param error - The error to generate a credential from.
|
|
*/
|
|
static credentialFromError(error) {
|
|
return PhoneAuthProvider.credentialFromTaggedObject((error.customData || {}));
|
|
}
|
|
static credentialFromTaggedObject({ _tokenResponse: tokenResponse }) {
|
|
if (!tokenResponse) {
|
|
return null;
|
|
}
|
|
const { phoneNumber, temporaryProof } = tokenResponse;
|
|
if (phoneNumber && temporaryProof) {
|
|
return PhoneAuthCredential._fromTokenResponse(phoneNumber, temporaryProof);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
/** Always set to {@link ProviderId}.PHONE. */
|
|
PhoneAuthProvider.PROVIDER_ID = "phone" /* ProviderId.PHONE */;
|
|
/** Always set to {@link SignInMethod}.PHONE. */
|
|
PhoneAuthProvider.PHONE_SIGN_IN_METHOD = "phone" /* SignInMethod.PHONE */;
|
|
|
|
class MultiFactorAssertionImpl {
|
|
constructor(factorId) {
|
|
this.factorId = factorId;
|
|
}
|
|
_process(auth, session, displayName) {
|
|
switch (session.type) {
|
|
case "enroll" /* MultiFactorSessionType.ENROLL */:
|
|
return this._finalizeEnroll(auth, session.credential, displayName);
|
|
case "signin" /* MultiFactorSessionType.SIGN_IN */:
|
|
return this._finalizeSignIn(auth, session.credential);
|
|
default:
|
|
return debugFail('unexpected MultiFactorSessionType');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc PhoneMultiFactorAssertion}
|
|
*
|
|
* @public
|
|
*/
|
|
class PhoneMultiFactorAssertionImpl extends MultiFactorAssertionImpl {
|
|
constructor(credential) {
|
|
super("phone" /* FactorId.PHONE */);
|
|
this.credential = credential;
|
|
}
|
|
/** @internal */
|
|
static _fromCredential(credential) {
|
|
return new PhoneMultiFactorAssertionImpl(credential);
|
|
}
|
|
/** @internal */
|
|
_finalizeEnroll(auth, idToken, displayName) {
|
|
return finalizeEnrollPhoneMfa(auth, {
|
|
idToken,
|
|
displayName,
|
|
phoneVerificationInfo: this.credential._makeVerificationRequest()
|
|
});
|
|
}
|
|
/** @internal */
|
|
_finalizeSignIn(auth, mfaPendingCredential) {
|
|
return finalizeSignInPhoneMfa(auth, {
|
|
mfaPendingCredential,
|
|
phoneVerificationInfo: this.credential._makeVerificationRequest()
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Provider for generating a {@link PhoneMultiFactorAssertion}.
|
|
*
|
|
* @public
|
|
*/
|
|
class PhoneMultiFactorGenerator {
|
|
constructor() { }
|
|
/**
|
|
* Provides a {@link PhoneMultiFactorAssertion} to confirm ownership of the phone second factor.
|
|
*
|
|
* @remarks
|
|
* This method does not work in a Node.js environment.
|
|
*
|
|
* @param phoneAuthCredential - A credential provided by {@link PhoneAuthProvider.credential}.
|
|
* @returns A {@link PhoneMultiFactorAssertion} which can be used with
|
|
* {@link MultiFactorResolver.resolveSignIn}
|
|
*/
|
|
static assertion(credential) {
|
|
return PhoneMultiFactorAssertionImpl._fromCredential(credential);
|
|
}
|
|
}
|
|
/**
|
|
* The identifier of the phone second factor: `phone`.
|
|
*/
|
|
PhoneMultiFactorGenerator.FACTOR_ID = 'phone';
|
|
|
|
/**
|
|
* Provider for generating a {@link TotpMultiFactorAssertion}.
|
|
*
|
|
* @public
|
|
*/
|
|
class TotpMultiFactorGenerator {
|
|
/**
|
|
* Provides a {@link TotpMultiFactorAssertion} to confirm ownership of
|
|
* the TOTP (time-based one-time password) second factor.
|
|
* This assertion is used to complete enrollment in TOTP second factor.
|
|
*
|
|
* @param secret A {@link TotpSecret} containing the shared secret key and other TOTP parameters.
|
|
* @param oneTimePassword One-time password from TOTP App.
|
|
* @returns A {@link TotpMultiFactorAssertion} which can be used with
|
|
* {@link MultiFactorUser.enroll}.
|
|
*/
|
|
static assertionForEnrollment(secret, oneTimePassword) {
|
|
return TotpMultiFactorAssertionImpl._fromSecret(secret, oneTimePassword);
|
|
}
|
|
/**
|
|
* Provides a {@link TotpMultiFactorAssertion} to confirm ownership of the TOTP second factor.
|
|
* This assertion is used to complete signIn with TOTP as the second factor.
|
|
*
|
|
* @param enrollmentId identifies the enrolled TOTP second factor.
|
|
* @param oneTimePassword One-time password from TOTP App.
|
|
* @returns A {@link TotpMultiFactorAssertion} which can be used with
|
|
* {@link MultiFactorResolver.resolveSignIn}.
|
|
*/
|
|
static assertionForSignIn(enrollmentId, oneTimePassword) {
|
|
return TotpMultiFactorAssertionImpl._fromEnrollmentId(enrollmentId, oneTimePassword);
|
|
}
|
|
/**
|
|
* Returns a promise to {@link TotpSecret} which contains the TOTP shared secret key and other parameters.
|
|
* Creates a TOTP secret as part of enrolling a TOTP second factor.
|
|
* Used for generating a QR code URL or inputting into a TOTP app.
|
|
* This method uses the auth instance corresponding to the user in the multiFactorSession.
|
|
*
|
|
* @param session The {@link MultiFactorSession} that the user is part of.
|
|
* @returns A promise to {@link TotpSecret}.
|
|
*/
|
|
static async generateSecret(session) {
|
|
var _a;
|
|
const mfaSession = session;
|
|
_assert(typeof ((_a = mfaSession.user) === null || _a === void 0 ? void 0 : _a.auth) !== 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
const response = await startEnrollTotpMfa(mfaSession.user.auth, {
|
|
idToken: mfaSession.credential,
|
|
totpEnrollmentInfo: {}
|
|
});
|
|
return TotpSecret._fromStartTotpMfaEnrollmentResponse(response, mfaSession.user.auth);
|
|
}
|
|
}
|
|
/**
|
|
* The identifier of the TOTP second factor: `totp`.
|
|
*/
|
|
TotpMultiFactorGenerator.FACTOR_ID = "totp" /* FactorId.TOTP */;
|
|
class TotpMultiFactorAssertionImpl extends MultiFactorAssertionImpl {
|
|
constructor(otp, enrollmentId, secret) {
|
|
super("totp" /* FactorId.TOTP */);
|
|
this.otp = otp;
|
|
this.enrollmentId = enrollmentId;
|
|
this.secret = secret;
|
|
}
|
|
/** @internal */
|
|
static _fromSecret(secret, otp) {
|
|
return new TotpMultiFactorAssertionImpl(otp, undefined, secret);
|
|
}
|
|
/** @internal */
|
|
static _fromEnrollmentId(enrollmentId, otp) {
|
|
return new TotpMultiFactorAssertionImpl(otp, enrollmentId);
|
|
}
|
|
/** @internal */
|
|
async _finalizeEnroll(auth, idToken, displayName) {
|
|
_assert(typeof this.secret !== 'undefined', auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
return finalizeEnrollTotpMfa(auth, {
|
|
idToken,
|
|
displayName,
|
|
totpVerificationInfo: this.secret._makeTotpVerificationInfo(this.otp)
|
|
});
|
|
}
|
|
/** @internal */
|
|
async _finalizeSignIn(auth, mfaPendingCredential) {
|
|
_assert(this.enrollmentId !== undefined && this.otp !== undefined, auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
|
|
const totpVerificationInfo = { verificationCode: this.otp };
|
|
return finalizeSignInTotpMfa(auth, {
|
|
mfaPendingCredential,
|
|
mfaEnrollmentId: this.enrollmentId,
|
|
totpVerificationInfo
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Provider for generating a {@link TotpMultiFactorAssertion}.
|
|
*
|
|
* Stores the shared secret key and other parameters to generate time-based OTPs.
|
|
* Implements methods to retrieve the shared secret key and generate a QR code URL.
|
|
* @public
|
|
*/
|
|
class TotpSecret {
|
|
// The public members are declared outside the constructor so the docs can be generated.
|
|
constructor(secretKey, hashingAlgorithm, codeLength, codeIntervalSeconds, enrollmentCompletionDeadline, sessionInfo, auth) {
|
|
this.sessionInfo = sessionInfo;
|
|
this.auth = auth;
|
|
this.secretKey = secretKey;
|
|
this.hashingAlgorithm = hashingAlgorithm;
|
|
this.codeLength = codeLength;
|
|
this.codeIntervalSeconds = codeIntervalSeconds;
|
|
this.enrollmentCompletionDeadline = enrollmentCompletionDeadline;
|
|
}
|
|
/** @internal */
|
|
static _fromStartTotpMfaEnrollmentResponse(response, auth) {
|
|
return new TotpSecret(response.totpSessionInfo.sharedSecretKey, response.totpSessionInfo.hashingAlgorithm, response.totpSessionInfo.verificationCodeLength, response.totpSessionInfo.periodSec, new Date(response.totpSessionInfo.finalizeEnrollmentTime).toUTCString(), response.totpSessionInfo.sessionInfo, auth);
|
|
}
|
|
/** @internal */
|
|
_makeTotpVerificationInfo(otp) {
|
|
return { sessionInfo: this.sessionInfo, verificationCode: otp };
|
|
}
|
|
/**
|
|
* Returns a QR code URL as described in
|
|
* https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
|
* This can be displayed to the user as a QR code to be scanned into a TOTP app like Google Authenticator.
|
|
* If the optional parameters are unspecified, an accountName of <userEmail> and issuer of <firebaseAppName> are used.
|
|
*
|
|
* @param accountName the name of the account/app along with a user identifier.
|
|
* @param issuer issuer of the TOTP (likely the app name).
|
|
* @returns A QR code URL string.
|
|
*/
|
|
generateQrCodeUrl(accountName, issuer) {
|
|
var _a;
|
|
let useDefaults = false;
|
|
if (_isEmptyString(accountName) || _isEmptyString(issuer)) {
|
|
useDefaults = true;
|
|
}
|
|
if (useDefaults) {
|
|
if (_isEmptyString(accountName)) {
|
|
accountName = ((_a = this.auth.currentUser) === null || _a === void 0 ? void 0 : _a.email) || 'unknownuser';
|
|
}
|
|
if (_isEmptyString(issuer)) {
|
|
issuer = this.auth.name;
|
|
}
|
|
}
|
|
return `otpauth://totp/${issuer}:${accountName}?secret=${this.secretKey}&issuer=${issuer}&algorithm=${this.hashingAlgorithm}&digits=${this.codeLength}`;
|
|
}
|
|
}
|
|
/** @internal */
|
|
function _isEmptyString(input) {
|
|
return typeof input === 'undefined' || (input === null || input === void 0 ? void 0 : input.length) === 0;
|
|
}
|
|
|
|
const STORAGE_AVAILABLE_KEY = '__sak';
|
|
|
|
exports.AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY;
|
|
exports.ActionCodeOperation = ActionCodeOperation;
|
|
exports.ActionCodeURL = ActionCodeURL;
|
|
exports.AuthCredential = AuthCredential;
|
|
exports.AuthImpl = AuthImpl;
|
|
exports.BaseOAuthProvider = BaseOAuthProvider;
|
|
exports.Delay = Delay;
|
|
exports.EmailAuthCredential = EmailAuthCredential;
|
|
exports.EmailAuthProvider = EmailAuthProvider;
|
|
exports.FacebookAuthProvider = FacebookAuthProvider;
|
|
exports.FactorId = FactorId;
|
|
exports.FederatedAuthProvider = FederatedAuthProvider;
|
|
exports.FetchProvider = FetchProvider;
|
|
exports.GithubAuthProvider = GithubAuthProvider;
|
|
exports.GoogleAuthProvider = GoogleAuthProvider;
|
|
exports.OAuthCredential = OAuthCredential;
|
|
exports.OAuthProvider = OAuthProvider;
|
|
exports.OperationType = OperationType;
|
|
exports.PhoneAuthCredential = PhoneAuthCredential;
|
|
exports.PhoneAuthProvider = PhoneAuthProvider;
|
|
exports.PhoneMultiFactorGenerator = PhoneMultiFactorGenerator;
|
|
exports.ProviderId = ProviderId;
|
|
exports.RecaptchaVerifier = RecaptchaVerifier;
|
|
exports.SAMLAuthCredential = SAMLAuthCredential;
|
|
exports.SAMLAuthProvider = SAMLAuthProvider;
|
|
exports.STORAGE_AVAILABLE_KEY = STORAGE_AVAILABLE_KEY;
|
|
exports.SignInMethod = SignInMethod;
|
|
exports.TotpMultiFactorGenerator = TotpMultiFactorGenerator;
|
|
exports.TotpSecret = TotpSecret;
|
|
exports.TwitterAuthProvider = TwitterAuthProvider;
|
|
exports.UserImpl = UserImpl;
|
|
exports._assert = _assert;
|
|
exports._assertInstanceOf = _assertInstanceOf;
|
|
exports._assertLinkedStatus = _assertLinkedStatus;
|
|
exports._castAuth = _castAuth;
|
|
exports._createError = _createError;
|
|
exports._emulatorUrl = _emulatorUrl;
|
|
exports._fail = _fail;
|
|
exports._gapiScriptUrl = _gapiScriptUrl;
|
|
exports._generateCallbackName = _generateCallbackName;
|
|
exports._getActiveServiceWorker = _getActiveServiceWorker;
|
|
exports._getClientVersion = _getClientVersion;
|
|
exports._getCurrentUrl = _getCurrentUrl;
|
|
exports._getInstance = _getInstance;
|
|
exports._getServiceWorkerController = _getServiceWorkerController;
|
|
exports._getWorkerGlobalScope = _getWorkerGlobalScope;
|
|
exports._isAndroid = _isAndroid;
|
|
exports._isChromeIOS = _isChromeIOS;
|
|
exports._isFirefox = _isFirefox;
|
|
exports._isIE10 = _isIE10;
|
|
exports._isIOS = _isIOS;
|
|
exports._isIOS7Or8 = _isIOS7Or8;
|
|
exports._isIOSStandalone = _isIOSStandalone;
|
|
exports._isMobileBrowser = _isMobileBrowser;
|
|
exports._isSafari = _isSafari;
|
|
exports._isWorker = _isWorker;
|
|
exports._link = _link;
|
|
exports._loadJS = _loadJS;
|
|
exports._logWarn = _logWarn;
|
|
exports._performApiRequest = _performApiRequest;
|
|
exports._persistenceKeyName = _persistenceKeyName;
|
|
exports._reauthenticate = _reauthenticate;
|
|
exports._serverAppCurrentUserOperationNotSupportedError = _serverAppCurrentUserOperationNotSupportedError;
|
|
exports._setExternalJSProvider = _setExternalJSProvider;
|
|
exports._setWindowLocation = _setWindowLocation;
|
|
exports._signInWithCredential = _signInWithCredential;
|
|
exports._window = _window;
|
|
exports.applyActionCode = applyActionCode;
|
|
exports.beforeAuthStateChanged = beforeAuthStateChanged;
|
|
exports.checkActionCode = checkActionCode;
|
|
exports.confirmPasswordReset = confirmPasswordReset;
|
|
exports.connectAuthEmulator = connectAuthEmulator;
|
|
exports.createUserWithEmailAndPassword = createUserWithEmailAndPassword;
|
|
exports.debugAssert = debugAssert;
|
|
exports.debugErrorMap = debugErrorMap;
|
|
exports.deleteUser = deleteUser;
|
|
exports.fetchSignInMethodsForEmail = fetchSignInMethodsForEmail;
|
|
exports.getAdditionalUserInfo = getAdditionalUserInfo;
|
|
exports.getIdToken = getIdToken;
|
|
exports.getIdTokenResult = getIdTokenResult;
|
|
exports.getMultiFactorResolver = getMultiFactorResolver;
|
|
exports.inMemoryPersistence = inMemoryPersistence;
|
|
exports.initializeAuth = initializeAuth;
|
|
exports.initializeRecaptchaConfig = initializeRecaptchaConfig;
|
|
exports.isSignInWithEmailLink = isSignInWithEmailLink;
|
|
exports.linkWithCredential = linkWithCredential;
|
|
exports.linkWithPhoneNumber = linkWithPhoneNumber;
|
|
exports.multiFactor = multiFactor;
|
|
exports.onAuthStateChanged = onAuthStateChanged;
|
|
exports.onIdTokenChanged = onIdTokenChanged;
|
|
exports.parseActionCodeURL = parseActionCodeURL;
|
|
exports.prodErrorMap = prodErrorMap;
|
|
exports.reauthenticateWithCredential = reauthenticateWithCredential;
|
|
exports.reauthenticateWithPhoneNumber = reauthenticateWithPhoneNumber;
|
|
exports.registerAuth = registerAuth;
|
|
exports.reload = reload;
|
|
exports.revokeAccessToken = revokeAccessToken;
|
|
exports.sendEmailVerification = sendEmailVerification;
|
|
exports.sendPasswordResetEmail = sendPasswordResetEmail;
|
|
exports.sendSignInLinkToEmail = sendSignInLinkToEmail;
|
|
exports.setPersistence = setPersistence;
|
|
exports.signInAnonymously = signInAnonymously;
|
|
exports.signInWithCredential = signInWithCredential;
|
|
exports.signInWithCustomToken = signInWithCustomToken;
|
|
exports.signInWithEmailAndPassword = signInWithEmailAndPassword;
|
|
exports.signInWithEmailLink = signInWithEmailLink;
|
|
exports.signInWithIdp = signInWithIdp;
|
|
exports.signInWithPhoneNumber = signInWithPhoneNumber;
|
|
exports.signOut = signOut;
|
|
exports.unlink = unlink;
|
|
exports.updateCurrentUser = updateCurrentUser;
|
|
exports.updateEmail = updateEmail;
|
|
exports.updatePassword = updatePassword;
|
|
exports.updatePhoneNumber = updatePhoneNumber;
|
|
exports.updateProfile = updateProfile;
|
|
exports.useDeviceLanguage = useDeviceLanguage;
|
|
exports.validatePassword = validatePassword;
|
|
exports.verifyBeforeUpdateEmail = verifyBeforeUpdateEmail;
|
|
exports.verifyPasswordResetCode = verifyPasswordResetCode;
|
|
//# sourceMappingURL=index-4d73adbf.js.map
|