Skip to content

Passwordless #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 15, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/auth-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface ApplicationVerifier {

export interface AuthCredential {
providerId: string;
signInMethod: string;
}

export interface AuthProvider {
Expand All @@ -109,7 +110,10 @@ export interface ConfirmationResult {

export class EmailAuthProvider extends EmailAuthProvider_Instance {
static PROVIDER_ID: string;
static EMAIL_PASSWORD_SIGN_IN_METHOD: string;
static EMAIL_LINK_SIGN_IN_METHOD: string;
static credential(email: string, password: string): AuthCredential;
static credentialWithLink(email: string, emailLink: string): AuthCredential;
}
export class EmailAuthProvider_Instance implements AuthProvider {
providerId: string;
Expand All @@ -122,6 +126,7 @@ export interface Error {

export class FacebookAuthProvider extends FacebookAuthProvider_Instance {
static PROVIDER_ID: string;
static FACEBOOK_SIGN_IN_METHOD: string;
static credential(token: string): AuthCredential;
}
export class FacebookAuthProvider_Instance implements AuthProvider {
Expand All @@ -132,6 +137,7 @@ export class FacebookAuthProvider_Instance implements AuthProvider {

export class GithubAuthProvider extends GithubAuthProvider_Instance {
static PROVIDER_ID: string;
static GITHUB_SIGN_IN_METHOD: string;
static credential(token: string): AuthCredential;
}
export class GithubAuthProvider_Instance implements AuthProvider {
Expand All @@ -142,6 +148,7 @@ export class GithubAuthProvider_Instance implements AuthProvider {

export class GoogleAuthProvider extends GoogleAuthProvider_Instance {
static PROVIDER_ID: string;
static GOOGLE_SIGN_IN_METHOD: string;
static credential(
idToken?: string | null,
accessToken?: string | null
Expand All @@ -162,6 +169,7 @@ export class OAuthProvider implements AuthProvider {

export class PhoneAuthProvider extends PhoneAuthProvider_Instance {
static PROVIDER_ID: string;
static PHONE_SIGN_IN_METHOD: string;
static credential(
verificationId: string,
verificationCode: string
Expand Down Expand Up @@ -191,6 +199,7 @@ export class RecaptchaVerifier_Instance implements ApplicationVerifier {

export class TwitterAuthProvider extends TwitterAuthProvider_Instance {
static PROVIDER_ID: string;
static TWITTER_SIGN_IN_METHOD: string;
static credential(token: string, secret: string): AuthCredential;
}
export class TwitterAuthProvider_Instance implements AuthProvider {
Expand Down Expand Up @@ -238,6 +247,8 @@ export class FirebaseAuth {
): Promise<any>;
currentUser: User | null;
fetchProvidersForEmail(email: string): Promise<any>;
fetchSignInMethodsForEmail(email: string): Promise<any>;
isSignInWithEmailLink(emailLink: string): boolean;
getRedirectResult(): Promise<any>;
languageCode: string | null;
onAuthStateChanged(
Expand All @@ -250,6 +261,10 @@ export class FirebaseAuth {
error?: (a: Error) => any,
completed?: Unsubscribe
): Unsubscribe;
sendSignInLinkToEmail(
email: string,
actionCodeSettings: ActionCodeSettings
): Promise<any>;
sendPasswordResetEmail(
email: string,
actionCodeSettings?: ActionCodeSettings | null
Expand All @@ -266,6 +281,7 @@ export class FirebaseAuth {
email: string,
password: string
): Promise<any>;
signInWithEmailLink(email: string, emailLink: string): Promise<any>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emailLink is optional here. Change to:
signInWithEmailLink(email: string, emailLink?: string): Promise<any>;

signInWithPhoneNumber(
phoneNumber: string,
applicationVerifier: ApplicationVerifier
Expand Down
19 changes: 17 additions & 2 deletions packages/auth/src/actioncodeinfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ fireauth.ActionCodeInfo = function(response) {
var newEmail = response[fireauth.ActionCodeInfo.ServerFieldName.NEW_EMAIL];
var operation =
response[fireauth.ActionCodeInfo.ServerFieldName.REQUEST_TYPE];
if (!email || !operation) {
// Email could be empty only if the request type is EMAIL_SIGNIN.
if (!operation ||
(operation != fireauth.ActionCodeInfo.RequestType.EMAIL_SIGNIN &&
!email)) {
// This is internal only.
throw new Error('Invalid provider user info!');
}
data[fireauth.ActionCodeInfo.DataField.FROM_EMAIL] = newEmail || null;
data[fireauth.ActionCodeInfo.DataField.EMAIL] = email;
data[fireauth.ActionCodeInfo.DataField.EMAIL] = email || null;
fireauth.object.setReadonlyProperty(
this,
fireauth.ActionCodeInfo.PropertyName.OPERATION,
Expand All @@ -58,6 +61,18 @@ fireauth.ActionCodeInfo = function(response) {
};


/**
* Firebase Auth Action Code Info requestType possible values.
* @enum {string}
*/
fireauth.ActionCodeInfo.RequestType = {
PASSWORD_RESET: 'PASSWORD_RESET',
RECOVER_EMAIL: 'RECOVER_EMAIL',
EMAIL_SIGNIN: 'EMAIL_SIGNIN',
VERIFY_EMAIL: 'VERIFY_EMAIL'
};


/**
* The checkActionCode endpoint server response field names.
* @enum {string}
Expand Down
16 changes: 9 additions & 7 deletions packages/auth/src/actioncodesettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,6 @@ fireauth.ActionCodeSettings.prototype.initialize_ = function(settingsObj) {
}
/** @private {boolean} Whether the code can be handled in app. */
this.canHandleCodeInApp_ = !!canHandleCodeInApp;
// canHandleCodeInApp can't be true when no mobile application is provided.
if (this.canHandleCodeInApp_ && !this.ibi_ && !this.apn_) {
throw new fireauth.AuthError(
fireauth.authenum.Error.ARGUMENT_ERROR,
fireauth.ActionCodeSettings.RawField.HANDLE_CODE_IN_APP +
' property can\'t be true when no mobile application is provided.');
}
};


Expand Down Expand Up @@ -227,3 +220,12 @@ fireauth.ActionCodeSettings.prototype.buildRequest = function() {
}
return request;
};


/**
* Returns the canHandleCodeInApp setting of ActionCodeSettings.
* @return {boolean} Whether the code can be handled in app.
*/
fireauth.ActionCodeSettings.prototype.canHandleCodeInApp = function() {
return this.canHandleCodeInApp_;
};
92 changes: 92 additions & 0 deletions packages/auth/src/actioncodeurl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* 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 firebase.auth.ActionCodeUrl class which is the utility
* to parse action code URLs.
*/

goog.provide('fireauth.ActionCodeUrl');

goog.require('goog.Uri');


/**
* The utility class to help parse action code URLs used for out of band email
* flows such as password reset, email verification, email link sign in, etc.
* @param {string} actionCodeUrl The action code URL.
* @constructor
*/
fireauth.ActionCodeUrl = function(actionCodeUrl) {
/** @private {!goog.Uri} The action code URL components.*/
this.uri_ = goog.Uri.parse(actionCodeUrl);
};


/**
* Enums for fields in URL query string.
* @enum {string}
*/
fireauth.ActionCodeUrl.QueryField = {
API_KEY: 'apiKey',
CODE: 'oobCode',
MODE: 'mode'
};


/**
* Enums for action code modes.
* @enum {string}
*/
fireauth.ActionCodeUrl.Mode = {
RESET_PASSWORD: 'resetPassword',
REVOKE_EMAIL: 'recoverEmail',
SIGN_IN: 'signIn',
VERIFY_EMAIL: 'verifyEmail'
};


/**
* Returns the API key parameter of action code URL.
* @return {?string} The first API key value in action code URL or
* undefined if apiKey does not appear in the URL.
*/
fireauth.ActionCodeUrl.prototype.getApiKey = function() {
return this.uri_.getParameterValue(
fireauth.ActionCodeUrl.QueryField.API_KEY) || null;
};


/**
* Returns the action code parameter of action code URL.
* @return {?string} The first oobCode value in action code URL or
* undefined if oobCode does not appear in the URL.
*/
fireauth.ActionCodeUrl.prototype.getCode = function() {
return this.uri_.getParameterValue(
fireauth.ActionCodeUrl.QueryField.CODE) || null;
};


/**
* Returns the mode parameter of action code URL.
* @return {?string} The first mode value in action code URL or
* undefined if mode does not appear in the URL.
*/
fireauth.ActionCodeUrl.prototype.getMode = function() {
return this.uri_.getParameterValue(
fireauth.ActionCodeUrl.QueryField.MODE) || null;
};
75 changes: 75 additions & 0 deletions packages/auth/src/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ goog.require('fireauth.AuthEventManager');
goog.require('fireauth.AuthProvider');
goog.require('fireauth.AuthUser');
goog.require('fireauth.ConfirmationResult');
goog.require('fireauth.EmailAuthProvider');
goog.require('fireauth.RpcHandler');
goog.require('fireauth.UserEventType');
goog.require('fireauth.authenum.Error');
Expand Down Expand Up @@ -1823,6 +1824,60 @@ fireauth.Auth.prototype.fetchProvidersForEmail = function(email) {
};


/**
* Gets the list of possible sign in methods for the given email address.
* @param {string} email The email address.
* @return {!goog.Promise<!Array<string>>}
*/
fireauth.Auth.prototype.fetchSignInMethodsForEmail = function(email) {
return /** @type {!goog.Promise<!Array<string>>} */ (
this.registerPendingPromise_(
this.getRpcHandler().fetchSignInMethodsForIdentifier(email)));
};


/**
* @param {string} emailLink The email link.
* @return {boolean} Whether the link is a sign in with email link.
*/
fireauth.Auth.prototype.isSignInWithEmailLink = function(emailLink) {
return !!fireauth.EmailAuthProvider
.getActionCodeFromSignInEmailLink(emailLink);
};


/**
* Sends the sign-in with email link for the email account provided.
* @param {string} email The email account to sign in with.
* @param {!Object} actionCodeSettings The action code settings object.
* @return {!goog.Promise<void>}
*/
fireauth.Auth.prototype.sendSignInLinkToEmail = function(
email, actionCodeSettings) {
var self = this;
return this.registerPendingPromise_(
// Wrap in promise as ActionCodeSettings constructor could throw a
// synchronous error if invalid arguments are specified.
goog.Promise.resolve()
.then(function() {
var actionCodeSettingsBuilder =
new fireauth.ActionCodeSettings(actionCodeSettings);
if (!actionCodeSettingsBuilder.canHandleCodeInApp()) {
throw new fireauth.AuthError(
fireauth.authenum.Error.ARGUMENT_ERROR,
fireauth.ActionCodeSettings.RawField.HANDLE_CODE_IN_APP +
' must be true when sending sign in link to email');
}
return actionCodeSettingsBuilder.buildRequest();
}).then(function(additionalRequestData) {
return self.getRpcHandler().sendSignInLinkToEmail(
email, additionalRequestData);
}).then(function(email) {
// Do not return the email.
}));
};


/**
* Verifies an email action code for password reset and returns a promise
* that resolves with the associated email if verified.
Expand Down Expand Up @@ -1930,3 +1985,23 @@ fireauth.Auth.prototype.signInWithPhoneNumber =
// This will wait for redirectStateIsReady to resolve first.
goog.bind(this.signInAndRetrieveDataWithCredential, this))));
};


/**
* Signs in a Firebase User with the provided email and the passwordless
* sign-in email link.
* @param {string} email The email account to sign in with.
* @param {?string=} opt_link The optional link which contains the OTP needed
* to complete the sign in with email link. If not specified, the current
* URL is used instead.
* @return {!goog.Promise<!fireauth.AuthEventManager.Result>}
*/
fireauth.Auth.prototype.signInWithEmailLink = function(email, opt_link) {
var self = this;
return this.registerPendingPromise_(
goog.Promise.resolve().then(function() {
var credential = fireauth.EmailAuthProvider.credentialWithLink(
email, opt_link || fireauth.util.getCurrentUrl());
return self.signInAndRetrieveDataWithCredential(credential);
}));
};
Loading