Skip to content

Commit 2cb9ef5

Browse files
authored
Passwordless (#529)
* passwordless signin added new files for passwordless signin added licence exported function * updated externs and types * [AUTOMATED]: Prettier Code Styling * added passwordless sign in in demo and fixed type * remove email in localstorage after used for passwordless sign in
1 parent b949d81 commit 2cb9ef5

19 files changed

+2099
-67
lines changed

packages/auth-types/index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export interface ApplicationVerifier {
9696

9797
export interface AuthCredential {
9898
providerId: string;
99+
signInMethod: string;
99100
}
100101

101102
export interface AuthProvider {
@@ -109,7 +110,10 @@ export interface ConfirmationResult {
109110

110111
export class EmailAuthProvider extends EmailAuthProvider_Instance {
111112
static PROVIDER_ID: string;
113+
static EMAIL_PASSWORD_SIGN_IN_METHOD: string;
114+
static EMAIL_LINK_SIGN_IN_METHOD: string;
112115
static credential(email: string, password: string): AuthCredential;
116+
static credentialWithLink(email: string, emailLink: string): AuthCredential;
113117
}
114118
export class EmailAuthProvider_Instance implements AuthProvider {
115119
providerId: string;
@@ -122,6 +126,7 @@ export interface Error {
122126

123127
export class FacebookAuthProvider extends FacebookAuthProvider_Instance {
124128
static PROVIDER_ID: string;
129+
static FACEBOOK_SIGN_IN_METHOD: string;
125130
static credential(token: string): AuthCredential;
126131
}
127132
export class FacebookAuthProvider_Instance implements AuthProvider {
@@ -132,6 +137,7 @@ export class FacebookAuthProvider_Instance implements AuthProvider {
132137

133138
export class GithubAuthProvider extends GithubAuthProvider_Instance {
134139
static PROVIDER_ID: string;
140+
static GITHUB_SIGN_IN_METHOD: string;
135141
static credential(token: string): AuthCredential;
136142
}
137143
export class GithubAuthProvider_Instance implements AuthProvider {
@@ -142,6 +148,7 @@ export class GithubAuthProvider_Instance implements AuthProvider {
142148

143149
export class GoogleAuthProvider extends GoogleAuthProvider_Instance {
144150
static PROVIDER_ID: string;
151+
static GOOGLE_SIGN_IN_METHOD: string;
145152
static credential(
146153
idToken?: string | null,
147154
accessToken?: string | null
@@ -162,6 +169,7 @@ export class OAuthProvider implements AuthProvider {
162169

163170
export class PhoneAuthProvider extends PhoneAuthProvider_Instance {
164171
static PROVIDER_ID: string;
172+
static PHONE_SIGN_IN_METHOD: string;
165173
static credential(
166174
verificationId: string,
167175
verificationCode: string
@@ -191,6 +199,7 @@ export class RecaptchaVerifier_Instance implements ApplicationVerifier {
191199

192200
export class TwitterAuthProvider extends TwitterAuthProvider_Instance {
193201
static PROVIDER_ID: string;
202+
static TWITTER_SIGN_IN_METHOD: string;
194203
static credential(token: string, secret: string): AuthCredential;
195204
}
196205
export class TwitterAuthProvider_Instance implements AuthProvider {
@@ -238,6 +247,8 @@ export class FirebaseAuth {
238247
): Promise<any>;
239248
currentUser: User | null;
240249
fetchProvidersForEmail(email: string): Promise<any>;
250+
fetchSignInMethodsForEmail(email: string): Promise<any>;
251+
isSignInWithEmailLink(emailLink: string): boolean;
241252
getRedirectResult(): Promise<any>;
242253
languageCode: string | null;
243254
onAuthStateChanged(
@@ -250,6 +261,10 @@ export class FirebaseAuth {
250261
error?: (a: Error) => any,
251262
completed?: Unsubscribe
252263
): Unsubscribe;
264+
sendSignInLinkToEmail(
265+
email: string,
266+
actionCodeSettings: ActionCodeSettings
267+
): Promise<any>;
253268
sendPasswordResetEmail(
254269
email: string,
255270
actionCodeSettings?: ActionCodeSettings | null
@@ -266,6 +281,7 @@ export class FirebaseAuth {
266281
email: string,
267282
password: string
268283
): Promise<any>;
284+
signInWithEmailLink(email: string, emailLink?: string): Promise<any>;
269285
signInWithPhoneNumber(
270286
phoneNumber: string,
271287
applicationVerifier: ApplicationVerifier

packages/auth/demo/public/index.html

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,29 @@
254254
</button>
255255
</form>
256256

257+
<!-- Sign in with email link -->
258+
<div class="group">Sign In with Email Link</div>
259+
<form class="form form-bordered no-submit">
260+
<input type="email" name="email" id="sign-in-with-email-link-email"
261+
class="form-control" placeholder="Email" />
262+
<button class="btn btn-block btn-primary"
263+
id="send-sign-in-link-to-email">
264+
Send Sign In With Email Link
265+
</button>
266+
<button class="btn btn-block btn-primary"
267+
id="send-sign-in-link-to-email-current-url">
268+
Send Sign In With Email Link Current URL
269+
</button>
270+
</form>
271+
<form class="form form-bordered no-submit">
272+
<input type="text" name="emailLink" id="sign-in-with-email-link-link"
273+
class="form-control" placeholder="Link" />
274+
<button class="btn btn-block btn-primary"
275+
id="sign-in-with-email-link">
276+
Sign In With Email Link
277+
</button>
278+
</form>
279+
257280
<!-- Password Reset Process -->
258281
<div class="group">Password Reset</div>
259282
<form class="form form-bordered no-submit">
@@ -280,6 +303,20 @@
280303
Confirm Password Change
281304
</button>
282305
</form>
306+
<!-- Fetch Sign In Methods/Providers -->
307+
<div class="group">Fetch Sign In Methods/Providers</div>
308+
<form class="form form-bordered no-submit">
309+
<input type="email" name="email" id="fetch-providers-email"
310+
class="form-control" placeholder="Email" />
311+
<button class="btn btn-block btn-primary"
312+
id="fetch-sign-in-methods-for-email">
313+
Fetch Sign In Methods For Email
314+
</button>
315+
<button class="btn btn-block btn-primary"
316+
id="fetch-providers-for-email">
317+
Fetch Providers For Email
318+
</button>
319+
</form>
283320
</div>
284321
<div class="tab-pane" id="user-section">
285322
<!-- Profile updates -->
@@ -359,6 +396,21 @@
359396
</button>
360397
</form>
361398

399+
<form class="form form-bordered no-submit">
400+
<input type="email" name="email" id="link-with-email-link-email"
401+
class="form-control" placeholder="Email" />
402+
<button class="btn btn-block btn-primary"
403+
id="send-link-email-link">
404+
Send Email Link To Link Or Reauthenticate User
405+
</button>
406+
<input type="text" name="emailLink" id="link-with-email-link-link"
407+
class="form-control" placeholder="Link" />
408+
<button class="btn btn-block btn-primary"
409+
id="link-with-email-link">Link With Email Link</button>
410+
<button class="btn btn-block btn-primary"
411+
id="reauth-with-email-link">Reauthenticate With Email Link</button>
412+
</form>
413+
362414
<form class="form form-bordered no-submit">
363415
<input type="text" id="unlinked-provider-id" class="form-control"
364416
placeholder="Provider ID" list="provider-id-options" />

packages/auth/demo/public/script.js

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,49 @@ function onSignInWithEmailAndPassword() {
312312
}
313313

314314

315+
/**
316+
* Signs in a user with an email link.
317+
*/
318+
function onSignInWithEmailLink() {
319+
var email = $('#sign-in-with-email-link-email').val();
320+
var link = $('#sign-in-with-email-link-link').val() || undefined;
321+
if (auth.isSignInWithEmailLink(link)) {
322+
auth.signInWithEmailLink(email, link).then(onAuthSuccess, onAuthError);
323+
} else {
324+
alertError('Sign in link is invalid');
325+
}
326+
}
327+
328+
/**
329+
* Links a user with an email link.
330+
*/
331+
function onLinkWithEmailLink() {
332+
var email = $('#link-with-email-link-email').val();
333+
var link = $('#link-with-email-link-link').val() || undefined;
334+
var credential = firebase.auth.EmailAuthProvider
335+
.credentialWithLink(email, link);
336+
activeUser().linkAndRetrieveDataWithCredential(credential)
337+
.then(onAuthUserCredentialSuccess, onAuthError);
338+
}
339+
340+
341+
/**
342+
* Re-authenticate a user with email link credential.
343+
*/
344+
function onReauthenticateWithEmailLink() {
345+
var email = $('#link-with-email-link-email').val();
346+
var link = $('#link-with-email-link-link').val() || undefined;
347+
var credential = firebase.auth.EmailAuthProvider
348+
.credentialWithLink(email, link);
349+
activeUser().reauthenticateAndRetrieveDataWithCredential(credential)
350+
.then(function(result) {
351+
logAdditionalUserInfo(result);
352+
refreshUserData();
353+
alertSuccess('User reauthenticated!');
354+
}, onAuthError);
355+
}
356+
357+
315358
/**
316359
* Signs in with a custom token.
317360
* @param {DOMEvent} event HTML DOM event returned by the listener.
@@ -581,6 +624,52 @@ function onUpdateProfile() {
581624
}
582625

583626

627+
/**
628+
* Sends sign in with email link to the user.
629+
*/
630+
function onSendSignInLinkToEmail() {
631+
var email = $('#sign-in-with-email-link-email').val();
632+
auth.sendSignInLinkToEmail(email, getActionCodeSettings()).then(function() {
633+
alertSuccess('Email sent!');
634+
}, onAuthError);
635+
}
636+
637+
/**
638+
* Sends sign in with email link to the user and pass in current url.
639+
*/
640+
function onSendSignInLinkToEmailCurrentUrl() {
641+
var email = $('#sign-in-with-email-link-email').val();
642+
var actionCodeSettings = {
643+
'url': window.location.href,
644+
'handleCodeInApp': true
645+
};
646+
647+
auth.sendSignInLinkToEmail(email, actionCodeSettings).then(function() {
648+
if ('localStorage' in window && window['localStorage'] !== null) {
649+
window.localStorage.setItem(
650+
'emailForSignIn',
651+
// Save the email and the timestamp.
652+
JSON.stringify({
653+
email: email,
654+
timestamp: new Date().getTime()
655+
}));
656+
}
657+
alertSuccess('Email sent!');
658+
}, onAuthError);
659+
}
660+
661+
662+
/**
663+
* Sends email link to link the user.
664+
*/
665+
function onSendLinkEmailLink() {
666+
var email = $('#link-with-email-link-email').val();
667+
auth.sendSignInLinkToEmail(email, getActionCodeSettings()).then(function() {
668+
alertSuccess('Email sent!');
669+
}, onAuthError);
670+
}
671+
672+
584673
/**
585674
* Sends password reset email to the user.
586675
*/
@@ -615,6 +704,41 @@ function onConfirmPasswordReset() {
615704
}
616705

617706

707+
/**
708+
* Gets the list of IDPs that can be used to log in for the given email address.
709+
*/
710+
function onFetchProvidersForEmail() {
711+
var email = $('#fetch-providers-email').val();
712+
auth.fetchProvidersForEmail(email).then(function(providers) {
713+
log('Providers for ' + email + ' :');
714+
log(providers);
715+
if (providers.length == 0) {
716+
alertSuccess('Providers for ' + email + ': N/A');
717+
} else {
718+
alertSuccess('Providers for ' + email +': ' + providers.join(', '));
719+
}
720+
}, onAuthError);
721+
}
722+
723+
724+
/**
725+
* Gets the list of possible sign in methods for the given email address.
726+
*/
727+
function onFetchSignInMethodsForEmail() {
728+
var email = $('#fetch-providers-email').val();
729+
auth.fetchSignInMethodsForEmail(email).then(function(signInMethods) {
730+
log('Sign in methods for ' + email + ' :');
731+
log(signInMethods);
732+
if (signInMethods.length == 0) {
733+
alertSuccess('Sign In Methods for ' + email + ': N/A');
734+
} else {
735+
alertSuccess(
736+
'Sign In Methods for ' + email +': ' + signInMethods.join(', '));
737+
}
738+
}, onAuthError);
739+
}
740+
741+
618742
/**
619743
* Fetches and logs the user's providers data.
620744
*/
@@ -966,13 +1090,43 @@ function getParameterByName(name) {
9661090
* the input field for the confirm email verification process.
9671091
*/
9681092
function populateActionCodes() {
1093+
var emailForSignIn = null;
1094+
var signInTime = 0;
1095+
if ('localStorage' in window && window['localStorage'] !== null) {
1096+
try {
1097+
// Try to parse as JSON first using new storage format.
1098+
var emailForSignInData =
1099+
JSON.parse(window.localStorage.getItem('emailForSignIn'));
1100+
emailForSignIn = emailForSignInData['email'] || null;
1101+
signInTime = emailForSignInData['timestamp'] || 0;
1102+
} catch (e) {
1103+
// JSON parsing failed. This means the email is stored in the old string
1104+
// format.
1105+
emailForSignIn = window.localStorage.getItem('emailForSignIn');
1106+
}
1107+
if (emailForSignIn) {
1108+
// Clear old codes. Old format codes should be cleared immediately.
1109+
if (new Date().getTime() - signInTime >= 1 * 24 * 3600 * 1000) {
1110+
// Remove email from storage.
1111+
window.localStorage.removeItem('emailForSignIn');
1112+
}
1113+
}
1114+
}
9691115
var actionCode = getParameterByName('oobCode');
9701116
if (actionCode != null) {
9711117
var mode = getParameterByName('mode');
9721118
if (mode == 'verifyEmail') {
9731119
$('#email-verification-code').val(actionCode);
9741120
} else if (mode == 'resetPassword') {
9751121
$('#password-reset-code').val(actionCode);
1122+
} else if (mode == 'signIn') {
1123+
if (emailForSignIn) {
1124+
$('#sign-in-with-email-link-email').val(emailForSignIn);
1125+
$('#sign-in-with-email-link-link').val(window.location.href);
1126+
onSignInWithEmailLink();
1127+
// Remove email from storage as the code is only usable once.
1128+
window.localStorage.removeItem('emailForSignIn');
1129+
}
9761130
} else {
9771131
$('#email-verification-code').val(actionCode);
9781132
$('#password-reset-code').val(actionCode);
@@ -1153,11 +1307,19 @@ function initApp(){
11531307
e.preventDefault();
11541308
}
11551309
});
1310+
$('#sign-in-with-email-link').click(onSignInWithEmailLink);
1311+
$('#link-with-email-link').click(onLinkWithEmailLink);
1312+
$('#reauth-with-email-link').click(onReauthenticateWithEmailLink);
11561313

11571314
$('#change-email').click(onChangeEmail);
11581315
$('#change-password').click(onChangePassword);
11591316
$('#update-profile').click(onUpdateProfile);
11601317

1318+
$('#send-sign-in-link-to-email').click(onSendSignInLinkToEmail);
1319+
$('#send-sign-in-link-to-email-current-url')
1320+
.click(onSendSignInLinkToEmailCurrentUrl);
1321+
$('#send-link-email-link').click(onSendLinkEmailLink);
1322+
11611323
$('#send-password-reset-email').click(onSendPasswordResetEmail);
11621324
$('#verify-password-reset-code').click(onVerifyPasswordResetCode);
11631325
$('#confirm-password-reset').click(onConfirmPasswordReset);
@@ -1202,6 +1364,9 @@ function initApp(){
12021364

12031365
$('#set-language-code').click(onSetLanguageCode);
12041366
$('#use-device-language').click(onUseDeviceLanguage);
1367+
1368+
$('#fetch-providers-for-email').click(onFetchProvidersForEmail);
1369+
$('#fetch-sign-in-methods-for-email').click(onFetchSignInMethodsForEmail);
12051370
}
12061371

12071372
$(initApp);

0 commit comments

Comments
 (0)