0dd8c34d by Adam Heath

Extensible frontend auth module, support for facebook and form.

1 parent bb992620
.*.swp
.tmp/
dist/
node_modules/
src/lib/
.grunt/
_SpecRunner.html
/* global module */
module.exports = function (grunt) {
/* global require */
'use strict';
var config = {};
config.base = 'src';
config.jshint = {
options: {
},
browserOptions: {
},
};
config.jscs = {
options: {
validateIndentation: 4,
reporter: 'console',
maxErrors: -1,
},
};
config.bower = {
directory: 'lib/bower',
};
config.jasmine = {
withCoverage: true,
};
var montyPython = require('grunt-monty-python')(grunt);
montyPython.createConfig(config);
};
{
"name": "frontend-auth",
"version": "2016.06.27-0",
"authors": [
"Adam Heath <doogie@brainfood.com>"
],
"main": [
"src/scripts/Auth.js"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"src/lib",
"src/scripts/*.spec.js",
"src/scripts/config.js",
"src/scripts/main.js"
],
"dependencies": {
"backbone": "",
"jquery": "",
"requirejs": "",
"underscore": ""
}
}
{
"name": "backbone-model-overlay",
"version": "2016.06.27-0",
"main": [
"src/scripts/Auth.js"
],
"dependencies": {
"rivets": "",
"requirejs": ""
},
"devDependencies": {
"bower-requirejs": "~0.9.2",
"grunt": "~0",
"grunt-monty-python": "git+ssh://git@gitlab.brainfood.com:brainfood/frontend-auth.git"
},
"engines": {
"node": ">=0.8.0"
}
}
define(function(require) {
'use strict';
var module = require('module');
var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
function bindMethods() {
var args = _.toArray(arguments);
var self = args.shift();
_.each(args, function(methodName) {
self[methodName] = _.bind(self[methodName], self);
});
}
function attachStart(self, callback) {
var start = $.Deferred();
self.start = function() {
start.resolve();
return this;
};
return start.done(_.bind(callback, self));
}
function attachReady(self) {
var deferred = $.Deferred().done(function() {
self.set('isReady', true);
});
var promise = deferred.promise();
self.ready = function() {
return promise;
};
return deferred;
}
var FacebookProvider = Backbone.Model.extend({
initialize: function(data, globalOptions) {
// early facebook javascript load
require(['facebook']);
FacebookProvider.__super__.initialize.apply(this, arguments);
var self = this;
var fbLoaded = $.Deferred();
var ready = attachReady(this);
attachStart(this, function() {
require(['facebook'], function(FB) {
FB.init({
appId: globalOptions.appId,
status: true,
});
FB.getLoginStatus(function(response) {
self.set({
'status': response.status,
'authResponse': response.authResponse,
});
fbLoaded.resolve(FB);
});
}, function() {
/* global console:false */
console.log('error loading facebook javascript');
fbLoaded.reject();
});
});
fbLoaded.always(function() {
ready.resolve();
});
// log helper
this.on('change:status', function() {
/* global console:false */
switch (this.get('status')) {
case 'connected':
console.log('FB connected', this.get('authResponse'));
break;
case 'not_authorized':
console.log('FB not authorized');
break;
default:
console.log('FB not logged in');
break;
}
}, this);
this.login = function() {
fbLoaded.done(function(FB) {
FB.login(function(response) {
self.set({
'status': response.status,
'authResponse': response.authResponse,
});
self.trigger('provider:logged-in');
});
});
return self;
};
this.logout = function() {
fbLoaded.done(function(FB) {
if (self.get('authResponse')) {
FB.logout(function(response) {
self.set({
'status': response.status,
'authResponse': null,
});
self.trigger('provider:logged-out');
});
} else {
self.trigger('provider:logged-out');
}
});
return self;
};
this.attachProviderData = function(data) {
var authResponse = this.get('authResponse') || {};
_.extend(data, {
facebookAccessToken: authResponse.accessToken,
facebookUserId: authResponse.userID,
});
};
bindMethods(this, 'attachProviderData');
},
});
var FormProvider = Backbone.Model.extend({
initialize: function(data, globalOptions) {
FormProvider.__super__.initialize.apply(this, arguments);
var ready = attachReady(this);
attachStart(this, function() {
ready.resolve();
});
this.login = function() {
// TODO: possibly fetch USERNAME/PASSWORD from browser.localStorage
this.trigger('provider:logged-in');
return this;
};
this.logout = function() {
this.trigger('provider:logged-out');
return this;
};
this.attachProviderData = function(data) {
_.extend(data, {
USERNAME: this.get('USERNAME'),
PASSWORD: this.get('PASSWORD'),
});
};
bindMethods(this, 'login', 'logout', 'attachProviderData');
},
});
var Auth = Backbone.Model.extend({
defaults: function() {
return {
isLoggedIn: false,
permissions: new Backbone.Model(),
user: new Backbone.Model(),
providers: new Backbone.Model(),
};
},
initialize: function(data, globalOptions) {
Auth.__super__.initialize.apply(this, arguments);
(function(self, cookieName) {
if (!cookieName) {
return;
}
var sessionId = self.get('sessionId');
if (sessionId) {
$.cookie('cookieName', sessionId);
} else {
self.set('sessionId', $.cookie(cookieName));
}
self.on('change:sessionId', function() {
var sessionId = this.get('sessionId');
if (sessionId) {
$.cookie(cookieName, sessionId);
} else {
$.removeCookie(cookieName);
}
}, self);
})(this, globalOptions.cookieName);
var ready = attachReady(this);
attachStart(this, function() {
_.invoke(this.get('providers').values(), 'start');
});
var self = this;
function applyLoginResults(resultData) {
var userData = resultData.user;
var user = self.get('user');
self.set('isLoggedIn', !!userData);
if (userData) {
user.set(userData);
} else {
user.clear();
}
self.set('sessionId', resultData.sessionId);
var permissions = self.get('permissions');
var toErase = {}, toSet = {};
_.each(permissions.keys(), function(key) {
toErase[key] = false;
});
_.each(resultData.permissions, function(key) {
delete toErase[key];
toSet[key] = true;
});
permissions.set(toErase, {unset: true}).set(toSet);
}
function api(apiName, data) {
var apiHandler = globalOptions.api || function(apiName, data) {};
return apiHandler(apiName, data, {sessionId: self.get('sessionId')}).done(applyLoginResults);
}
var addProviderData = _.bind(function addProviderData() {
var args = _.toArray(arguments);
var providerToApply, data;
if (typeof args[0] === 'object') {
data = args.shift();
}
if (typeof args[0] === 'string') {
providerToApply = args[0];
}
data = _.extend({}, data);
var providers = this.get('providers');
_.each(providerToApply ? [providerToApply] : providers.keys(), function(providerKey) {
providers.get(providerKey).attachProviderData(data);
});
return data;
}, this);
var providerReadies = _.map(this.get('providers').values(), function(provider) {
return provider.ready();
});
$.when.apply($, providerReadies).done(_.bind(function() {
api('auth:startVisit', addProviderData({
userAgent: navigator.userAgent,
})).always(function() {
ready.resolve();
});
var providers = this.get('providers');
_.each(providers.keys(), function(providerKey) {
providers.get(providerKey).on('provider:logged-in', function() {
api('auth:login:' + providerKey, addProviderData(providerKey));
});
});
}, this));
this.login = function(how) {
var provider = how ? this.get('providers').get(how) : null;
if (provider) {
provider.login();
}
};
this.logout = function() {
_.invoke(this.get('providers').values(), 'logout');
api('auth:logout');
};
bindMethods(this, 'login', 'logout');
},
}, {
FacebookProvider: FacebookProvider,
FormProvider: FormProvider,
});
return Auth;
});
define(function(require) {
'use strict';
var $ = require('jquery');
window.jQuery = $;
var _ = require('underscore');
var Backbone = require('backbone');
var Auth = require('Auth');
describe('Auth', function() {
it('exists', function() {
expect(Auth).toBeDefined();
});
});
});
/* global require:true */
var require;
require = (function() {
'use strict';
var require = {
baseUrl: 'scripts',
config: {
'Auth': {},
},
shim: {
backbone: {
deps: ['underscore'],
exports: 'Backbone',
},
underscore: {
exports: '_',
},
},
paths: {
backbone: '../lib/bower/backbone/backbone',
underscore: '../lib/bower/underscore/underscore',
jquery: '../lib/bower/jquery/dist/jquery',
},
};
return require;
})();
/* global require */
define(function(require) {
'use strict';
});