Skip to content

Commit

Permalink
Merge pull request FirebaseExtended#611 from firebase/kato-resolve
Browse files Browse the repository at this point in the history
Fixes FirebaseExtended#590 - $requireAuth/$waitForAuth now detects auth state changes
  • Loading branch information
Jacob Wenger committed May 1, 2015
2 parents 86ea97e + aeda971 commit 141f27e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 26 deletions.
54 changes: 32 additions & 22 deletions src/FirebaseAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// Define a service which provides user authentication and management.
angular.module('firebase').factory('$firebaseAuth', [
'$q', '$firebaseUtils', '$log', function($q, $firebaseUtils, $log) {
'$q', '$firebaseUtils', function($q, $firebaseUtils) {
/**
* This factory returns an object allowing you to manage the client's authentication state.
*
Expand All @@ -13,21 +13,20 @@
* authentication state, and managing users.
*/
return function(ref) {
var auth = new FirebaseAuth($q, $firebaseUtils, $log, ref);
var auth = new FirebaseAuth($q, $firebaseUtils, ref);
return auth.construct();
};
}
]);

FirebaseAuth = function($q, $firebaseUtils, $log, ref) {
FirebaseAuth = function($q, $firebaseUtils, ref) {
this._q = $q;
this._utils = $firebaseUtils;
this._log = $log;

if (typeof ref === 'string') {
throw new Error('Please provide a Firebase reference instead of a URL when creating a `$firebaseAuth` object.');
}
this._ref = ref;
this._initialAuthResolver = this._initAuthResolver();
};

FirebaseAuth.prototype = {
Expand Down Expand Up @@ -246,27 +245,38 @@
* rejected if the client is unauthenticated and rejectIfAuthDataIsNull is true.
*/
_routerMethodOnAuthPromise: function(rejectIfAuthDataIsNull) {
var ref = this._ref;
var ref = this._ref, utils = this._utils;
// wait for the initial auth state to resolve; on page load we have to request auth state
// asynchronously so we don't want to resolve router methods or flash the wrong state
return this._initialAuthResolver.then(function() {
// auth state may change in the future so rather than depend on the initially resolved state
// we also check the auth data (synchronously) if a new promise is requested, ensuring we resolve
// to the current auth state and not a stale/initial state
var authData = ref.getAuth(), res = null;
if (rejectIfAuthDataIsNull && authData === null) {
res = utils.reject("AUTH_REQUIRED");
}
else {
res = utils.resolve(authData);
}
return res;
});
},

return this._utils.promise(function(resolve,reject){
function callback(authData) {
/**
* Helper that returns a promise which resolves when the initial auth state has been
* fetched from the Firebase server. This never rejects and resolves to undefined.
*
* @return {Promise<Object>} A promise fulfilled when the server returns initial auth state.
*/
_initAuthResolver: function() {
var ref = this._ref;
return this._utils.promise(function(resolve) {
function callback() {
// Turn off this onAuth() callback since we just needed to get the authentication data once.
ref.offAuth(callback);

if (authData !== null) {
resolve(authData);
return;
}
else if (rejectIfAuthDataIsNull) {
reject("AUTH_REQUIRED");
return;
}
else {
resolve(null);
return;
}
resolve();
}

ref.onAuth(callback);
});
},
Expand Down
25 changes: 21 additions & 4 deletions tests/unit/FirebaseAuth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,31 +289,48 @@ describe('FirebaseAuth',function(){

describe('$requireAuth()',function(){
it('will be resolved if user is logged in', function(){
ref.getAuth.and.returnValue({provider:'facebook'});
wrapPromise(auth.$requireAuth());
callback('onAuth')({provider:'facebook'});
callback('onAuth')();
$timeout.flush();
expect(result).toEqual({provider:'facebook'});
});

it('will be rejected if user is not logged in', function(){
ref.getAuth.and.returnValue(null);
wrapPromise(auth.$requireAuth());
callback('onAuth')(null);
callback('onAuth')();
$timeout.flush();
expect(failure).toBe('AUTH_REQUIRED');
});
});

describe('$waitForAuth()',function(){
it('will be resolved with authData if user is logged in', function(){
ref.getAuth.and.returnValue({provider:'facebook'});
wrapPromise(auth.$waitForAuth());
callback('onAuth')({provider:'facebook'});
callback('onAuth')();
$timeout.flush();
expect(result).toEqual({provider:'facebook'});
});

it('will be resolved with null if user is not logged in', function(){
ref.getAuth.and.returnValue(null);
wrapPromise(auth.$waitForAuth());
callback('onAuth')();
$timeout.flush();
expect(result).toBe(null);
});

it('promise resolves with current value if auth state changes after onAuth() completes', function() {
ref.getAuth.and.returnValue({provider:'facebook'});
wrapPromise(auth.$waitForAuth());
callback('onAuth')();
$timeout.flush();
expect(result).toEqual({provider:'facebook'});

ref.getAuth.and.returnValue(null);
wrapPromise(auth.$waitForAuth());
callback('onAuth')(null);
$timeout.flush();
expect(result).toBe(null);
});
Expand Down

0 comments on commit 141f27e

Please sign in to comment.