ez-firebase-auth
Version:
Easy Firebase Auth for Angular 4.x
1,658 lines (1,610 loc) • 121 kB
JavaScript
import { Component, Directive, ElementRef, Injectable, Input, NgModule, Renderer2 } from '@angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { BehaviorSubject as BehaviorSubject$1 } from 'rxjs/BehaviorSubject';
import { Subject as Subject$1 } from 'rxjs/Subject';
import { auth as auth$1 } from 'firebase';
import { assign, compact, each, filter, find, has, includes, isArray, map, omit, trim, uniqueId } from 'lodash';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/takeUntil';
import { isEmail } from 'validator';
import 'rxjs/add/operator/combineLatest';
import 'rxjs/add/operator/debounceTime';
class EzfaEmailChangedEvent {
/**
* @param {?} user
* @param {?} oldEmail
* @param {?} newEmail
*/
constructor(user, oldEmail, newEmail) {
this.user = user;
this.oldEmail = oldEmail;
this.newEmail = newEmail;
}
}
EzfaEmailChangedEvent.type = 'EzfaEmailChangedEvent';
let EzfaOauthMethod = {};
EzfaOauthMethod.popup = 1;
EzfaOauthMethod.redirect = 2;
EzfaOauthMethod[EzfaOauthMethod.popup] = "popup";
EzfaOauthMethod[EzfaOauthMethod.redirect] = "redirect";
class EzfaOptions {
constructor() {
this.providerIds = [];
this.providers = [];
this.requireDisplayName = true;
this.requireTos = true;
this.sendEmailVerificationLink = true;
this.oauthMethod = EzfaOauthMethod.redirect;
}
}
EzfaOptions.decorators = [
{ type: Injectable },
];
/**
* @nocollapse
*/
EzfaOptions.ctorParameters = () => [];
class EzfaProviderLabels {
constructor() {
this['password'] = 'Email/Password';
this['twitter.com'] = 'Twitter';
this['facebook.com'] = 'Facebook';
this['github.com'] = 'GitHub';
this['google.com'] = 'Google';
}
}
class EzfaProviderLinkedEvent {
/**
* @param {?} user
* @param {?} providerId
* @param {?=} credential
*/
constructor(user, providerId, credential = null) {
this.user = user;
this.providerId = providerId;
this.credential = credential;
}
}
EzfaProviderLinkedEvent.type = 'EzfaProviderLinkedEvent';
class EzfaProviderUnlinkedEvent {
/**
* @param {?} user
* @param {?} providerId
*/
constructor(user, providerId) {
this.user = user;
this.providerId = providerId;
}
}
EzfaProviderUnlinkedEvent.type = 'EzfaProviderUnlinkedEvent';
class EzfaSignedInEvent {
/**
* @param {?} user
* @param {?} providerId
* @param {?=} credential
*/
constructor(user, providerId, credential = null) {
this.user = user;
this.providerId = providerId;
this.credential = credential;
this.redirectCancelled = false;
}
}
EzfaSignedInEvent.type = 'EzfaSignedInEvent';
class EzfaSignedOutEvent {
constructor() {
this.redirectCancelled = false;
}
}
EzfaSignedOutEvent.type = 'EzfaSignedOutEvent';
class EzfaService {
/**
* @param {?} router
* @param {?} afAuth
* @param {?} options
*/
constructor(router$$1, afAuth, options) {
this.router = router$$1;
this.afAuth = afAuth;
this.options = options;
this.savedPopupPromise_ = null;
const initialMethod = options.oauthMethod === EzfaOauthMethod.popup ? EzfaOauthMethod.popup : EzfaOauthMethod.redirect;
this.oauthMethod$ = new BehaviorSubject$1(initialMethod);
const localPersEnabled = localStorage.getItem(EzfaService.STORAGE_KEY_PERSISTENCE) === 'yes' ? false : true;
this.localPersistenceEnabled$ = new BehaviorSubject$1(localPersEnabled);
this.routeChanges$ = new Subject$1();
this.signedInEvents$ = new Subject$1();
this.signedOutEvents$ = new Subject$1();
this.emailChangedEvents$ = new Subject$1();
this.providerLinkedEvents$ = new Subject$1();
this.providerUnlinkedEvents$ = new Subject$1();
}
/**
* @return {?}
*/
get auth() {
return this.afAuth.auth;
}
/**
* @return {?}
*/
get authState() {
return this.afAuth.authState;
}
/**
* @return {?}
*/
get applicationLabel() {
return this.options.applicationLabel;
}
/**
* @return {?}
*/
get rootSlug() {
return this.options.rootSlug;
}
/**
* @return {?}
*/
get providerIds() {
return filter(this.options.providerIds, id => {
return includes(EzfaService.ENABLED_PROVIDERS, id);
});
}
/**
* @param {?} ids
* @return {?}
*/
set providerIds(ids) {
this.options.providerIds = ids;
}
/**
* @return {?}
*/
get oauthProviderIds() {
return filter(this.providerIds, id => {
return includes(EzfaService.ENABLED_OAUTH_PROVIDERS, id);
});
}
/**
* @return {?}
*/
get passwordProviderEnabled() {
return includes(this.providerIds, 'password');
}
/**
* @return {?}
*/
get providerLabels() {
const /** @type {?} */ passed = this.options.providerLabels || {};
const /** @type {?} */ def = new EzfaProviderLabels();
return assign({}, def, passed);
}
/**
* @return {?}
*/
get requireDisplayName() {
return this.options.requireDisplayName !== false;
}
/**
* @param {?} b
* @return {?}
*/
set requireDisplayName(b) {
this.options.requireDisplayName = b;
}
/**
* @return {?}
*/
get requireTos() {
return this.options.requireTos !== false;
}
/**
* @param {?} b
* @return {?}
*/
set requireTos(b) {
this.options.requireTos = b;
}
/**
* @return {?}
*/
get sendEmailVerificationLink() {
return this.options.sendEmailVerificationLink !== false;
}
/**
* @param {?} b
* @return {?}
*/
set sendEmailVerificationLink(b) {
this.options.sendEmailVerificationLink = b;
}
/**
* @return {?}
*/
get oauthMethod() {
return this.oauthMethod$.value;
}
/**
* @param {?} method
* @return {?}
*/
set oauthMethod(method) {
this.oauthMethod$.next(method);
}
/**
* @param {?} id
* @return {?}
*/
getProviderById(id) {
return new Promise((resolve, reject) => {
if (!includes(this.providerIds, id)) {
return reject({ code: 'ezfa/provider-not-configured' });
}
const /** @type {?} */ customProvider = find(this.options.providers, { providerId: id });
if (customProvider) {
return resolve(customProvider);
}
switch (id) {
case 'password': return resolve(new auth$1.EmailAuthProvider());
case 'twitter.com': return resolve(new auth$1.TwitterAuthProvider());
case 'facebook.com': return resolve(new auth$1.FacebookAuthProvider());
case 'google.com': return resolve(new auth$1.GoogleAuthProvider());
case 'github.com': return resolve(new auth$1.GithubAuthProvider());
}
});
}
/**
* @param {?=} slug
* @return {?}
*/
routerLink(slug) {
const /** @type {?} */ commands = ['/' + this.rootSlug];
if (slug) {
commands.push(slug);
}
return commands;
}
/**
* @param {?=} slug
* @param {?=} extras
* @return {?}
*/
navigate(slug, extras) {
return this.router.navigate(this.routerLink(slug), extras);
}
/**
* @return {?}
*/
get localPersistenceEnabled() {
return this.localPersistenceEnabled$.asObservable();
}
/**
* @param {?} b
* @return {?}
*/
setPersistenceLocal(b) {
return new Promise((resolve) => {
const /** @type {?} */ persistence = b ?
auth$1.Auth.Persistence.LOCAL : auth$1.Auth.Persistence.SESSION;
this.auth.setPersistence(persistence)
.then(() => {
if (b) {
localStorage.removeItem(EzfaService.STORAGE_KEY_PERSISTENCE);
}
else {
localStorage.setItem(EzfaService.STORAGE_KEY_PERSISTENCE, 'yes');
}
this.localPersistenceEnabled$.next(b);
resolve();
});
});
}
/**
* @return {?}
*/
get routeChanges() {
return this.routeChanges$.asObservable();
}
/**
* @param {?} route
* @return {?}
*/
onRouteChange(route) {
this.routeChanges$.next(route);
}
/**
* @return {?}
*/
get signedInEvents() {
return this.signedInEvents$.asObservable();
}
/**
* @param {?} event
* @return {?}
*/
onSignedIn(event) {
this.signedInEvents$.next(event);
}
/**
* @return {?}
*/
get signedOutEvents() {
return this.signedOutEvents$.asObservable();
}
/**
* @param {?} event
* @return {?}
*/
onSignedOut(event) {
this.signedOutEvents$.next(event);
}
/**
* @return {?}
*/
get emailChangedEvents() {
return this.emailChangedEvents$.asObservable();
}
/**
* @param {?} event
* @return {?}
*/
onEmailChanged(event) {
this.emailChangedEvents$.next(event);
}
/**
* @return {?}
*/
get providerLinkedEvents() {
return this.providerLinkedEvents$.asObservable();
}
/**
* @param {?} event
* @return {?}
*/
onProviderLinked(event) {
this.providerLinkedEvents$.next(event);
}
/**
* @return {?}
*/
get providerUnlinkedEvents() {
return this.providerUnlinkedEvents$.asObservable();
}
/**
* @param {?} event
* @return {?}
*/
onProviderUnlinked(event) {
this.providerUnlinkedEvents$.next(event);
}
/**
* @return {?}
*/
get savedPopupPromise() {
return this.savedPopupPromise_;
}
/**
* @param {?} promise
* @return {?}
*/
set savedPopupPromise(promise) {
this.savedPopupPromise_ = promise;
}
}
EzfaService.STORAGE_KEY_PERSISTENCE = 'ezfa-local-persistence-disabled';
EzfaService.ENABLED_PROVIDERS = ['password', 'twitter.com', 'facebook.com', 'google.com', 'github.com'];
EzfaService.ENABLED_OAUTH_PROVIDERS = ['twitter.com', 'facebook.com', 'google.com', 'github.com'];
EzfaService.OUT_OF_BAND_MODES = {
resetPassword: 'resetPassword',
verifyEmail: 'verifyEmail',
recoverEmail: 'recoverEmail'
};
EzfaService.decorators = [
{ type: Injectable },
];
/**
* @nocollapse
*/
EzfaService.ctorParameters = () => [
{ type: Router, },
{ type: AngularFireAuth, },
{ type: EzfaOptions, },
];
class EzfaModule {
}
EzfaModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule,
],
providers: [
EzfaService
],
declarations: []
},] },
];
/**
* @nocollapse
*/
EzfaModule.ctorParameters = () => [];
class UserProviderData {
/**
* @param {?} user
* @param {?} enabled
*/
constructor(user, enabled) {
if (!user) {
this.ids = [];
this.oauthIds = [];
}
else {
const userProviderIds = map(user.providerData, 'providerId');
this.ids = filter(userProviderIds, id => {
return includes(enabled, id);
});
this.oauthIds = filter(userProviderIds, id => {
return includes(enabled, id) && 'password' !== id;
});
}
this.hasPassword = includes(this.ids, 'password');
this.canAddPassword = includes(enabled, 'password') && (!this.hasPassword);
this.canAddOauth = filter(enabled, id => {
return id !== 'password' && (!includes(this.oauthIds, id));
});
}
}
/**
* @abstract
*/
class BaseComponent {
/**
* @param {?} service
*/
constructor(service) {
this.service = service;
this.ngUnsubscribe = new Subject$1();
this.user = null;
}
/**
* @return {?}
*/
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
/**
* @return {?}
*/
onInitLoadUser() {
return new Promise(resolve => {
this.service.authState.take(1).subscribe((firstResult) => {
this.onAuthChangedUpdate(firstResult);
this.service.authState.takeUntil(this.ngUnsubscribe).subscribe((subsequentResult) => {
this.onAuthChangedUpdate(subsequentResult);
});
resolve();
});
});
}
/**
* @return {?}
*/
gateToSignedInUser() {
this.service.authState.takeUntil(this.ngUnsubscribe).subscribe((user) => {
if (!user) {
this.service.navigate();
}
});
}
/**
* @param {?} user
* @return {?}
*/
onAuthChangedUpdate(user) {
this.user = user;
this.userProviderData = new UserProviderData(user, this.service.providerIds);
}
/**
* @return {?}
*/
gateToUserWithNoPassword() {
this.service.authState.takeUntil(this.ngUnsubscribe).subscribe((user) => {
const /** @type {?} */ password = user ? find(user.providerData, { providerId: 'password' }) : null;
if (password || (!user)) {
return this.service.navigate();
}
});
}
/**
* @return {?}
*/
gateToUserWithPassword() {
this.service.authState.takeUntil(this.ngUnsubscribe).subscribe((user) => {
const /** @type {?} */ password = user ? find(user.providerData, { providerId: 'password' }) : null;
if (!password) {
return this.service.navigate();
}
});
}
}
class IndexRouteComponent extends BaseComponent {
/**
* @param {?} route
* @param {?} service
*/
constructor(route, service) {
super(service);
this.route = route;
}
/**
* @return {?}
*/
ngOnInit() {
const /** @type {?} */ oobCode = this.route.snapshot.queryParams.oobCode || null;
const /** @type {?} */ mode = this.route.snapshot.queryParams.mode || null;
if ((!mode) || (!has(EzfaService.OUT_OF_BAND_MODES, mode)) || (!oobCode)) {
this.service.authState.take(1).subscribe((user) => {
if (!user) {
return this.service.navigate('sign-in');
}
else {
return this.service.navigate('account');
}
});
return;
}
switch (mode) {
case EzfaService.OUT_OF_BAND_MODES.recoverEmail:
return this.service.navigate('recover-email', { queryParamsHandling: 'preserve' });
case EzfaService.OUT_OF_BAND_MODES.resetPassword:
return this.service.navigate('reset-password', { queryParamsHandling: 'preserve' });
case EzfaService.OUT_OF_BAND_MODES.verifyEmail:
return this.service.navigate('verify-email', { queryParamsHandling: 'preserve' });
}
}
}
IndexRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-index-route',
template: `
<p class="text-muted">
<i class="fa fa-spin fa-fw fa-refresh"></i> Please wait...
</p>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
IndexRouteComponent.ctorParameters = () => [
{ type: ActivatedRoute, },
{ type: EzfaService, },
];
class AccountRouteComponent extends BaseComponent {
/**
* @param {?} route
* @param {?} service
*/
constructor(route, service) {
super(service);
this.route = route;
this.message = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.service.onRouteChange('account');
this.initMessage();
this.onInitLoadUser()
.then(() => {
this.gateToSignedInUser();
});
}
/**
* @return {?}
*/
initMessage() {
if (!this.route.snapshot.queryParams.message) {
this.message = null;
return;
}
const /** @type {?} */ message = parseInt(this.route.snapshot.queryParams.message, 10);
this.message = isNaN(message) ? null : message;
}
/**
* @param {?} providerId
* @return {?}
*/
addProvider(providerId) {
if ('password' === providerId) {
this.service.navigate('add-password');
return;
}
this.service.navigate('link', { queryParams: { providerId: providerId } });
if (EzfaOauthMethod.popup === this.service.oauthMethod) {
this.service.savedPopupPromise = this.service.getProviderById(providerId)
.then(provider => {
return this.user.linkWithPopup(provider);
});
}
}
}
AccountRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-account',
template: `
<div *ngIf="user">
<div *ngIf="user.displayName">
<h6 class="text-muted mb-1">Name</h6>
<p>{{user.displayName}}</p>
</div>
<h6 class="text-muted mb-1">Email</h6>
<p>
{{user.email}}
<span class="badge badge-success" *ngIf="user.emailVerified">
<i class="fa fa-fw fa-check" aria-hidden="true"></i>
Verified
</span>
<span *ngIf="!user.emailVerified">
<a routerLink="../send-email-verification-link">Verify</a>
</span>
</p>
<h6 class="text-muted mb-1">Sign In Methods</h6>
<div class="mb-3" *ngIf="userProviderData">
<ul class="list-group">
<li *ngIf="userProviderData.hasPassword" class="list-group-item">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted">
<ezfa-provider-icon providerId="password"></ezfa-provider-icon>
</span>
<ezfa-provider-label providerId="password"></ezfa-provider-label>
<br>
<small class="text-muted">
<a routerLink="../change-email">Change Email</a>
|
<a routerLink="../change-password">Change Password</a>
</small>
</div>
<div>
<a
routerLink="../unlink"
[queryParams]="{providerId: 'password'}"
type="button"
*ngIf="userProviderData.ids.length > 1"
class="btn btn-sm btn-outline-light"
[ezfaProviderTitle]="{label: 'Remove', id: 'password'}">
<i class="fa fa-fw fa-times text-danger"></i>
<span class="sr-only">
Remove <ezfa-provider-label providerId="password"></ezfa-provider-label>
</span>
</a>
</div>
</div>
</li>
<li *ngFor="let id of userProviderData.oauthIds" class="list-group-item">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted">
<ezfa-provider-icon [providerId]="id"></ezfa-provider-icon>
</span>
<ezfa-provider-label [providerId]="id"></ezfa-provider-label>
</div>
<div>
<a
routerLink="../unlink"
[queryParams]="{providerId: id}"
*ngIf="userProviderData.ids.length > 1"
class="btn btn-sm btn-outline-light"
[ezfaProviderTitle]="{label: 'Remove', id: id}">
<i class="fa fa-fw fa-times text-danger"></i>
<span class="sr-only">
Remove <ezfa-provider-label [providerId]="id"></ezfa-provider-label>
</span>
</a>
</div>
</div>
</li>
</ul>
<div *ngIf="userProviderData.canAddPassword || userProviderData.canAddOauth.length > 0" >
<small class="text-muted">
Add...
</small>
<div class="list-group">
<a
*ngIf="userProviderData.canAddPassword"
routerLink="../add-password"
class="list-group-item list-group-item-action"
[ezfaProviderTitle]="{label: 'Add', id: 'password'}">
<ezfa-provider-icon providerId="password"></ezfa-provider-icon>
Add
<ezfa-provider-label providerId="password"></ezfa-provider-label>
</a>
<button *ngFor="let id of userProviderData.canAddOauth"
type="button"
class="list-group-item list-group-item-action"
(click)="addProvider(id)"
[ezfaProviderTitle]="{label: 'Add', id: id}">
<ezfa-provider-icon [providerId]="id"></ezfa-provider-icon>
Add
<ezfa-provider-label [providerId]="id"></ezfa-provider-label>
</button>
</div>
</div>
</div>
<ezfa-persistence-form></ezfa-persistence-form>
<p class="text-right mb-0">
<a routerLink="../sign-out">Sign Out</a>
</p>
</div>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
AccountRouteComponent.ctorParameters = () => [
{ type: ActivatedRoute, },
{ type: EzfaService, },
];
class SignInRouteComponent extends BaseComponent {
/**
* @param {?} route
* @param {?} service
*/
constructor(route, service) {
super(service);
this.route = route;
this.email = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.service.onRouteChange('sign-in');
this.email = this.route.snapshot.queryParams.email || '';
this.onInitLoadUser()
.then(() => {
if (this.user) {
this.service.navigate();
}
});
}
}
SignInRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-sign-in-route',
template: `
<div *ngIf="authService.passwordProviderEnabled">
<ezfa-email-sign-in-form [email]="email"></ezfa-email-sign-in-form>
<hr *ngIf="authService.oauthProviderIds.length > 0">
</div>
<div *ngIf="authService.oauthProviderIds.length > 0">
<h6 class="text-muted">
<span *ngIf="!authService.passwordProviderEnabled">Sign in with...</span>
<span *ngIf="authService.passwordProviderEnabled">Or sign in with...</span>
</h6>
<ezfa-oauth-sign-in></ezfa-oauth-sign-in>
</div>
<hr>
<ezfa-persistence-form></ezfa-persistence-form>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
SignInRouteComponent.ctorParameters = () => [
{ type: ActivatedRoute, },
{ type: EzfaService, },
];
let Messages = {};
Messages.emailSaved = 1;
Messages.passwordSaved = 2;
Messages.passwordRemoved = 3;
Messages.twitterAccountRemoved = 4;
Messages.githubAccountRemoved = 5;
Messages.googleAccountRemoved = 6;
Messages.facebookAccountRemoved = 7;
Messages.twitterAccountAdded = 8;
Messages.githubAccountAdded = 9;
Messages.facebookAccountAdded = 10;
Messages.googleAccountAdded = 11;
Messages.emailVerified = 12;
Messages.passwordReset = 13;
Messages.signedIn = 14;
Messages.signedOut = 15;
Messages[Messages.emailSaved] = "emailSaved";
Messages[Messages.passwordSaved] = "passwordSaved";
Messages[Messages.passwordRemoved] = "passwordRemoved";
Messages[Messages.twitterAccountRemoved] = "twitterAccountRemoved";
Messages[Messages.githubAccountRemoved] = "githubAccountRemoved";
Messages[Messages.googleAccountRemoved] = "googleAccountRemoved";
Messages[Messages.facebookAccountRemoved] = "facebookAccountRemoved";
Messages[Messages.twitterAccountAdded] = "twitterAccountAdded";
Messages[Messages.githubAccountAdded] = "githubAccountAdded";
Messages[Messages.facebookAccountAdded] = "facebookAccountAdded";
Messages[Messages.googleAccountAdded] = "googleAccountAdded";
Messages[Messages.emailVerified] = "emailVerified";
Messages[Messages.passwordReset] = "passwordReset";
Messages[Messages.signedIn] = "signedIn";
Messages[Messages.signedOut] = "signedOut";
class SignOutRouteComponent extends BaseComponent {
/**
* @param {?} service
*/
constructor(service) {
super(service);
this.screen = 'wait';
}
/**
* @return {?}
*/
ngOnInit() {
this.service.onRouteChange('sign-out');
this.service.auth.signOut()
.then(() => {
const /** @type {?} */ event = new EzfaSignedOutEvent();
this.screen = 'success';
this.service.onSignedOut(event);
if (!event.redirectCancelled) {
this.service.navigate('sign-in', { queryParams: { message: Messages.signedOut } });
}
});
}
}
SignOutRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-sign-out-route',
template: `
<div *ngIf=" 'wait' === screen " class="alert alert-light" role="alert">
<i class="fa fa-fw fa-spin fa-refresh" aria-hidden="true"></i>
Please wait. Signing you out...
</div>
<div *ngIf=" 'success' === screen " class="alert alert-success" role="alert">
<i class="fa fa-fw fa-check" aria-hidden="true"></i>
Thanks! You’re signed out!
</div>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
SignOutRouteComponent.ctorParameters = () => [
{ type: EzfaService, },
];
const validateEmail = (fc) => {
const /** @type {?} */ value = trim(fc.value);
if (value.length === 0) {
return null;
}
return isEmail(value) ? null : { email: true };
};
const clearControlErrors = (fc, keys) => {
const /** @type {?} */ orig = fc.errors;
if (!orig) {
return;
}
const /** @type {?} */ updated = omit(orig, keys);
if (Object.keys(updated).length > 0) {
fc.setErrors(updated);
}
else {
fc.setErrors(null);
}
};
const firebaseToFormError = (error) => {
const /** @type {?} */ formError = {};
formError[error.code] = true;
return formError;
};
class AddPasswordRouteComponent extends BaseComponent {
/**
* @param {?} fb
* @param {?} service
*/
constructor(fb, service) {
super(service);
this.fb = fb;
this.submitting = false;
this.unhandledError = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.service.onRouteChange('add-password');
this.id = uniqueId('ezfa-add-password-route');
this.fg = this.fb.group({
password: ['', [Validators.required]]
});
this.fg.get('password').valueChanges.takeUntil(this.ngUnsubscribe).subscribe(() => {
clearControlErrors(this.fg.get('password'), ['auth/weak-password']);
});
this.onInitLoadUser()
.then(() => {
this.gateToUserWithNoPassword();
});
}
/**
* @return {?}
*/
submit() {
this.submitting = true;
this.unhandledError = null;
const /** @type {?} */ password = this.fg.value.password;
this.addPassword(/** @type {?} */ (this.user), password)
.then((result) => {
this.user = result;
this.submitting = false;
this.service.navigate('account');
})
.catch((error) => {
switch (error.code) {
case 'auth/weak-password':
((this.fg.get('password'))).setErrors(firebaseToFormError(error));
break;
case 'auth/requires-recent-login':
this.service.navigate('reauthenticate', { queryParams: { redirect: 'add-password' } });
break;
default:
this.unhandledError = error;
break;
}
this.submitting = false;
});
}
/**
* @param {?} user
* @param {?} password
* @return {?}
*/
addPassword(user, password) {
return new Promise((resolve, reject) => {
this.service.getProviderById('password')
.then((provider) => {
const /** @type {?} */ credential = auth$1.EmailAuthProvider.credential(/** @type {?} */ (user.email), password);
return user.linkWithCredential(credential);
})
.then(resolve)
.catch(reject);
});
}
}
AddPasswordRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-add-password-route',
template: `
<form [formGroup]="fg" (ngSubmit)="submit()">
<div class="form-group">
<label [attr.for]="id + 'password'">
Choose Password
</label>
<ezfa-toggleable-password [control]="passwordInput">
<input
#passwordInput
[attr.id]="id + 'password'"
class="form-control"
[ezfaInvalidInput]="fg.get('password')"
placeholder="Password"
type="password"
[attr.aria-describedby]="id + 'passwordHelp'"
formControlName="password">
</ezfa-toggleable-password>
<div [ezfaInvalidFeedback]="fg.get('password')" key="required">
Required.
</div>
<div [ezfaInvalidFeedback]="fg.get('password')" key="auth/weak-password">
That password is too weak.
</div>
<small [attr.id]="id + 'passwordHelp'" class="form-text text-muted">
Please choose a strong password.
</small>
</div> <!-- password form-group -->
<div class="form-group text-right">
<button
type="submit"
[disabled]="submitting || fg.invalid"
class="btn"
[class.btn-outline-dark]="fg.invalid"
[class.btn-outline-success]="!fg.invalid">
<i class="fa fa-fw fa-save" aria-hidden="true"></i>
Add Password
</button>
<p *ngIf="submitting">
<i class="fa fa-fw fa-spin fa-refresh" aria-hidden="true"></i>
Adding password to account...
</p>
<p *ngIf="unhandledError">
<i class="fa fa-fw fa-warning text-danger" aria-hidden="true"></i>
Error: {{unhandledError.code}}
</p>
</div>
</form>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
AddPasswordRouteComponent.ctorParameters = () => [
{ type: FormBuilder, },
{ type: EzfaService, },
];
class UnlinkRouteComponent extends BaseComponent {
/**
* @param {?} route
* @param {?} service
*/
constructor(route, service) {
super(service);
this.route = route;
this.providerId = null;
this.screen = 'wait';
this.submitting = false;
this.unhandledError = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.onInitLoadUser()
.then(() => {
this.gateByUserAndProvider();
});
}
/**
* @return {?}
*/
submit() {
this.submitting = true;
this.unhandledError = null;
this.user.unlink(/** @type {?} */ (this.providerId))
.then((user) => {
const /** @type {?} */ event = new EzfaProviderUnlinkedEvent(user, this.providerId);
let /** @type {?} */ message;
switch (this.providerId) {
case 'password':
message = Messages.passwordRemoved;
break;
case 'twitter.com':
message = Messages.twitterAccountRemoved;
break;
case 'facebook.com':
message = Messages.facebookAccountRemoved;
break;
case 'google.com':
message = Messages.googleAccountRemoved;
break;
case 'github.com':
message = Messages.githubAccountRemoved;
break;
}
this.service.onProviderUnlinked(event);
this.service.navigate('account', { queryParams: { message: message } });
this.submitting = false;
})
.catch((error) => {
this.submitting = false;
this.unhandledError = error;
});
}
/**
* @return {?}
*/
gateByUserAndProvider() {
this.service.authState.takeUntil(this.ngUnsubscribe).subscribe((user) => {
this.providerId = this.route.snapshot.queryParams.providerId || null;
if (!this.providerId) {
this.service.navigate();
return;
}
if (!user) {
this.service.navigate();
return;
}
const /** @type {?} */ provider = find(user.providerData, { providerId: this.providerId }) || null;
if (!provider) {
this.service.navigate();
return;
}
this.screen = 'form';
});
}
}
UnlinkRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-unlink-route',
template: `
<div *ngIf=" 'wait' === screen " class="alert alert-info my-3" role="alert">
<i class="fa fa-fw fa-spin fa-refresh" aria-hidden="true"></i>
Please wait...
</div>
<div *ngIf=" 'form' === screen ">
<div class="card my-3">
<div class="card-body">
<p>
Are you sure you want to
<span *ngIf="providerId === 'password'">remove the password</span>
<span *ngIf="providerId !== 'password'">
unlink
<ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
</span>
from your account?
</p>
<div class="form-group text-right">
<p>
<a class="btn btn-secondary" routerLink="../account">Cancel</a>
<button class="btn btn-danger" type="button" (click)="submit()">
<i class="fa fa-times fa-fw" aria-hidden="true"></i>
<span *ngIf="providerId !== 'password'">
Unlink
<ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
</span>
<span *ngIf="providerId === 'password'">
Remove Password
</span>
</button>
</p>
<p *ngIf="submitting">
<i class="fa fa-fw fa-spin fa-refresh" aria-hidden="true"></i>
Removing <ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
sign in method....
</p>
<p *ngIf="unhandledError">
<i class="fa fa-fw fa-warning text-danger" aria-hidden="true"></i>
Error: {{unhandledError.code}}
</p>
</div>
</div>
</div>
</div>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
UnlinkRouteComponent.ctorParameters = () => [
{ type: ActivatedRoute, },
{ type: EzfaService, },
];
class LinkRouteComponent extends BaseComponent {
/**
* @param {?} route
* @param {?} service
*/
constructor(route, service) {
super(service);
this.route = route;
this.error = null;
this.success = null;
this.wait = true;
}
/**
* @return {?}
*/
ngOnInit() {
this.service.onRouteChange('link');
this.providerId = this.route.snapshot.queryParams.providerId;
if (!this.providerId) {
this.service.navigate();
return;
}
this.onInitLoadUser()
.then(() => {
return this.onInitHandleSavedPopupPromise();
})
.then((handled) => {
if (handled) {
return true;
}
else {
return this.onInitCheckForRedirect();
}
})
.then((handled) => {
if (!handled) {
this.link();
}
this.gateToSignedInUser();
});
}
/**
* @return {?}
*/
link() {
this.error = null;
this.success = null;
this.wait = true;
this.service.getProviderById(this.providerId)
.then((provider) => {
switch (this.service.oauthMethod) {
case EzfaOauthMethod.popup:
return this.user.linkWithPopup(provider);
default:
return this.user.linkWithRedirect(provider);
}
})
.then((result) => {
if (result) {
this.onSuccess(result);
}
})
.catch((error) => {
this.onError(error);
});
}
/**
* @return {?}
*/
onInitHandleSavedPopupPromise() {
return new Promise((resolve) => {
const /** @type {?} */ p = this.service.savedPopupPromise;
this.service.savedPopupPromise = null;
if (!this.user) {
this.service.navigate();
return resolve(true);
}
if (!p) {
return resolve(false);
}
p.then((result) => {
this.onSuccess(result);
resolve(true);
})
.catch((error) => {
this.onError(error);
resolve(true);
});
});
}
/**
* @return {?}
*/
onInitCheckForRedirect() {
return new Promise((resolve) => {
if (!this.user) {
this.service.navigate();
return resolve(true);
}
this.service.auth.getRedirectResult()
.then((result) => {
if (result.user) {
this.onSuccess(result);
resolve(true);
}
else {
resolve(false);
}
})
.catch((error) => {
this.onError(error);
resolve(true);
});
});
}
/**
* @param {?} cred
* @return {?}
*/
onSuccess(cred) {
this.error = null;
const /** @type {?} */ event = new EzfaProviderLinkedEvent(cred.user, cred.credential.providerId, cred);
this.success = event;
this.wait = false;
const /** @type {?} */ params = { message: null };
switch (cred.credential.providerId) {
case 'twitter.com':
params.message = Messages.twitterAccountAdded;
break;
case 'facebook.com':
params.message = Messages.facebookAccountAdded;
break;
case 'github.com':
params.message = Messages.githubAccountAdded;
break;
case 'google.com':
params.message = Messages.googleAccountAdded;
break;
}
this.service.onProviderLinked(event);
this.service.navigate('account', { queryParams: params });
}
/**
* @param {?} error
* @return {?}
*/
onError(error) {
this.wait = false;
this.error = error;
this.success = null;
}
}
LinkRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-link-route',
template: `
<p class="text-muted" *ngIf="wait">
<i class="fa fa-spin fa-fw fa-refresh"></i> Please wait...
</p>
<div *ngIf="error">
<div [ngSwitch]="error.code" class="alert alert-danger my-3" role="alert">
<p>
<i class="fa fa-fw fa-warning"></i>
<strong>
Account Link Error
</strong>
</p>
<div *ngSwitchCase="'auth/credential-already-in-use'">
<p>
The <ezfa-provider-label [providerId]="error.credential.providerId"></ezfa-provider-label>
account you tried to add is already associated with a different
<ezfa-application-label></ezfa-application-label> account ({{error.email}}.)
</p>
<p class="mb-0">
You’re currently signed in as {{user.email}}.
</p>
</div>
<div *ngSwitchCase="'auth/email-already-in-use'">
<p>
The email for the <ezfa-provider-label [providerId]="error.credential.providerId"></ezfa-provider-label>
account you tried to add ({{error.email}}) is already in use by a different
<ezfa-application-label></ezfa-application-label> account.
</p>
<p class="mb-0">
You’re currently signed in as {{user.email}}.
</p>
</div>
<div *ngSwitchCase="'auth/cancelled-popup-request'">
<p class="mb-0">
You closed the <ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
popup window without linking <ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>.
</p>
</div>
<div *ngSwitchCase="'auth/popup-closed-by-user'">
<p class="mb-0">
You closed the <ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
popup window without linking a <ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
account.
</p>
</div>
<p *ngSwitchCase="'auth/popup-blocked'" class="mb-0">
the popup was blocked.
</p>
<p *ngSwitchDefault class="mb-0">
Unhandled error: {{error | json}}
</p>
</div>
<p class="text-muted">
Try again...
<button class="btn btn-primary btn-block my-1" (click)="link()">
Add
<ezfa-provider-icon [providerId]="providerId"></ezfa-provider-icon>
<ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
</button>
<small class="form-text">
Make sure the <ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
account you add is associated with the email address {{user.email}}.
</small>
</p>
<p>
<a routerLink="../account" class="btn btn-secondary btn-block">
Back to Account
</a>
</p>
</div>
<div *ngIf="success">
<div class="alert alert-success my-3" role="alert">
<ezfa-provider-icon [providerId]="providerId"></ezfa-provider-icon>
<ezfa-provider-label [providerId]="providerId"></ezfa-provider-label>
added!
</div>
<p>
<a routerLink="../account" class="btn btn-secondary btn-block">
Back to Account
</a>
</p>
</div>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
LinkRouteComponent.ctorParameters = () => [
{ type: ActivatedRoute, },
{ type: EzfaService, },
];
class ChangePasswordRouteComponent extends BaseComponent {
/**
* @param {?} fb
* @param {?} service
*/
constructor(fb, service) {
super(service);
this.fb = fb;
this.user = null;
this.hasPasswordProvider = false;
this.success = false;
this.submitting = false;
this.unhandledError = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.id = uniqueId('ezfa-change-password-route');
this.service.onRouteChange('change-password');
this.fg = this.fb.group({
password: ['', [Validators.required]]
});
const /** @type {?} */ fc = (this.fg.get('password'));
fc.valueChanges.takeUntil(this.ngUnsubscribe).subscribe(() => {
clearControlErrors(fc, ['auth/weak-password']);
});
this.onInitLoadUser()
.then(() => {
this.gateToUserWithPassword();
});
}
/**
* @return {?}
*/
submit() {
if (!this.user) {
return;
}
this.unhandledError = null;
this.submitting = true;
const /** @type {?} */ fc = (this.fg.get('password'));
const /** @type {?} */ user = (this.user);
const /** @type {?} */ password = fc.value;
user.updatePassword(password)
.then(() => {
return user.reload();
})
.then(() => {
this.success = true;
this.submitting = false;
this.service.navigate('account', { queryParams: { message: Messages.passwordSaved } });
})
.catch((error) => {
this.submitting = false;
switch (error.code) {
case 'auth/weak-password':
fc.setErrors(firebaseToFormError(error));
break;
case 'auth/requires-recent-login':
this.service.navigate('reauthenticate', { queryParams: { redirect: 'change-password' } });
break;
default:
this.unhandledError = error;
break;
}
});
}
}
ChangePasswordRouteComponent.decorators = [
{ type: Component, args: [{
selector: 'ezfa-change-password-route',
template: `
<div *ngIf="user">
<form [formGroup]="fg" (ngSubmit)="submit()" *ngIf="! success">
<div class="form-group">
<label [attr.for]="id + 'password'">
Choose New Password
</label>
<ezfa-toggleable-password [control]="passwordInput">
<input
#passwordInput
[attr.id]="id + 'password'"
class="form-control"
[ezfaInvalidInput]="fg.get('password')"
placeholder="Password"
type="password"
[attr.aria-describedby]="id + 'passwordHelp'"
formControlName="password">
</ezfa-toggleable-password>
<div [ezfaInvalidFeedback]="fg.get('password')" key="required">
Required.
</div>
<div [ezfaInvalidFeedback]="fg.get('password')" key="auth/weak-password">
That password is too weak.
</div>
<small [attr.id]="id + 'passwordHelp'" class="form-text text-muted">
Please choose a strong password.
</small>
</div> <!-- password form-group -->
<div class="form-group text-right">
<button
type="submit"
[disabled]="submitting || fg.invalid"
class="btn"
[class.btn-outline-dark]="fg.invalid"
[class.btn-outline-success]="!fg.invalid">
<i class="fa fa-fw fa-save" aria-hidden="true"></i>
Save Password
</button>
<p *ngIf="submitting">
<i class="fa fa-fw fa-spin fa-refresh" aria-hidden="true"></i>
Saving password...
</p>
<p *ngIf="unhandledError">
<i class="fa fa-fw fa-warning text-danger" aria-hidden="true"></i>
Error: {{unhandledError.code}}
</p>
</div>
</form>
<div *ngIf="success">
<div class="alert alert-success" role="alert">
Your new password has been saved.
</div>
<p>
<a routerLink="../account" class="btn btn-secondary btn-block">
Back to Account
</a>
</p>
</div>
</div>
`,
styles: [`
`]
},] },
];
/**
* @nocollapse
*/
ChangePasswordRouteComponent.ctorParameters = () => [
{ type: FormBuilder, },
{ type: EzfaService, },
];
class ChangeEmailRouteComponent extends BaseComponent {
/**
* @param {?} fb
* @param {?} service
*/
constructor(fb, service) {
super(service);
this.fb = fb;
this.user = null;
this.success = false;
this.submitting = false;
this.unhandledError = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.service.onRouteChange('change-email');
this.id = uniqueId('ezfa-change-email-route');
this.fg = this.fb.group({
email: ['', [Validators.required, validateEmail, this.validateNotSame.bind(this)]]
});
const /** @type {?} */ fc = (this.fg.get('email'));
fc.valueChanges.takeUntil(this.ngUnsubscribe).subscribe(() => {
clearControlErrors(fc, ['auth/invalid-email', 'auth/email-already-in-use']);
});
this.onInitLoadUser()
.then(() => {
this.gateToUserWithPassword()