From 7116024d84b05cb26d63b3ba414a52d748204ae3 Mon Sep 17 00:00:00 2001
From: katowulf
Date: Mon, 22 Dec 2014 13:19:33 -0700
Subject: [PATCH] Implemented new auth methods. Upgraded to AngularFire 0.9.1.
Can't tag/release this until AngularFire is pushed to bower/npm.
---
angularfire/config.json | 15 +-
app/index.js | 29 ++--
readme.md | 2 +-
route/index.js | 7 +-
.../coffeescript/controllers/account.coffee | 8 +-
.../coffeescript/controllers/chat.coffee | 2 +-
.../coffeescript/routes.with.login.coffee | 4 +-
.../common/app/scripts/angularfire/config.js | 4 +-
.../app/scripts/angularfire/firebase.utils.js | 4 +-
.../app/scripts/angularfire/simpleLogin.js | 146 +++---------------
templates/common/app/views/login.html | 6 +-
templates/common/root/_bower.json | 5 +-
templates/javascript/controllers/account.js | 12 +-
templates/javascript/controllers/chat.js | 2 +-
templates/javascript/controllers/login.js | 45 +++---
templates/javascript/routes.with.login.js | 2 +-
16 files changed, 99 insertions(+), 194 deletions(-)
diff --git a/angularfire/config.json b/angularfire/config.json
index da14f4ed7..96d097785 100644
--- a/angularfire/config.json
+++ b/angularfire/config.json
@@ -1,17 +1,20 @@
{
"bower": {
- "firebase": "1.0.x",
- "angularfire": "0.8.x",
- "mockfirebase": "0.2.x",
- "simplelogin": "1.6.x"
+ "firebase": "2.0.x",
+ "angularfire": "0.9.1",
+ "mockfirebase": "0.8.x"
},
- "simpleLoginProviders": [
+ "authProviders": [
{ "name": "Email/Password", "value": "password", "checked": true },
{ "name": "Anonymous", "value": "anonymous" },
{ "name": "Facebook", "value": "facebook" },
{ "name": "Google", "value": "google" },
{ "name": "Twitter", "value": "twitter" },
{ "name": "GitHub", "value": "github" }
- ]
+ ],
+
+ "specialRoutes": {
+ "chat": true, "login": true, "account": true
+ }
}
diff --git a/app/index.js b/app/index.js
index 871aa6520..a03a93d26 100644
--- a/app/index.js
+++ b/app/index.js
@@ -36,7 +36,7 @@ var FIREBASE_PROMPTS = [
type: 'checkbox',
name: 'providers',
message: 'Which providers shall I install?',
- choices: afconfig.simpleLoginProviders,
+ choices: afconfig.authProviders,
when: function(answers) {
return answers.loginModule;
},
@@ -55,6 +55,7 @@ var Generator = module.exports = function Generator(args, options) {
//angularfire
this.afconfig = afconfig;
+ this.env.options.afconfig = afconfig;
this.angularFireSourceFiles = [];
this.option('app-suffix', {
@@ -194,25 +195,21 @@ var Generator = module.exports = function Generator(args, options) {
});
if (this.env.options.ngRoute) {
+ // this will not create controller or html because
+ // "chat" exists in config.json::specialRoutes
this.invoke('angularfire:route', {
//angularfire
- args: ['chat', true]
+ args: ['chat']
});
}
//angularfire
if(this.env.options.loginModule) {
if( this.env.options.ngRoute ) {
+ // this will not create controller or html because
+ // "login" exists in config.json::specialRoutes
this.invoke('angularfire:route', {
- args: ['login', true]
- });
- }
- else {
- this.invoke('angularfire:controller', {
- args: ['login', true]
- });
- this.invoke('angularfire.view', {
- args: ['login', true]
+ args: ['login']
});
}
}
@@ -248,12 +245,12 @@ Generator.prototype.welcome = function welcome() {
Generator.prototype.askFirebaseQuestions = function askForCompass() {
this.firebaseName = null;
this.loginModule = false;
- this.simpleLoginProviders = [];
+ this.authProviders = [];
this.hasOauthProviders = false;
this.hasPasswordProvider = false;
// allow firebase instance to be set on command line
- this._defaultNamespace(this.options['instance'], FIREBASE_PROMPTS);
+ this._defaultNamespace(this.options.instance, FIREBASE_PROMPTS);
var cb = this.async();
this.prompt(FIREBASE_PROMPTS, function (props) {
@@ -531,15 +528,15 @@ Generator.prototype._tpl = function(src, dest) {
//angularfire
Generator.prototype._processProviders = function(list) {
- var providerMap = {}, i = afconfig.simpleLoginProviders.length, p;
+ var providerMap = {}, i = afconfig.authProviders.length, p;
while(i--) {
- p = afconfig.simpleLoginProviders[i];
+ p = afconfig.authProviders[i];
providerMap[p.value] = {name: p.name, value: p.value};
}
list.forEach(function(p) {
if( p === 'password' ) { this.hasPasswordProvider = true; }
else { this.hasOauthProviders = true; }
- this.simpleLoginProviders.push(providerMap[p]);
+ this.authProviders.push(providerMap[p]);
}, this);
};
diff --git a/readme.md b/readme.md
index b5028b31a..26ee3a1c3 100644
--- a/readme.md
+++ b/readme.md
@@ -42,7 +42,7 @@ Available generators:
**Note: Generators are to be run from the root directory of your app.**
### App
-Sets up a new AngularJS + Firebase app, generating all the boilerplate you need to get started. The app generator also optionally installs FirebaseSimpleLogin, Bootstrap and additional AngularJS modules, such as angular-resource (installed by default).
+Sets up a new AngularJS + Firebase app, generating all the boilerplate you need to get started. The app generator also optionally installs Firebase authentication and account management, Bootstrap and additional AngularJS modules, such as angular-resource (installed by default).
Example:
```bash
diff --git a/route/index.js b/route/index.js
index bb944fd38..d13329c3a 100644
--- a/route/index.js
+++ b/route/index.js
@@ -6,7 +6,7 @@ var ScriptBase = require('../script-base.js');
var angularUtils = require('../util.js');
-var Generator = module.exports = function Generator(name, skipFiles) {
+var Generator = module.exports = function Generator(name) {
ScriptBase.apply(this, arguments);
this.option('uri', {
desc: 'Allow a custom uri for routing',
@@ -28,7 +28,7 @@ var Generator = module.exports = function Generator(name, skipFiles) {
this.foundWhenForRoute = true;
}
- if( skipFiles !== true ) {
+ if( this.env.options.afconfig.specialRoutes[name] !== true) {
this.hookFor('angularfire:controller');
this.hookFor('angularfire:view');
}
@@ -37,7 +37,7 @@ var Generator = module.exports = function Generator(name, skipFiles) {
util.inherits(Generator, ScriptBase);
//angularFire
-Generator.prototype.rewriteAppJs = function () {
+Generator.prototype.rewriteRoutesJs = function () {
var coffee = this.env.options.coffee;
if (!this.foundWhenForRoute) {
@@ -69,7 +69,6 @@ Generator.prototype.rewriteAppJs = function () {
var whenMethod = this.env.options.authRequired || this.options['auth-required']? 'whenAuthenticated' : 'when';
- console.log(whenMethod, this.options);
if (coffee) {
config.splicable.unshift("." + whenMethod + " '/" + this.uri + "',");
diff --git a/templates/coffeescript/controllers/account.coffee b/templates/coffeescript/controllers/account.coffee
index b3ed54846..57651e34a 100644
--- a/templates/coffeescript/controllers/account.coffee
+++ b/templates/coffeescript/controllers/account.coffee
@@ -27,13 +27,15 @@ angular.module("<%= scriptAppName %>").controller "AccountCtrl", ($scope, user,
return
<% } %>loadProfile = (user) ->
- $scope.profile.$destroy() if $scope.profile
- fbutil.syncObject("users/" + user.uid).$bindTo $scope, "profile"
+ profile.$destroy() if profile
+ profile = fbutil.syncObject("users/" + user.uid);
+ profile.$bindTo $scope, "profile"
return
$scope.user = user
$scope.logout = simpleLogin.logout
$scope.messages = []
+ profile = null
loadProfile user
<% if( hasPasswordProvider ) { %>
$scope.changePassword = (oldPass, newPass, confirm) ->
@@ -43,7 +45,7 @@ angular.module("<%= scriptAppName %>").controller "AccountCtrl", ($scope, user,
else if newPass isnt confirm
error "Passwords do not match"
else
- simpleLogin.changePassword(user.email, oldPass, newPass).then (->
+ simpleLogin.changePassword(profile.email, oldPass, newPass).then (->
success "Password changed"
return
), error
diff --git a/templates/coffeescript/controllers/chat.coffee b/templates/coffeescript/controllers/chat.coffee
index 6bf3f1555..7a334c05b 100644
--- a/templates/coffeescript/controllers/chat.coffee
+++ b/templates/coffeescript/controllers/chat.coffee
@@ -15,7 +15,7 @@ angular.module("<%= scriptAppName %>").controller "ChatCtrl", ($scope, fbutil, $
), 5000
# synchronize a read-only, synchronized array of messages, limit to most recent 10
- $scope.messages = fbutil.syncArray("messages", limit: 10)
+ $scope.messages = fbutil.syncArray("messages", limitToLast: 10)
# display any errors
$scope.messages.$loaded().then null, alert
diff --git a/templates/coffeescript/routes.with.login.coffee b/templates/coffeescript/routes.with.login.coffee
index 35bf5cfad..22be6689b 100644
--- a/templates/coffeescript/routes.with.login.coffee
+++ b/templates/coffeescript/routes.with.login.coffee
@@ -7,7 +7,7 @@
# routes.js
Configure routes for use with Angular, and apply authentication security
-Add new routes to the ROUTES constant or use yo angularfire:route to create them
+Add new routes using `yo angularfire:route` with the optional --auth-required flag.
Any controller can be secured so that it will only load if user is logged in by
using `whenAuthenticated()` in place of `when()`. This requires the user to
@@ -115,4 +115,4 @@ angular.module("<%= scriptAppName %>").config([
$location.path loginRedirectPath if angular.isObject(err) and err.authRequired
return
-]).constant "SECURED_ROUTES", {}
\ No newline at end of file
+]).constant "SECURED_ROUTES", {}
diff --git a/templates/common/app/scripts/angularfire/config.js b/templates/common/app/scripts/angularfire/config.js
index b0f602791..77027abe3 100644
--- a/templates/common/app/scripts/angularfire/config.js
+++ b/templates/common/app/scripts/angularfire/config.js
@@ -2,9 +2,9 @@ angular.module('firebase.config', [])
.constant('FBURL', 'https://<%= firebaseName %>.firebaseio.com')<%
if( loginModule ) {
%>
- .constant('SIMPLE_LOGIN_PROVIDERS', ['<%= _.map(simpleLoginProviders, function(p) {
+ .constant('SIMPLE_LOGIN_PROVIDERS', ['<%= _.map(authProviders, function(p) {
return p.value;
}).join("','") %>'])
.constant('loginRedirectPath', '/login')<% }
- %>;
\ No newline at end of file
+ %>;
diff --git a/templates/common/app/scripts/angularfire/firebase.utils.js b/templates/common/app/scripts/angularfire/firebase.utils.js
index 3fd47255d..677d03890 100644
--- a/templates/common/app/scripts/angularfire/firebase.utils.js
+++ b/templates/common/app/scripts/angularfire/firebase.utils.js
@@ -75,7 +75,7 @@ angular.module('firebase.utils', ['firebase', 'firebase.config'])
function syncData(path, props) {
var ref = firebaseRef(path);
props = angular.extend({}, props);
- angular.forEach(['limit', 'startAt', 'endAt'], function(k) {
+ angular.forEach(['limitToFirst', 'limitToLast', 'orderByKey', 'orderByChild', 'orderByPriority', 'startAt', 'endAt'], function(k) {
if( props.hasOwnProperty(k) ) {
var v = props[k];
ref = ref[k].apply(ref, angular.isArray(v)? v : [v]);
@@ -84,4 +84,4 @@ angular.module('firebase.utils', ['firebase', 'firebase.config'])
});
return $firebase(ref, props);
}
- }]);
\ No newline at end of file
+ }]);
diff --git a/templates/common/app/scripts/angularfire/simpleLogin.js b/templates/common/app/scripts/angularfire/simpleLogin.js
index 75a9af876..45d9fbec9 100644
--- a/templates/common/app/scripts/angularfire/simpleLogin.js
+++ b/templates/common/app/scripts/angularfire/simpleLogin.js
@@ -2,77 +2,81 @@
'use strict';
angular.module('simpleLogin', ['firebase', 'firebase.utils', 'firebase.config'])
- // a simple wrapper on simpleLogin.getUser() that rejects the promise
+ // a simple wrapper that rejects the promise
// if the user does not exists (i.e. makes user required), useful for
// setting up secure routes that require authentication
.factory('authRequired', function(simpleLogin, $q) {
return function() {
- return simpleLogin.getUser().then(function (user) {
+ return simpleLogin.auth.$requireAuth().then(function (user) {
return user ? user : $q.reject({ authRequired: true });
});
};
})
- .factory('simpleLogin', function($firebaseSimpleLogin, fbutil, $q, $rootScope<% if( hasPasswordProvider ) { %>, createProfile, changeEmail<% } %>) {
- var auth = $firebaseSimpleLogin(fbutil.ref());
+ .factory('simpleLogin', function($firebaseAuth, fbutil, $q, $rootScope<% if( hasPasswordProvider ) { %>, createProfile<% } %>) {
+ var auth = $firebaseAuth(fbutil.ref());
var listeners = [];
function statusChange() {
fns.initialized = true;
- fns.user = auth.user || null;
+ fns.user = auth.$getAuth() || null;
angular.forEach(listeners, function(fn) {
fn(fns.user);
});
}
var fns = {
- user: null,
+ auth: auth,
+
+ user: null, //todo use getUser() and remove this var
initialized: false,
getUser: function() {
- return auth.$getCurrentUser();
+ return auth.$getAuth();
},
login: function(provider, opts) {
- return auth.$login(provider, opts);
+ return auth.$authWithOAuthPopup(provider, opts);
+ },
+
+ passwordLogin: function(creds, opts) {
+ return auth.$authWithPassword(creds, opts);
},
logout: function() {
- auth.$logout();
+ auth.$unauth();
},<% if( hasPasswordProvider ) { %>
- createAccount: function(email, pass, name) {
- return auth.$createUser(email, pass)
+ createAccount: function(email, pass, opts) {
+ return auth.$createUser({email: email, password: pass})
.then(function() {
// authenticate so we have permission to write to Firebase
- return fns.login('password', {email: email, password: pass});
+ return fns.passwordLogin({email: email, password: pass}, opts);
})
.then(function(user) {
// store user data in Firebase after creating account
- return createProfile(user.uid, email, name).then(function() {
+ return createProfile(user.uid, email/*, name*/).then(function() {
return user;
});
});
},
changePassword: function(email, oldpass, newpass) {
- return auth.$changePassword(email, oldpass, newpass);
+ return auth.$changePassword({email: email, oldPassword: oldpass, newPassword: newpass});
},
changeEmail: function(password, newEmail) {
- return changeEmail(password, fns.user.email, newEmail, this);
+ return auth.$changeEmail({password: password, oldEmail: fns.user.email, newEmail: newEmail});
},
removeUser: function(email, pass) {
- return auth.$removeUser(email, pass);
+ return auth.$removeUser({email: email, password: pass});
},<% } %>
watch: function(cb, $scope) {
listeners.push(cb);
- fns.getUser().then(function(user) {
- cb(user);
- });
+ auth.$waitForAuth(cb);
var unbind = function() {
var i = listeners.indexOf(cb);
if( i > -1 ) { listeners.splice(i, 1); }
@@ -84,10 +88,7 @@
}
};
- $rootScope.$on('$firebaseSimpleLogin:login', statusChange);
- $rootScope.$on('$firebaseSimpleLogin:logout', statusChange);
- $rootScope.$on('$firebaseSimpleLogin:error', statusChange);
- auth.$getCurrentUser(statusChange);
+ auth.$onAuth(statusChange);
return fns;
})<% if( hasPasswordProvider ) { %>
@@ -119,102 +120,5 @@
return def.promise;
};
- })
-
- .factory('changeEmail', function(fbutil, $q) {
- return function(password, oldEmail, newEmail, simpleLogin) {
- var ctx = { old: { email: oldEmail }, curr: { email: newEmail } };
-
- // execute activities in order; first we authenticate the user
- return authOldAccount()
- // then we fetch old account details
- .then( loadOldProfile )
- // then we create a new account
- .then( createNewAccount )
- // then we copy old account info
- .then( copyProfile )
- // and once they safely exist, then we can delete the old ones
- // we have to authenticate as the old user again
- .then( authOldAccount )
- .then( removeOldProfile )
- .then( removeOldLogin )
- // and now authenticate as the new user
- .then( authNewAccount )
- .catch(function(err) { console.error(err); return $q.reject(err); });
-
- function authOldAccount() {
- return simpleLogin.login('password', {email: ctx.old.email, password: password})
- .then(function(user) {
- ctx.old.uid = user.uid;
- });
- }
-
- function loadOldProfile() {
- var def = $q.defer();
- ctx.old.ref = fbutil.ref('users', ctx.old.uid);
- ctx.old.ref.once('value',
- function(snap){
- var dat = snap.val();
- if( dat === null ) {
- def.reject(oldEmail + ' not found');
- }
- else {
- ctx.old.name = dat.name;
- ctx.curr.name = dat.name;
- def.resolve();
- }
- },
- function(err){
- def.reject(err);
- });
- return def.promise;
- }
-
- function createNewAccount() {
- return simpleLogin.createAccount(ctx.curr.email, password, ctx.old.name).then(function(user) {
- ctx.curr.uid = user.uid;
- });
- }
-
- function copyProfile() {
- var d = $q.defer();
- ctx.curr.ref = fbutil.ref('users', ctx.curr.uid);
- var profile = {email: ctx.curr.email, name: ctx.curr.name};
- ctx.curr.ref.set(profile, function(err) {
- if (err) {
- d.reject(err);
- } else {
- d.resolve();
- }
- });
- return d.promise;
- }
-
- function removeOldProfile() {
- var d = $q.defer();
- ctx.old.ref.remove(function(err) {
- if (err) {
- d.reject(err);
- } else {
- d.resolve();
- }
- });
- return d.promise;
- }
-
- function removeOldLogin() {
- var def = $q.defer();
- simpleLogin.removeUser(ctx.old.email, password).then(function() {
- def.resolve();
- }, function(err) {
- def.reject(err);
- });
- return def.promise;
- }
-
- function authNewAccount() {
- return simpleLogin.login('password', {email: ctx.curr.email, password: password});
- }
- };
})<% } %>;
-})();
\ No newline at end of file
+})();
diff --git a/templates/common/app/views/login.html b/templates/common/app/views/login.html
index f5137d8e7..419519fa2 100644
--- a/templates/common/app/views/login.html
+++ b/templates/common/app/views/login.html
@@ -12,8 +12,8 @@ Login Page
<% if( hasOauthProviders ) { %>
<% } %><% if( hasPasswordProvider ) { %>
@@ -40,4 +40,4 @@ Login Page
{{err}}
-<% } %>
\ No newline at end of file
+<% } %>
diff --git a/templates/common/root/_bower.json b/templates/common/root/_bower.json
index ee6b9f328..dcaba8b15 100644
--- a/templates/common/root/_bower.json
+++ b/templates/common/root/_bower.json
@@ -12,9 +12,8 @@
"angular-sanitize": "<%= ngVer %>"<% } %><% if (animateModule) { %>,
"angular-animate": "<%= ngVer %>"<% } %><% if (touchModule) { %>,
"angular-touch": "<%= ngVer %>"<% } %><% if (routeModule) { %>,
- "angular-route": "<%= ngVer %>",
- "firebase": "<%= afconfig.bower.firebase %>"<% } %><% if (loginModule) { %>,
- "firebase-simple-login": "<%= afconfig.bower.simplelogin %>"<% } %>,
+ "angular-route": "<%= ngVer %>",<% } %>
+ "firebase": "<%= afconfig.bower.firebase %>",
"angularfire": "<%= afconfig.bower.angularfire %>"
},
"devDependencies": {
diff --git a/templates/javascript/controllers/account.js b/templates/javascript/controllers/account.js
index 5dfd61067..cb23837af 100644
--- a/templates/javascript/controllers/account.js
+++ b/templates/javascript/controllers/account.js
@@ -11,6 +11,7 @@ angular.module('<%= scriptAppName %>')
$scope.user = user;
$scope.logout = simpleLogin.logout;
$scope.messages = [];
+ var profile;
loadProfile(user);<% if( hasPasswordProvider ) { %>
$scope.changePassword = function(oldPass, newPass, confirm) {
@@ -22,7 +23,7 @@ angular.module('<%= scriptAppName %>')
error('Passwords do not match');
}
else {
- simpleLogin.changePassword(user.email, oldPass, newPass)
+ simpleLogin.changePassword(profile.email, oldPass, newPass)
.then(function() {
success('Password changed');
}, error);
@@ -56,9 +57,10 @@ angular.module('<%= scriptAppName %>')
}<% } %>
function loadProfile(user) {
- if( $scope.profile ) {
- $scope.profile.$destroy();
+ if( profile ) {
+ profile.$destroy();
}
- fbutil.syncObject('users/'+user.uid).$bindTo($scope, 'profile');
+ profile = fbutil.syncObject('users/'+user.uid);
+ profile.$bindTo($scope, 'profile');
}
- });
\ No newline at end of file
+ });
diff --git a/templates/javascript/controllers/chat.js b/templates/javascript/controllers/chat.js
index e1e9cf75a..8c0a334e7 100644
--- a/templates/javascript/controllers/chat.js
+++ b/templates/javascript/controllers/chat.js
@@ -9,7 +9,7 @@
angular.module('<%= scriptAppName %>')
.controller('ChatCtrl', function ($scope, fbutil, $timeout) {
// synchronize a read-only, synchronized array of messages, limit to most recent 10
- $scope.messages = fbutil.syncArray('messages', {limit: 10});
+ $scope.messages = fbutil.syncArray('messages', {limitToLast: 10});
// display any errors
$scope.messages.$loaded().catch(alert);
diff --git a/templates/javascript/controllers/login.js b/templates/javascript/controllers/login.js
index 07f78a2a0..97bdd9747 100644
--- a/templates/javascript/controllers/login.js
+++ b/templates/javascript/controllers/login.js
@@ -8,18 +8,28 @@
*/
angular.module('<%= scriptAppName %>')
.controller('LoginCtrl', function ($scope, simpleLogin, $location) {
- <% if( hasOauthProviders ) { %>$scope.oauthlogin = function(provider) {
- login(provider, {
- rememberMe: true
- });
+ <% if( hasOauthProviders ) { %>$scope.oauthLogin = function(provider) {
+ $scope.err = null;
+ simpleLogin.login(provider, {rememberMe: true}).then(
+ function() {
+ $location.path('/account');
+ },
+ function(err) {
+ $scope.err = err;
+ }
+ );
};
<% } %><% if( hasPasswordProvider ) { %>$scope.passwordLogin = function(email, pass) {
- login('password', {
- email: email,
- password: pass,
- rememberMe: true
- });
+ $scope.err = null;
+ simpleLogin.passwordLogin({email: email, password: pass}, {rememberMe: true}).then(
+ function() {
+ $location.path('/account');
+ },
+ function(err) {
+ $scope.err = err;
+ }
+ );
};
$scope.createAccount = function(email, pass, confirm) {
@@ -31,7 +41,7 @@ angular.module('<%= scriptAppName %>')
$scope.err = 'Passwords do not match';
}
else {
- simpleLogin.createAccount(email, pass/*, name*/)
+ simpleLogin.createAccount(email, pass, {rememberMe: true})
.then(function() {
$location.path('/account');
}, function(err) {
@@ -39,17 +49,6 @@ angular.module('<%= scriptAppName %>')
});
}
};
+ <% } %>
- <% } %>function login(provider, opts) {
- $scope.err = null;
- simpleLogin.login(provider, opts).then(
- function() {
- $location.path('/account');
- },
- function(err) {
- $scope.err = err;
- }
- );
- }
-
- });
\ No newline at end of file
+ });
diff --git a/templates/javascript/routes.with.login.js b/templates/javascript/routes.with.login.js
index e48132d4b..307c64153 100644
--- a/templates/javascript/routes.with.login.js
+++ b/templates/javascript/routes.with.login.js
@@ -6,7 +6,7 @@
* # routes.js
*
* Configure routes for use with Angular, and apply authentication security
- * Add new routes to the ROUTES constant or use yo angularfire:route to create them
+ * Add new routes using `yo angularfire:route` with the optional --auth-required flag.
*
* Any controller can be secured so that it will only load if user is logged in by
* using `whenAuthenticated()` in place of `when()`. This requires the user to