UNPKG

voluptasmollitia

Version:
1,456 lines (1,289 loc) 55.1 kB
/** * @license * Copyright 2017 Google Inc. * * 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. */ /** * @fileoverview Defines Auth credentials used for signInWithCredential. */ goog.provide('fireauth.AuthCredential'); goog.provide('fireauth.AuthProvider'); goog.provide('fireauth.EmailAuthCredential'); goog.provide('fireauth.EmailAuthProvider'); goog.provide('fireauth.FacebookAuthProvider'); goog.provide('fireauth.FederatedProvider'); goog.provide('fireauth.GithubAuthProvider'); goog.provide('fireauth.GoogleAuthProvider'); goog.provide('fireauth.OAuthCredential'); goog.provide('fireauth.OAuthProvider'); goog.provide('fireauth.OAuthResponse'); goog.provide('fireauth.PhoneAuthCredential'); goog.provide('fireauth.PhoneAuthProvider'); goog.provide('fireauth.SAMLAuthCredential'); goog.provide('fireauth.SAMLAuthProvider'); goog.provide('fireauth.TwitterAuthProvider'); goog.requireType('fireauth.RpcHandler'); goog.require('fireauth.ActionCodeInfo'); goog.require('fireauth.ActionCodeURL'); goog.require('fireauth.AuthError'); goog.require('fireauth.DynamicLink'); goog.require('fireauth.IdToken'); goog.require('fireauth.MultiFactorAuthCredential'); goog.require('fireauth.MultiFactorEnrollmentRequestIdentifier'); goog.require('fireauth.MultiFactorSession'); goog.require('fireauth.MultiFactorSignInRequestIdentifier'); goog.require('fireauth.authenum.Error'); goog.require('fireauth.constants'); goog.require('fireauth.idp'); goog.require('fireauth.object'); goog.require('fireauth.util'); goog.require('goog.Promise'); goog.require('goog.Uri'); goog.require('goog.array'); goog.require('goog.object'); /** * The interface that represents Auth credential. It provides the underlying * implementation for retrieving the ID token depending on the type of * credential. * @interface */ fireauth.AuthCredential = function() {}; /** * Returns a promise to retrieve ID token using the underlying RPC handler API * for the current credential. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @return {!goog.Promise<!Object, !fireauth.AuthError>} * idTokenPromise The RPC handler method that returns a promise which * resolves with an ID token. */ fireauth.AuthCredential.prototype.getIdTokenProvider = function(rpcHandler) {}; /** * Links the credential to an existing account, identified by an ID token. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} idToken The ID token of the existing account. * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts * are linked. */ fireauth.AuthCredential.prototype.linkToIdToken = function(rpcHandler, idToken) {}; /** * Tries to match the credential's idToken with the provided UID. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} uid The UID of the user to reauthenticate. * @return {!goog.Promise<!Object>} A Promise that resolves when * idToken UID match succeeds and returns the server response. */ fireauth.AuthCredential.prototype.matchIdTokenWithUid = function(rpcHandler, uid) {}; /** * @return {!Object} The plain object representation of an Auth credential. This * will be exposed as toJSON() externally. */ fireauth.AuthCredential.prototype.toPlainObject = function() {}; /** * @param {!goog.Promise<!Object>} idTokenResolver A promise that resolves with * the ID token response. * @param {string} uid The UID to match in the token response. * @return {!goog.Promise<!Object>} A promise that resolves with the same * response if the UID matches. */ fireauth.AuthCredential.verifyTokenResponseUid = function(idTokenResolver, uid) { return idTokenResolver.then(function(response) { // This should not happen as rpcHandler verifyAssertion and verifyPassword // always guarantee an ID token is available. if (response[fireauth.RpcHandler.AuthServerField.ID_TOKEN]) { // Parse the token object. var parsedIdToken = fireauth.IdToken.parse( response[fireauth.RpcHandler.AuthServerField.ID_TOKEN]); // Confirm token localId matches the provided UID. If not, throw the user // mismatch error. if (!parsedIdToken || uid != parsedIdToken.getLocalId()) { throw new fireauth.AuthError(fireauth.authenum.Error.USER_MISMATCH); } return response; } throw new fireauth.AuthError(fireauth.authenum.Error.USER_MISMATCH); }) .thenCatch(function(error) { // Translate auth/user-not-found error directly to auth/user-mismatch. throw fireauth.AuthError.translateError( error, fireauth.authenum.Error.USER_DELETED, fireauth.authenum.Error.USER_MISMATCH); }); }; /** * The interface that represents the Auth provider. * @interface */ fireauth.AuthProvider = function() {}; /** * @param {...*} var_args The credential data. * @return {!fireauth.AuthCredential} The Auth provider credential. */ fireauth.AuthProvider.credential; /** * @typedef {{ * accessToken: (?string|undefined), * idToken: (?string|undefined), * nonce: (?string|undefined), * oauthToken: (?string|undefined), * oauthTokenSecret: (?string|undefined), * pendingToken: (?string|undefined) * }} */ fireauth.OAuthResponse; /** * The SAML Auth credential class. The Constructor is not publicly visible. * This is constructed by the SDK on successful or failure after SAML sign-in * and returned to developer. * @param {!fireauth.idp.ProviderId} providerId The provider ID. * @param {string} pendingToken The SAML response pending token. * @constructor * @implements {fireauth.AuthCredential} */ fireauth.SAMLAuthCredential = function(providerId, pendingToken) { if (pendingToken) { /** @private {string} The pending token where SAML response is encrypted. */ this.pendingToken_ = pendingToken; } else { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR, 'failed to construct a credential'); } fireauth.object.setReadonlyProperty(this, 'providerId', providerId); fireauth.object.setReadonlyProperty(this, 'signInMethod', providerId); }; /** * Returns a promise to retrieve ID token using the underlying RPC handler API * for the current credential. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @return {!goog.Promise<!Object, !fireauth.AuthError>} * idTokenPromise The RPC handler method that returns a promise which * resolves with an ID token. * @override */ fireauth.SAMLAuthCredential.prototype.getIdTokenProvider = function(rpcHandler) { return rpcHandler.verifyAssertion( /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ ( this.makeVerifyAssertionRequest_())); }; /** * Links the credential to an existing account, identified by an ID token. * @param {!fireauth.RpcHandler} rpcHandler The rpc handler. * @param {string} idToken The ID token of the existing account. * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts * are linked, returning the backend response. * @override */ fireauth.SAMLAuthCredential.prototype.linkToIdToken = function(rpcHandler, idToken) { var request = this.makeVerifyAssertionRequest_(); request['idToken'] = idToken; return rpcHandler.verifyAssertionForLinking( /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request)); }; /** * Tries to match the credential's idToken with the provided UID. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} uid The UID of the user to reauthenticate. * @return {!goog.Promise<!Object>} A Promise that resolves when * idToken UID match succeeds and returns the server response. * @override */ fireauth.SAMLAuthCredential.prototype.matchIdTokenWithUid = function(rpcHandler, uid) { var request = this.makeVerifyAssertionRequest_(); // Do not create a new account if the user doesn't exist. return fireauth.AuthCredential.verifyTokenResponseUid( rpcHandler.verifyAssertionForExisting( /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request)), uid); }; /** * @return {!Object} A request to the VerifyAssertion endpoint, populated with * the assertion data from this credential. * @private */ fireauth.SAMLAuthCredential.prototype.makeVerifyAssertionRequest_ = function() { return { 'pendingToken': this.pendingToken_, // Always use http://localhost. 'requestUri': 'http://localhost' }; }; /** * @return {!Object} The plain object representation of an Auth credential. * @override */ fireauth.SAMLAuthCredential.prototype.toPlainObject = function() { return { 'providerId': this['providerId'], 'signInMethod': this['signInMethod'], 'pendingToken': this.pendingToken_ }; }; /** * @param {?Object|undefined} json The plain object representation of a * SAMLAuthCredential. * @return {?fireauth.SAMLAuthCredential} The SAML credential if the object * is a JSON representation of a SAMLAuthCredential, null otherwise. */ fireauth.SAMLAuthCredential.fromJSON = function(json) { if (json && json['providerId'] && json['signInMethod'] && json['providerId'].indexOf(fireauth.constants.SAML_PREFIX) == 0 && json['pendingToken']) { try { return new fireauth.SAMLAuthCredential( json['providerId'], json['pendingToken']); } catch (e) { return null; } } return null; }; /** * The OAuth credential class. * @param {!fireauth.idp.ProviderId} providerId The provider ID. * @param {!fireauth.OAuthResponse} oauthResponse The OAuth * response object containing token information. * @param {!fireauth.idp.SignInMethod} signInMethod The sign in method. * @constructor * @implements {fireauth.AuthCredential} */ fireauth.OAuthCredential = function(providerId, oauthResponse, signInMethod) { /** * @private {?string} The pending token where the IdP response is encrypted. */ this.pendingToken_ = null; if (oauthResponse['idToken'] || oauthResponse['accessToken']) { // OAuth 2 and either ID token or access token. if (oauthResponse['idToken']) { fireauth.object.setReadonlyProperty( this, 'idToken', oauthResponse['idToken']); } if (oauthResponse['accessToken']) { fireauth.object.setReadonlyProperty( this, 'accessToken', oauthResponse['accessToken']); } // Add nonce if available and no pendingToken is present. if (oauthResponse['nonce'] && !oauthResponse['pendingToken']) { fireauth.object.setReadonlyProperty( this, 'nonce', oauthResponse['nonce']); } if (oauthResponse['pendingToken']) { this.pendingToken_ = oauthResponse['pendingToken']; } } else if (oauthResponse['oauthToken'] && oauthResponse['oauthTokenSecret']) { // OAuth 1 and OAuth token with OAuth token secret. fireauth.object.setReadonlyProperty( this, 'accessToken', oauthResponse['oauthToken']); fireauth.object.setReadonlyProperty( this, 'secret', oauthResponse['oauthTokenSecret']); } else { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR, 'failed to construct a credential'); } fireauth.object.setReadonlyProperty(this, 'providerId', providerId); fireauth.object.setReadonlyProperty(this, 'signInMethod', signInMethod); }; /** * Returns a promise to retrieve ID token using the underlying RPC handler API * for the current credential. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @return {!goog.Promise<!Object, !fireauth.AuthError>} * idTokenPromise The RPC handler method that returns a promise which * resolves with an ID token. * @override */ fireauth.OAuthCredential.prototype.getIdTokenProvider = function(rpcHandler) { return rpcHandler.verifyAssertion( /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ ( this.makeVerifyAssertionRequest_())); }; /** * Links the credential to an existing account, identified by an ID token. * @param {!fireauth.RpcHandler} rpcHandler The rpc handler. * @param {string} idToken The ID token of the existing account. * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts * are linked, returning the backend response. * @override */ fireauth.OAuthCredential.prototype.linkToIdToken = function(rpcHandler, idToken) { var request = this.makeVerifyAssertionRequest_(); request['idToken'] = idToken; return rpcHandler.verifyAssertionForLinking( /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request)); }; /** * Tries to match the credential's idToken with the provided UID. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} uid The UID of the user to reauthenticate. * @return {!goog.Promise<!Object>} A Promise that resolves when * idToken UID match succeeds and returns the server response. * @override */ fireauth.OAuthCredential.prototype.matchIdTokenWithUid = function(rpcHandler, uid) { var request = this.makeVerifyAssertionRequest_(); // Do not create a new account if the user doesn't exist. return fireauth.AuthCredential.verifyTokenResponseUid( rpcHandler.verifyAssertionForExisting( /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request)), uid); }; /** * @return {!Object} A request to the VerifyAssertion endpoint, populated with * the OAuth data from this credential. * @private */ fireauth.OAuthCredential.prototype.makeVerifyAssertionRequest_ = function() { var 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']; // Pass nonce in postBody if available. if (this['nonce'] && !this.pendingToken_) { postBody['nonce'] = this['nonce']; } var request = { 'postBody': goog.Uri.QueryData.createFromMap(postBody).toString(), // Always use http://localhost. 'requestUri': 'http://localhost' }; if (this.pendingToken_) { // For pendingToken, just pass it through and drop postBody. delete request['postBody']; request['pendingToken'] = this.pendingToken_; } return request; }; /** * @return {!Object} The plain object representation of an Auth credential. * @override */ fireauth.OAuthCredential.prototype.toPlainObject = function() { var obj = { 'providerId': this['providerId'], 'signInMethod': this['signInMethod'] }; if (this['idToken']) { obj['oauthIdToken'] = this['idToken']; } if (this['accessToken']) { obj['oauthAccessToken'] = this['accessToken']; } if (this['secret']) { obj['oauthTokenSecret'] = this['secret']; } if (this['nonce']) { obj['nonce'] = this['nonce']; } if (this.pendingToken_) { obj['pendingToken'] = this.pendingToken_; } return obj; }; /** * @param {?Object|undefined} json The plain object representation of an * OAuthCredential. * @return {?fireauth.OAuthCredential} The OAuth/OIDC credential if the object * is a JSON representation of an OAuthCredential, null otherwise. */ fireauth.OAuthCredential.fromJSON = function(json) { if (json && json['providerId'] && json['signInMethod']) { // Convert to OAuthResponse format. var oauthResponse = { // OIDC && google.com. 'idToken': json['oauthIdToken'], // OAuth 2.0 providers. 'accessToken': json['oauthTokenSecret'] ? null : json['oauthAccessToken'], // OAuth 1.0 provider, eg. Twitter. 'oauthTokenSecret': json['oauthTokenSecret'], 'oauthToken': json['oauthTokenSecret'] && json['oauthAccessToken'], 'nonce': json['nonce'], 'pendingToken': json['pendingToken'] }; try { // Constructor will validate the OAuthResponse. return new fireauth.OAuthCredential( json['providerId'], oauthResponse, json['signInMethod']); } catch (e) { return null; } } return null; }; /** * A generic OAuth provider (OAuth1 or OAuth2). * @param {string} providerId The IdP provider ID (e.g. google.com, * facebook.com) registered with the backend. * @param {?Array<string>=} opt_reservedParams The backlist of parameters that * cannot be set through setCustomParameters. * @constructor */ fireauth.FederatedProvider = function(providerId, opt_reservedParams) { /** @private {!Array<string>} */ this.reservedParams_ = opt_reservedParams || []; // Set read only instance providerId property. // Set read only instance isOAuthProvider property. fireauth.object.setReadonlyProperties(this, { 'providerId': providerId, 'isOAuthProvider': true }); /** @private {!Object} The OAuth custom parameters for current provider. */ this.customParameters_ = {}; /** @protected {?string} The custom OAuth language parameter. */ this.languageParameter = (fireauth.idp.getIdpSettings(/** @type {!fireauth.idp.ProviderId} */ ( providerId)) || {}).languageParam || null; /** @protected {?string} The default language. */ this.defaultLanguageCode = null; }; /** * @param {!Object} customParameters The custom OAuth parameters to pass * in OAuth request. * @return {!fireauth.FederatedProvider} The FederatedProvider instance, for * chaining method calls. */ fireauth.FederatedProvider.prototype.setCustomParameters = function(customParameters) { this.customParameters_ = goog.object.clone(customParameters); return this; }; /** * Set the default language code on the provider instance. * @param {?string} languageCode The default language code to set if not already * provided in the custom parameters. */ fireauth.FederatedProvider.prototype.setDefaultLanguage = function(languageCode) { this.defaultLanguageCode = languageCode; }; /** * @return {!Object} The custom OAuth parameters to pass in OAuth request. */ fireauth.FederatedProvider.prototype.getCustomParameters = function() { // The backend already checks for these values and makes sure no reserved // fields like client ID, redirect URI, state are overwritten by these // fields. var params = fireauth.util.copyWithoutNullsOrUndefined(this.customParameters_); // Convert to strings. for (var key in params) { params[key] = params[key].toString(); } // Remove blacklisted OAuth custom parameters. var customParams = fireauth.util.removeEntriesWithKeys(params, this.reservedParams_); // If language param supported and not already provided, use default language. if (this.languageParameter && this.defaultLanguageCode && !customParams[this.languageParameter]) { customParams[this.languageParameter] = this.defaultLanguageCode; } return customParams; }; /** * Generic SAML auth provider. * @param {string} providerId The SAML IdP provider ID (e.g. saml.saml2rp) * registered with the backend. * @constructor * @extends {fireauth.FederatedProvider} * @implements {fireauth.AuthProvider} */ fireauth.SAMLAuthProvider = function(providerId) { // SAML provider IDs must be prefixed with the SAML_PREFIX. if (!fireauth.idp.isSaml(providerId)) { throw new fireauth.AuthError( fireauth.authenum.Error.ARGUMENT_ERROR, 'SAML provider IDs must be prefixed with "' + fireauth.constants.SAML_PREFIX + '"'); } // isOAuthProvider is true even though this is not an OAuth provider. // This can be confusing as this is a SAML provider. However, this property // is needed to allow signInWithPopup/Redirect. We should rename it to // something more accurate: isFederatedProvider. fireauth.SAMLAuthProvider.base(this, 'constructor', providerId, []); }; goog.inherits(fireauth.SAMLAuthProvider, fireauth.FederatedProvider); /** * Generic OAuth2 Auth provider. * @param {string} providerId The IdP provider ID (e.g. google.com, * facebook.com) registered with the backend. * @constructor * @extends {fireauth.FederatedProvider} * @implements {fireauth.AuthProvider} */ fireauth.OAuthProvider = function(providerId) { fireauth.OAuthProvider.base(this, 'constructor', providerId, fireauth.idp.RESERVED_OAUTH2_PARAMS); /** @private {!Array<string>} The list of OAuth2 scopes to request. */ this.scopes_ = []; }; goog.inherits(fireauth.OAuthProvider, fireauth.FederatedProvider); /** * @param {string} scope The OAuth scope to request. * @return {!fireauth.OAuthProvider} The OAuthProvider instance, for chaining * method calls. */ fireauth.OAuthProvider.prototype.addScope = function(scope) { // If not already added, add scope to list. if (!goog.array.contains(this.scopes_, scope)) { this.scopes_.push(scope); } return this; }; /** @return {!Array<string>} The Auth provider's list of scopes. */ fireauth.OAuthProvider.prototype.getScopes = function() { return goog.array.clone(this.scopes_); }; /** * Initializes an OAuth AuthCredential. At least one of ID token or access token * must be defined. When providing an OIDC ID token with a nonce encoded, the * raw nonce must also be provided. * @param {?Object|string} optionsOrIdToken Either the options object containing * the ID token, access token and raw nonce or the ID token string. * @param {?string=} opt_accessToken The optional OAuth access token. * @return {!fireauth.AuthCredential} The Auth credential object. */ fireauth.OAuthProvider.prototype.credential = function(optionsOrIdToken, opt_accessToken) { var oauthResponse; if (goog.isObject(optionsOrIdToken)) { oauthResponse = { 'idToken': optionsOrIdToken['idToken'] || null, 'accessToken': optionsOrIdToken['accessToken'] || null, 'nonce': optionsOrIdToken['rawNonce'] || null }; } else { oauthResponse = { 'idToken': optionsOrIdToken || null, 'accessToken': opt_accessToken || null }; } if (!oauthResponse['idToken'] && !oauthResponse['accessToken']) { throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'credential failed: must provide the ID token and/or the access ' + 'token.'); } // For OAuthCredential, sign in method is same as providerId. return new fireauth.OAuthCredential(this['providerId'], oauthResponse, this['providerId']); }; /** * Facebook Auth provider. * @constructor * @extends {fireauth.OAuthProvider} * @implements {fireauth.AuthProvider} */ fireauth.FacebookAuthProvider = function() { fireauth.FacebookAuthProvider.base(this, 'constructor', fireauth.idp.ProviderId.FACEBOOK); }; goog.inherits(fireauth.FacebookAuthProvider, fireauth.OAuthProvider); fireauth.object.setReadonlyProperty(fireauth.FacebookAuthProvider, 'PROVIDER_ID', fireauth.idp.ProviderId.FACEBOOK); fireauth.object.setReadonlyProperty(fireauth.FacebookAuthProvider, 'FACEBOOK_SIGN_IN_METHOD', fireauth.idp.SignInMethod.FACEBOOK); /** * Initializes a Facebook AuthCredential. * @param {string} accessTokenOrObject The Facebook access token, or object * containing the token for FirebaseUI backwards compatibility. * @return {!fireauth.AuthCredential} The Auth credential object. */ fireauth.FacebookAuthProvider.credential = function(accessTokenOrObject) { if (!accessTokenOrObject) { throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'credential failed: expected 1 argument (the OAuth access token).'); } var accessToken = accessTokenOrObject; if (goog.isObject(accessTokenOrObject)) { accessToken = accessTokenOrObject['accessToken']; } return new fireauth.FacebookAuthProvider().credential({ 'accessToken': /** @type {string} */ (accessToken) }); }; /** * GitHub Auth provider. * @constructor * @extends {fireauth.OAuthProvider} * @implements {fireauth.AuthProvider} */ fireauth.GithubAuthProvider = function() { fireauth.GithubAuthProvider.base(this, 'constructor', fireauth.idp.ProviderId.GITHUB); }; goog.inherits(fireauth.GithubAuthProvider, fireauth.OAuthProvider); fireauth.object.setReadonlyProperty(fireauth.GithubAuthProvider, 'PROVIDER_ID', fireauth.idp.ProviderId.GITHUB); fireauth.object.setReadonlyProperty(fireauth.GithubAuthProvider, 'GITHUB_SIGN_IN_METHOD', fireauth.idp.SignInMethod.GITHUB); /** * Initializes a GitHub AuthCredential. * @param {string} accessTokenOrObject The GitHub access token, or object * containing the token for FirebaseUI backwards compatibility. * @return {!fireauth.AuthCredential} The Auth credential object. */ fireauth.GithubAuthProvider.credential = function(accessTokenOrObject) { if (!accessTokenOrObject) { throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'credential failed: expected 1 argument (the OAuth access token).'); } var accessToken = accessTokenOrObject; if (goog.isObject(accessTokenOrObject)) { accessToken = accessTokenOrObject['accessToken']; } return new fireauth.GithubAuthProvider().credential({ 'accessToken': /** @type {string} */ (accessToken) }); }; /** * Google Auth provider. * @constructor * @extends {fireauth.OAuthProvider} * @implements {fireauth.AuthProvider} */ fireauth.GoogleAuthProvider = function() { fireauth.GoogleAuthProvider.base(this, 'constructor', fireauth.idp.ProviderId.GOOGLE); // Add profile scope to Google Auth provider as default scope. // This is to ensure profile info is populated in current user. this.addScope('profile'); }; goog.inherits(fireauth.GoogleAuthProvider, fireauth.OAuthProvider); fireauth.object.setReadonlyProperty(fireauth.GoogleAuthProvider, 'PROVIDER_ID', fireauth.idp.ProviderId.GOOGLE); fireauth.object.setReadonlyProperty(fireauth.GoogleAuthProvider, 'GOOGLE_SIGN_IN_METHOD', fireauth.idp.SignInMethod.GOOGLE); /** * Initializes a Google AuthCredential. * @param {?string=} idTokenOrObject The Google ID token. If null or undefined, * we expect the access token to be passed. It can also be an object * containing the tokens for FirebaseUI backwards compatibility. * @param {?string=} accessToken The Google access token. If null or * undefined, we expect the ID token to have been passed. * @return {!fireauth.AuthCredential} The Auth credential object. */ fireauth.GoogleAuthProvider.credential = function(idTokenOrObject, accessToken) { var idToken = idTokenOrObject; if (goog.isObject(idTokenOrObject)) { idToken = idTokenOrObject['idToken']; accessToken = idTokenOrObject['accessToken']; } return new fireauth.GoogleAuthProvider().credential({ 'idToken': /** @type {string} */ (idToken), 'accessToken': /** @type {string} */ (accessToken) }); }; /** * Twitter Auth provider. * @constructor * @extends {fireauth.FederatedProvider} * @implements {fireauth.AuthProvider} */ fireauth.TwitterAuthProvider = function() { fireauth.TwitterAuthProvider.base(this, 'constructor', fireauth.idp.ProviderId.TWITTER, fireauth.idp.RESERVED_OAUTH1_PARAMS); }; goog.inherits(fireauth.TwitterAuthProvider, fireauth.FederatedProvider); fireauth.object.setReadonlyProperty(fireauth.TwitterAuthProvider, 'PROVIDER_ID', fireauth.idp.ProviderId.TWITTER); fireauth.object.setReadonlyProperty(fireauth.TwitterAuthProvider, 'TWITTER_SIGN_IN_METHOD', fireauth.idp.SignInMethod.TWITTER); /** * Initializes a Twitter AuthCredential. * @param {string} tokenOrObject The Twitter access token, or object * containing the token for FirebaseUI backwards compatibility. * @param {string} secret The Twitter secret. * @return {!fireauth.AuthCredential} The Auth credential object. */ fireauth.TwitterAuthProvider.credential = function(tokenOrObject, secret) { var tokenObject = tokenOrObject; if (!goog.isObject(tokenObject)) { tokenObject = { 'oauthToken': tokenOrObject, 'oauthTokenSecret': secret }; } if (!tokenObject['oauthToken'] || !tokenObject['oauthTokenSecret']) { throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'credential failed: expected 2 arguments (the OAuth access token ' + 'and secret).'); } return new fireauth.OAuthCredential(fireauth.idp.ProviderId.TWITTER, /** @type {!fireauth.OAuthResponse} */ (tokenObject), fireauth.idp.SignInMethod.TWITTER); }; /** * The email and password credential class. * @param {string} email The credential email. * @param {string} password The credential password. * @param {string=} opt_signInMethod The credential sign in method can be either * 'password' or 'emailLink' * @constructor * @implements {fireauth.AuthCredential} */ fireauth.EmailAuthCredential = function(email, password, opt_signInMethod) { this.email_ = email; this.password_ = password; fireauth.object.setReadonlyProperty(this, 'providerId', fireauth.idp.ProviderId.PASSWORD); var signInMethod = opt_signInMethod === fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD'] ? fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD'] : fireauth.EmailAuthProvider['EMAIL_PASSWORD_SIGN_IN_METHOD']; fireauth.object.setReadonlyProperty(this, 'signInMethod', signInMethod); }; /** * Returns a promise to retrieve ID token using the underlying RPC handler API * for the current credential. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @return {!goog.Promise<!Object, !fireauth.AuthError>} * idTokenPromise The RPC handler method that returns a promise which * resolves with an ID token. * @override */ fireauth.EmailAuthCredential.prototype.getIdTokenProvider = function(rpcHandler) { if (this['signInMethod'] == fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD']) { return rpcHandler.emailLinkSignIn(this.email_, this.password_); } return rpcHandler.verifyPassword(this.email_, this.password_); }; /** * Adds an email and password account to an existing account, identified by an * ID token. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} idToken The ID token of the existing account. * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts * are linked, returning the backend response. * @override */ fireauth.EmailAuthCredential.prototype.linkToIdToken = function(rpcHandler, idToken) { if (this['signInMethod'] == fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD']) { return rpcHandler.emailLinkSignInForLinking( idToken, this.email_, this.password_); } return rpcHandler.updateEmailAndPassword( idToken, this.email_, this.password_); }; /** * Tries to match the credential's idToken with the provided UID. * @param {!fireauth.RpcHandler} rpcHandler The rpc handler. * @param {string} uid The UID of the user to reauthenticate. * @return {!goog.Promise<!Object>} A Promise that resolves when * reauthentication succeeds. * @override */ fireauth.EmailAuthCredential.prototype.matchIdTokenWithUid = function(rpcHandler, uid) { // Do not create a new account if the user doesn't exist. return fireauth.AuthCredential.verifyTokenResponseUid( // This shouldn't create a new email/password account. this.getIdTokenProvider(rpcHandler), uid); }; /** * @return {!Object} The plain object representation of an Auth credential. * @override */ fireauth.EmailAuthCredential.prototype.toPlainObject = function() { return { 'email': this.email_, 'password': this.password_, 'signInMethod': this['signInMethod'] }; }; /** * @param {?Object|undefined} json The plain object representation of a * EmailAuthCredential. * @return {?fireauth.EmailAuthCredential} The email credential if the object * is a JSON representation of an EmailAuthCredential, null otherwise. */ fireauth.EmailAuthCredential.fromJSON = function(json) { if (json && json['email'] && json['password']) { return new fireauth.EmailAuthCredential( json['email'], json['password'], json['signInMethod']); } return null; }; /** * Email password Auth provider implementation. * @constructor * @implements {fireauth.AuthProvider} */ fireauth.EmailAuthProvider = function() { // Set read-only instance providerId and isOAuthProvider property. fireauth.object.setReadonlyProperties(this, { 'providerId': fireauth.idp.ProviderId.PASSWORD, 'isOAuthProvider': false }); }; /** * Initializes an instance of an email/password Auth credential. * @param {string} email The credential email. * @param {string} password The credential password. * @return {!fireauth.EmailAuthCredential} The Auth credential object. */ fireauth.EmailAuthProvider.credential = function(email, password) { return new fireauth.EmailAuthCredential(email, password); }; /** * @param {string} email The credential email. * @param {string} emailLink The credential email link. * @return {!fireauth.EmailAuthCredential} The Auth credential object. */ fireauth.EmailAuthProvider.credentialWithLink = function(email, emailLink) { var actionCodeUrl = fireauth.EmailAuthProvider .getActionCodeUrlFromSignInEmailLink(emailLink); if (!actionCodeUrl) { throw new fireauth.AuthError( fireauth.authenum.Error.ARGUMENT_ERROR, 'Invalid email link!'); } return new fireauth.EmailAuthCredential(email, actionCodeUrl['code'], fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD']); }; /** * @param {string} emailLink The sign in email link to be validated. * @return {?fireauth.ActionCodeURL} The sign in email link action code URL. * Returns null if the email link is invalid. */ fireauth.EmailAuthProvider.getActionCodeUrlFromSignInEmailLink = function(emailLink) { emailLink = fireauth.DynamicLink.parseDeepLink(emailLink); var actionCodeUrl = fireauth.ActionCodeURL.parseLink(emailLink); if (actionCodeUrl && (actionCodeUrl['operation'] === fireauth.ActionCodeInfo.Operation.EMAIL_SIGNIN)) { return actionCodeUrl; } return null; }; // Set read only PROVIDER_ID property. fireauth.object.setReadonlyProperties(fireauth.EmailAuthProvider, { 'PROVIDER_ID': fireauth.idp.ProviderId.PASSWORD }); // Set read only EMAIL_LINK_SIGN_IN_METHOD property. fireauth.object.setReadonlyProperties(fireauth.EmailAuthProvider, { 'EMAIL_LINK_SIGN_IN_METHOD': fireauth.idp.SignInMethod.EMAIL_LINK }); // Set read only EMAIL_PASSWORD_SIGN_IN_METHOD property. fireauth.object.setReadonlyProperties(fireauth.EmailAuthProvider, { 'EMAIL_PASSWORD_SIGN_IN_METHOD': fireauth.idp.SignInMethod.EMAIL_PASSWORD }); /** * A credential for phone number sign-in. Phone credentials can also be used as * second factor assertions. * A `PhoneAuthCredential` is also a `MultiFactorAuthCredential`. A * `PhoneMultiFactorAssertion` requires a `PhoneAuthCredential`. * @param {!fireauth.PhoneAuthCredential.Parameters_} params The credential * parameters that prove the user owns the claimed phone number. * @constructor * @implements {fireauth.MultiFactorAuthCredential} */ fireauth.PhoneAuthCredential = function(params) { // Either verification ID and code, or phone number temporary proof must be // provided. if (!(params.verificationId && params.verificationCode) && !(params.temporaryProof && params.phoneNumber)) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } /** * The phone Auth parameters that prove ownership of a phone number, either * through completion of a phone verification flow, or by referencing a * previously completed verification flow ("temporaryProof"). * @private {!fireauth.PhoneAuthCredential.Parameters_} */ this.params_ = params; fireauth.object.setReadonlyProperty(this, 'providerId', fireauth.idp.ProviderId.PHONE); /** * @public {string} The provider ID required by the * `fireauth.MultiFactorAuthCredential` interface. */ this.providerId = fireauth.idp.ProviderId.PHONE; fireauth.object.setReadonlyProperty( this, 'signInMethod', fireauth.idp.SignInMethod.PHONE); }; /** * Parameters that prove ownership of a phone number via a ID "verificationId" * of a request to send a code to the phone number, with the code * "verificationCode" that the user received on their phone. * @private * @typedef {{ * verificationId: string, * verificationCode: string * }} */ fireauth.PhoneAuthCredential.VerificationParameters_; /** * Parameters that prove ownership of a phone number by referencing a previously * completed phone Auth flow. * @private * @typedef {{ * temporaryProof: string, * phoneNumber: string * }} */ fireauth.PhoneAuthCredential.TemporaryProofParameters_; /** * @private * @typedef { * !fireauth.PhoneAuthCredential.VerificationParameters_| * !fireauth.PhoneAuthCredential.TemporaryProofParameters_ * } */ fireauth.PhoneAuthCredential.Parameters_; /** * Retrieves an ID token from the backend given the current credential. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @return {!goog.Promise<!Object>} A Promise that resolves with the * backend response. * @override */ fireauth.PhoneAuthCredential.prototype.getIdTokenProvider = function(rpcHandler) { return rpcHandler.verifyPhoneNumber(this.makeVerifyPhoneNumberRequest_()); }; /** * Adds a phone credential to an existing account identified by an ID token. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} idToken The ID token of the existing account. * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts * are linked, returning the backend response. * @override */ fireauth.PhoneAuthCredential.prototype.linkToIdToken = function(rpcHandler, idToken) { var request = this.makeVerifyPhoneNumberRequest_(); request['idToken'] = idToken; return rpcHandler.verifyPhoneNumberForLinking(request); }; /** * Tries to match the credential's idToken with the provided UID. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler. * @param {string} uid The UID of the user to reauthenticate. * @return {!goog.Promise<!Object>} A Promise that resolves when * reauthentication succeeds. * @override */ fireauth.PhoneAuthCredential.prototype.matchIdTokenWithUid = function(rpcHandler, uid) { var request = this.makeVerifyPhoneNumberRequest_(); return fireauth.AuthCredential.verifyTokenResponseUid( rpcHandler.verifyPhoneNumberForExisting(request), uid); }; /** * Converts a PhoneAuthCredential to a plain object. * @return {!Object} * @override */ fireauth.PhoneAuthCredential.prototype.toPlainObject = function() { var obj = { 'providerId': fireauth.idp.ProviderId.PHONE }; if (this.params_.verificationId) { obj['verificationId'] = this.params_.verificationId; } if (this.params_.verificationCode) { obj['verificationCode'] = this.params_.verificationCode; } if (this.params_.temporaryProof) { obj['temporaryProof'] = this.params_.temporaryProof; } if (this.params_.phoneNumber) { obj['phoneNumber'] = this.params_.phoneNumber; } return obj; }; /** * @param {?Object|undefined} json The plain object representation of a * PhoneAuthCredential. * @return {?fireauth.PhoneAuthCredential} The phone credential if the object * is a JSON representation of an PhoneAuthCredential, null otherwise. */ fireauth.PhoneAuthCredential.fromJSON = function(json) { if (json && json['providerId'] === fireauth.idp.ProviderId.PHONE && ((json['verificationId'] && json['verificationCode']) || (json['temporaryProof'] && json['phoneNumber']))) { var params = {}; var allowedKeys = [ 'verificationId', 'verificationCode', 'temporaryProof', 'phoneNumber' ]; goog.array.forEach(allowedKeys, function(key) { if (json[key]) { params[key] = json[key]; } }); return new fireauth.PhoneAuthCredential( /** @type {!fireauth.PhoneAuthCredential.Parameters_} */ (params)); } return null; }; /** * @return {!Object} A request to the verifyPhoneNumber endpoint based on the * current state of the object. * @private */ fireauth.PhoneAuthCredential.prototype.makeVerifyPhoneNumberRequest_ = function() { if (this.params_.temporaryProof && this.params_.phoneNumber) { return { 'temporaryProof': this.params_.temporaryProof, 'phoneNumber': this.params_.phoneNumber }; } return { 'sessionInfo': this.params_.verificationId, 'code': this.params_.verificationCode }; }; /** * Finalizes the 2nd factor enrollment flow with the current AuthCredential * using the enrollment request identifier. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance. * @param {!fireauth.MultiFactorEnrollmentRequestIdentifier} enrollmentRequest * The enrollment request identifying the user. * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise * that resolves with the updated ID and refresh tokens. * @override */ fireauth.PhoneAuthCredential.prototype.finalizeMfaEnrollment = function(rpcHandler, enrollmentRequest) { goog.object.extend( enrollmentRequest, { 'phoneVerificationInfo': this.makeVerifyPhoneNumberRequest_() }); return /** @type {!goog.Promise<{idToken: string, refreshToken: string}>} */ ( rpcHandler.finalizePhoneMfaEnrollment(enrollmentRequest)); }; /** * Finalizes the 2nd factor sign-in flow with the current AuthCredential * using the sign-in request identifier. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance. * @param {!fireauth.MultiFactorSignInRequestIdentifier} signInRequest * The sign-in request identifying the user. * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise * that resolves with the signed in user's ID and refresh tokens. * @override */ fireauth.PhoneAuthCredential.prototype.finalizeMfaSignIn = function(rpcHandler, signInRequest) { goog.object.extend( signInRequest, { 'phoneVerificationInfo': this.makeVerifyPhoneNumberRequest_() }); return /** @type {!goog.Promise<{idToken: string, refreshToken: string}>} */ ( rpcHandler.finalizePhoneMfaSignIn(signInRequest)); }; /** * Phone Auth provider implementation. * @param {?fireauth.Auth=} opt_auth The Firebase Auth instance. * @constructor * @implements {fireauth.AuthProvider} */ fireauth.PhoneAuthProvider = function(opt_auth) { try { /** @private {!fireauth.Auth} */ this.auth_ = opt_auth || firebase['auth'](); } catch (e) { throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'Either an instance of firebase.auth.Auth must be passed as an ' + 'argument to the firebase.auth.PhoneAuthProvider constructor, or the ' + 'default firebase App instance must be initialized via ' + 'firebase.initializeApp().'); } fireauth.object.setReadonlyProperties(this, { 'providerId': fireauth.idp.ProviderId.PHONE, 'isOAuthProvider': false }); }; /** * The phone info options for single-factor sign-in. Only phone number is * required. * @private * @typedef {{ * phoneNumber: string * }} */ fireauth.PhoneAuthProvider.PhoneSingleFactorInfoOptions_; /** * The phone info options for multi-factor enrollment. Phone number and * multi-factor session are required. * @private * @typedef {{ * phoneNumber: string, * session: !fireauth.MultiFactorSession * }} */ fireauth.PhoneAuthProvider.PhoneMultiFactorEnrollInfoOptions_; /** * The phone info options for multi-factor sign-in. Either multi-factor hint or * multi-factor UID and multi-factor session are required. * @private * @typedef {{ * multiFactorHint: !fireauth.MultiFactorInfo, * session: !fireauth.MultiFactorSession * }|{ * multiFactorUid: string, * session: !fireauth.MultiFactorSession * }} */ fireauth.PhoneAuthProvider.PhoneMultiFactorSignInInfoOptions_; /** * The options for verifying the ownership of the phone number. It could be * used for single-factor sign-in, multi-factor enrollment or multi-factor * sign-in. * @typedef { * !fireauth.PhoneAuthProvider.PhoneSingleFactorInfoOptions_| * !fireauth.PhoneAuthProvider.PhoneMultiFactorEnrollInfoOptions_| * !fireauth.PhoneAuthProvider.PhoneMultiFactorSignInInfoOptions_ * } */ fireauth.PhoneAuthProvider.PhoneInfoOptions; /** * Initiates a phone number confirmation flow. If session is provided, it is * used to verify ownership of the second factor phone number. * * @param {string|!fireauth.PhoneAuthProvider.PhoneInfoOptions} phoneInfoOptions * The user's phone options for verifying the ownship of the phone number. * @param {!firebase.auth.ApplicationVerifier} applicationVerifier The * application verifier for anti-abuse purposes. * @return {!goog.Promise<string>} A Promise that resolves with the * verificationId of the phone number confirmation flow. */ fireauth.PhoneAuthProvider.prototype.verifyPhoneNumber = function(phoneInfoOptions, applicationVerifier) { var rpcHandler = this.auth_.getRpcHandler(); // Convert the promise into a goog.Promise. If the applicationVerifier throws // an error, just propagate it to the client. Reset the reCAPTCHA widget every // time after sending the token to the server. return goog.Promise.resolve(applicationVerifier['verify']()) .then(function(assertion) { if (typeof assertion !== 'string') { throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'An implementation of firebase.auth.ApplicationVerifier' + '.prototype.verify() must return a firebase.Promise ' + 'that resolves with a string.'); } switch (applicationVerifier['type']) { case 'recaptcha': var session = goog.isObject(phoneInfoOptions) ? phoneInfoOptions['session'] : null; // PhoneInfoOptions can be a phone number string for backward // compatibility. var phoneNumber = goog.isObject(phoneInfoOptions) ? phoneInfoOptions['phoneNumber'] : phoneInfoOptions; var verifyPromise; if (session && session.type == fireauth.MultiFactorSession.Type.ENROLL) { verifyPromise = session.getRawSession() .then(function(rawSession) { return rpcHandler.startPhoneMfaEnrollment({ 'idToken': rawSession, 'phoneEnrollmentInfo': { 'phoneNumber': phoneNumber, 'recaptchaToken': assertion } }); }); } else if (session && session.type == fireauth.MultiFactorSession.Type.SIGN_IN) { verifyPromise = session.getRawSession() .then(function(rawSession) { var mfaEnrollmentId = (phoneInfoOptions['multiFactorHint'] && phoneInfoOptions['multiFactorHint']['uid']) || phoneInfoOptions['multiFactorUid']; return rpcHandler.startPhoneMfaSignIn({ 'mfaPendingCredential': rawSession, 'mfaEnrollmentId': mfaEnrollmentId, 'phoneSignInInfo': { 'recaptchaToken': assertion } }); }); } else { verifyPromise = rpcHandler.sendVerificationCode({ 'phoneNumber': phoneNumber, 'recaptchaToken': assertion }); } // Reset the applicationVerifier after code is sent. return verifyPromise.then(function(verificationId) { if (typeof applicationVerifier.reset === 'function') { applicationVerifier.reset(); } return verificationId; }, function(error) { if (typeof applicationVerifier.reset === 'function') { applicationVerifier.reset(); } throw error; }); default: throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR, 'Only firebase.auth.ApplicationVerifiers with ' + 'type="recaptcha" are currently supported.'); } }); }; /** * Creates a PhoneAuthCredential. * @param {string} verificationId The ID of the phone number flow, to correlate * this request with a previous call to * PhoneAuthProvider.prototype.verifyPhoneNumber. * @param {string} verificationCode The verification code that was sent to the * user's phone. * @return {!fireauth.PhoneAuthCredential} */ fireauth.PhoneAuthProvider.credential = function(verificationId, verificationCode) { if (!verificationId) { throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_SESSION_INFO); } if (!verificationCode) { throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CODE); } return new fireauth.PhoneAuthCredential({ verification