UNPKG

ez-firebase-auth

Version:
1,658 lines (1,610 loc) 121 kB
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()