Skip to content

Commit

Permalink
feat(auth): New Auth API
Browse files Browse the repository at this point in the history
  • Loading branch information
davideast committed Mar 30, 2017
1 parent b388627 commit 12aa422
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 255 deletions.
83 changes: 6 additions & 77 deletions src/angularfire2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,100 +2,29 @@ import * as firebase from 'firebase/app';
import * as utils from './utils';
import { FirebaseAppConfigToken, FirebaseApp, _firebaseAppFactory } from './app/index';
import { FirebaseListFactoryOpts, FirebaseObjectFactoryOpts, FirebaseAppConfig } from './interfaces';
import { FirebaseAppName, WindowLocation, FirebaseAuthConfig } from './tokens';
import { FirebaseAppName } from './tokens';
import { Injectable, OpaqueToken, NgModule } from '@angular/core';
import { FirebaseSdkAuthBackend, AuthBackend, AngularFireAuth, AuthConfiguration } from './auth/index';

@Injectable()
export class AngularFire {
constructor(
public auth: AngularFireAuth) {}
}

export function _getAngularFire(auth: AngularFireAuth) {
return new AngularFire(auth);
}

export function _getAngularFireAuth(backend: AuthBackend, location: any, config: any) {
return new AngularFireAuth(backend, location, config);
}

export function _getWindowLocation(){
return window.location;
}

export function _getFirebaseAuthBackend(app: FirebaseApp) {
return new FirebaseSdkAuthBackend(app);
}

export const AuthBackendProvder = {
provide: AuthBackend,
useFactory: _getFirebaseAuthBackend,
deps: [ FirebaseApp ]
};

export const WindowLocationProvider = {
provide: WindowLocation,
useFactory: _getWindowLocation
};

export const FirebaseAppProvider = {
provide: FirebaseApp,
useFactory: _firebaseAppFactory,
deps: [ FirebaseAppConfigToken, FirebaseAppName ]
};

export const FirebaseAuthBackendProvider = {
provide: FirebaseSdkAuthBackend,
useFactory: _getFirebaseAuthBackend,
deps: [ FirebaseApp ]
};

export const AngularFireAuthProvider = {
provide: AngularFireAuth,
useFactory: _getAngularFireAuth,
deps: [ AuthBackend, WindowLocation, FirebaseAuthConfig ]
};

export const AngularFireProvider = {
provide: AngularFire,
useFactory: _getAngularFire,
deps: [ AngularFireAuth ]
};

export const FIREBASE_PROVIDERS:any[] = [
FirebaseAppProvider,
FirebaseAuthBackendProvider,
AuthBackendProvder,
WindowLocationProvider,
AngularFireAuthProvider,
AngularFireProvider
];

export {
FirebaseApp,
FirebaseAppConfigToken,
AngularFireAuth,
WindowLocation
}

export { AuthMethods, firebaseAuthConfig, AuthProviders, FirebaseAuthState } from './auth/index';

export { FirebaseConfig, FirebaseAuthConfig, FirebaseRef, FirebaseUrl, FirebaseUserConfig } from './tokens';
export { FirebaseAppConfig } from './interfaces';

@NgModule({
providers: [FIREBASE_PROVIDERS],
providers: [ FirebaseAppProvider ],
})
export class AngularFireModule {
static initializeApp(config: FirebaseAppConfig, authConfig?: AuthConfiguration, appName?: string) {
static initializeApp(config: FirebaseAppConfig, appName?: string) {
return {
ngModule: AngularFireModule,
providers: [
{ provide: FirebaseAppConfigToken, useValue: config },
{ provide: FirebaseAppName, useValue: appName },
{ provide: FirebaseAuthConfig, useValue: authConfig }
{ provide: FirebaseAppName, useValue: appName }
]
}
}
}

export { FirebaseApp, FirebaseAppConfigToken, FirebaseAppConfig }
4 changes: 2 additions & 2 deletions src/auth/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { TestBed, inject } from '@angular/core/testing';
import { _do } from 'rxjs/operator/do';
import { take } from 'rxjs/operator/take';
import { skip } from 'rxjs/operator/skip';
import { FIREBASE_PROVIDERS, FirebaseApp, FirebaseAppConfig, FirebaseAuthState, FirebaseAppConfigToken, AngularFireAuth, AuthMethods, firebaseAuthConfig, AuthProviders, WindowLocation, AngularFireModule } from '../angularfire2';
import { COMMON_CONFIG, ANON_AUTH_CONFIG } from '../test-config';
import { FirebaseApp, FirebaseAppConfig, AngularFireModule } from '../angularfire2';
import { COMMON_CONFIG } from '../test-config';
import { AuthBackend } from './auth_backend';
import { FirebaseSdkAuthBackend } from './firebase_sdk_auth_backend';

Expand Down
197 changes: 23 additions & 174 deletions src/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,188 +1,37 @@
import * as firebase from 'firebase/app';
import * as utils from '../utils';
import 'firebase/auth';
import { Provider, Inject, Injectable, Optional } from '@angular/core';
import { Injectable, NgZone } from '@angular/core';
import { Auth } from '../interfaces';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { FirebaseApp, FirebaseAuthConfig, WindowLocation } from '../tokens';
import * as utils from '../utils';
import {
authDataToAuthState,
AuthBackend,
AuthProviders,
AuthMethods,
EmailPasswordCredentials,
AuthConfiguration,
FirebaseAuthState,
stripProviderId
} from './auth_backend';
import { mergeMap } from 'rxjs/operator/mergeMap';
import { of as observableOf } from 'rxjs/observable/of';
import { map } from 'rxjs/operator/map';

const kBufferSize = 1;

export const firebaseAuthConfig = (config: AuthConfiguration): any => {
return { provide: FirebaseAuthConfig, useValue: config }
};
import { observeOn } from 'rxjs/operator/observeOn';
import { FirebaseApp } from '../app/index';

@Injectable()
export class AngularFireAuth extends ReplaySubject<FirebaseAuthState> {
private _credentialCache: {[key:string]: any} = {};
constructor(private _authBackend: AuthBackend,
@Inject(WindowLocation) loc: any,
@Optional() @Inject(FirebaseAuthConfig) private _config?: AuthConfiguration) {
super(kBufferSize);

let firstPass = true;
let onAuth = this._authBackend.onAuth();

mergeMap.call(onAuth, (authState: FirebaseAuthState) => {
if (firstPass) {
firstPass = false;
if(['http:', 'https:'].indexOf(loc.protocol) > -1) {
// Only call getRedirectResult() in a browser
return map.call(this._authBackend.getRedirectResult(), (userCredential: firebase.auth.UserCredential) => {
if (userCredential && userCredential.credential) {
authState = attachCredentialToAuthState(authState, userCredential.credential, userCredential.credential.provider);
this._credentialCache[userCredential.credential.provider] = userCredential.credential;
}
return authState;
});
}

}
return observableOf(authState);
})
.subscribe((authData: FirebaseAuthState) => this._emitAuthData(authData));
}

public login(config?: AuthConfiguration): firebase.Promise<FirebaseAuthState>;
// If logging in with email and password
public login(credentials?: EmailPasswordCredentials | firebase.auth.AuthCredential | string): firebase.Promise<FirebaseAuthState>;
public login(credentials: EmailPasswordCredentials | firebase.auth.AuthCredential | string, config?: AuthConfiguration): firebase.Promise<FirebaseAuthState>;
public login(obj1?: any, obj2?: AuthConfiguration): firebase.Promise<FirebaseAuthState> {
let config: AuthConfiguration = null;
let credentials: EmailPasswordCredentials | firebase.auth.AuthCredential | string = null;
if (arguments.length > 2) {
return this._reject('Login only accepts a maximum of two arguments.');
} else if (arguments.length == 2) {
credentials = obj1;
config = obj2;
} else if (arguments.length == 1) {
// Check if obj1 is password credentials
if (obj1.password && obj1.email) {
credentials = obj1;
config = {};
} else {
config = obj1;
}
}
config = this._mergeConfigs(config);

if (utils.isNil(config.method)) {
return this._reject('You must provide a login method');
}
let providerMethods = [AuthMethods.Popup, AuthMethods.Redirect, AuthMethods.OAuthToken];
if (providerMethods.indexOf(config.method) != -1) {
if (utils.isNil(config.provider)) {
return this._reject('You must include a provider to use this auth method.');
}
}
let credentialsMethods = [AuthMethods.Password, AuthMethods.OAuthToken, AuthMethods.CustomToken];
if (credentialsMethods.indexOf(config.method) != -1) {
if (!credentials) {
return this._reject('You must include credentials to use this auth method.');
}
}

switch (config.method) {
case AuthMethods.Popup:
return this._authBackend.authWithOAuthPopup(config.provider, this._scrubConfig(config))
.then((userCredential: firebase.auth.UserCredential) => {
// Incorrect type information
this._credentialCache[userCredential.credential.provider] = userCredential.credential;
return authDataToAuthState(userCredential.user, (<any>userCredential).credential);
});
case AuthMethods.Redirect:
// Gets around typings issue since this method doesn't resolve with a user.
// The method really only does anything with an error, since it redirects.
return <Promise<FirebaseAuthState>>(<any>this._authBackend).authWithOAuthRedirect(config.provider, this._scrubConfig(config));
case AuthMethods.Anonymous:
return this._authBackend.authAnonymously(this._scrubConfig(config));
case AuthMethods.Password:
return this._authBackend.authWithPassword(<EmailPasswordCredentials>credentials);
case AuthMethods.OAuthToken:
return this._authBackend.authWithOAuthToken(<firebase.auth.AuthCredential>credentials,
this._scrubConfig(config));
case AuthMethods.CustomToken:
return this._authBackend.authWithCustomToken(<string>credentials);
}
}

public logout(): Promise<void> {
return this._authBackend.unauth();
}

public getAuth(): FirebaseAuthState {
console.warn(`WARNING: the getAuth() API has changed behavior since adding support for Firebase 3.
This will return null for the initial value when the page loads, even if the user is actually logged in.
Please observe the actual authState asynchronously by subscribing to the auth service: af.auth.subscribe().
The getAuth method will be removed in future releases`);
return this._authBackend.getAuth()
}

public createUser(credentials: EmailPasswordCredentials): firebase.Promise<FirebaseAuthState> {
return this._authBackend.createUser(credentials);
}
export class AngularFireAuth {

/**
* Merges the config object that is passed in with the configuration
* provided through DI. Giving precendence to the one that was passed.
* Firebase Auth instance
*/
private _mergeConfigs(config: AuthConfiguration): AuthConfiguration {
if (this._config == null)
return config;

return Object.assign({}, this._config, config);
}

private _reject(msg: string): firebase.Promise<FirebaseAuthState> {
return (<Promise<FirebaseAuthState>>new Promise((res, rej) => {
return rej(msg);
}));
}
auth: firebase.auth.Auth;

/**
* Observable of authentication state
*/
authState: Observable<firebase.User>;

private _scrubConfig(config: AuthConfiguration, scrubProvider = true): any {
let scrubbed = Object.assign({}, config);
if (scrubProvider) {
delete scrubbed.provider;
}
delete scrubbed.method;
return scrubbed;
constructor(public app: FirebaseApp) {
this.authState = FirebaseAuthStateObservable(app);
}


private _emitAuthData(authData: FirebaseAuthState): void {
if (authData == null) {
this.next(null);
} else {
if (authData.auth && authData.auth.providerData && authData.auth.providerData[0]) {
let providerId = authData.auth.providerData[0].providerId;
let providerCredential = this._credentialCache[providerId];
if (providerCredential) {
authData = attachCredentialToAuthState(authData, providerCredential, providerId);
}
}

this.next(authData);
}
}
}

function attachCredentialToAuthState (authState: FirebaseAuthState, credential, providerId: string): FirebaseAuthState {
if (!authState) return authState;
// TODO make authState immutable
authState[stripProviderId(providerId)] = credential;
return authState;
/**
* Create an Observable of Firebase authentication state. Each event is called
* within the current zone.
* @param app - Firebase App instance
*/
export function FirebaseAuthStateObservable(app: FirebaseApp) {
const authState = Observable.create(firebase.auth().onAuthStateChanged);
return observeOn.call(authState, new utils.ZoneScheduler(Zone.current));
}
2 changes: 1 addition & 1 deletion src/database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FirebaseListObservable, FirebaseObjectObservable, FirebaseObjectFactory
@Injectable()
export class AngularFireDatabase {

constructor(private app: FirebaseApp) {}
constructor(public app: FirebaseApp) {}

list(pathOrRef: PathReference, opts?:FirebaseListFactoryOpts):FirebaseListObservable<any[]> {
const ref = utils.getRef(this.app, pathOrRef);
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ export type DatabaseSnapshot = firebase.database.DataSnapshot;
export type DatabaseReference = firebase.database.Reference;
export type DatabaseQuery = firebase.database.Query;
export type QueryReference = DatabaseReference | DatabaseQuery;
export type PathReference = QueryReference | string;
export type PathReference = QueryReference | string;
export type Auth = firebase.auth.Auth;

0 comments on commit 12aa422

Please sign in to comment.