0dd8c34d by Adam Heath

Extensible frontend auth module, support for facebook and form.

1 parent bb992620
1 .*.swp
2 .tmp/
3 dist/
4 node_modules/
5 src/lib/
6 .grunt/
7 _SpecRunner.html
1 /* global module */
2
3 module.exports = function (grunt) {
4 /* global require */
5 'use strict';
6
7 var config = {};
8 config.base = 'src';
9 config.jshint = {
10 options: {
11 },
12 browserOptions: {
13 },
14 };
15 config.jscs = {
16 options: {
17 validateIndentation: 4,
18 reporter: 'console',
19 maxErrors: -1,
20 },
21 };
22 config.bower = {
23 directory: 'lib/bower',
24 };
25 config.jasmine = {
26 withCoverage: true,
27 };
28 var montyPython = require('grunt-monty-python')(grunt);
29 montyPython.createConfig(config);
30 };
1 {
2 "name": "frontend-auth",
3 "version": "2016.06.27-0",
4 "authors": [
5 "Adam Heath <doogie@brainfood.com>"
6 ],
7 "main": [
8 "src/scripts/Auth.js"
9 ],
10 "private": true,
11 "ignore": [
12 "**/.*",
13 "node_modules",
14 "src/lib",
15 "src/scripts/*.spec.js",
16 "src/scripts/config.js",
17 "src/scripts/main.js"
18 ],
19 "dependencies": {
20 "backbone": "",
21 "jquery": "",
22 "requirejs": "",
23 "underscore": ""
24 }
25 }
1 {
2 "name": "backbone-model-overlay",
3 "version": "2016.06.27-0",
4 "main": [
5 "src/scripts/Auth.js"
6 ],
7 "dependencies": {
8 "rivets": "",
9 "requirejs": ""
10 },
11 "devDependencies": {
12 "bower-requirejs": "~0.9.2",
13 "grunt": "~0",
14 "grunt-monty-python": "git+ssh://git@gitlab.brainfood.com:brainfood/frontend-auth.git"
15 },
16 "engines": {
17 "node": ">=0.8.0"
18 }
19 }
20
1 define(function(require) {
2 'use strict';
3 var module = require('module');
4 var $ = require('jquery');
5 var _ = require('underscore');
6 var Backbone = require('backbone');
7
8 function bindMethods() {
9 var args = _.toArray(arguments);
10 var self = args.shift();
11 _.each(args, function(methodName) {
12 self[methodName] = _.bind(self[methodName], self);
13 });
14 }
15 function attachStart(self, callback) {
16 var start = $.Deferred();
17 self.start = function() {
18 start.resolve();
19 return this;
20 };
21 return start.done(_.bind(callback, self));
22 }
23 function attachReady(self) {
24 var deferred = $.Deferred().done(function() {
25 self.set('isReady', true);
26 });
27
28 var promise = deferred.promise();
29 self.ready = function() {
30 return promise;
31 };
32 return deferred;
33 }
34
35 var FacebookProvider = Backbone.Model.extend({
36 initialize: function(data, globalOptions) {
37 // early facebook javascript load
38 require(['facebook']);
39 FacebookProvider.__super__.initialize.apply(this, arguments);
40 var self = this;
41 var fbLoaded = $.Deferred();
42 var ready = attachReady(this);
43 attachStart(this, function() {
44 require(['facebook'], function(FB) {
45 FB.init({
46 appId: globalOptions.appId,
47 status: true,
48 });
49 FB.getLoginStatus(function(response) {
50 self.set({
51 'status': response.status,
52 'authResponse': response.authResponse,
53 });
54 fbLoaded.resolve(FB);
55 });
56 }, function() {
57 /* global console:false */
58 console.log('error loading facebook javascript');
59 fbLoaded.reject();
60 });
61 });
62 fbLoaded.always(function() {
63 ready.resolve();
64 });
65 // log helper
66 this.on('change:status', function() {
67 /* global console:false */
68 switch (this.get('status')) {
69 case 'connected':
70 console.log('FB connected', this.get('authResponse'));
71 break;
72 case 'not_authorized':
73 console.log('FB not authorized');
74 break;
75 default:
76 console.log('FB not logged in');
77 break;
78 }
79 }, this);
80 this.login = function() {
81 fbLoaded.done(function(FB) {
82 FB.login(function(response) {
83 self.set({
84 'status': response.status,
85 'authResponse': response.authResponse,
86 });
87 self.trigger('provider:logged-in');
88 });
89 });
90 return self;
91 };
92 this.logout = function() {
93 fbLoaded.done(function(FB) {
94 if (self.get('authResponse')) {
95 FB.logout(function(response) {
96 self.set({
97 'status': response.status,
98 'authResponse': null,
99 });
100 self.trigger('provider:logged-out');
101 });
102 } else {
103 self.trigger('provider:logged-out');
104 }
105 });
106 return self;
107 };
108 this.attachProviderData = function(data) {
109 var authResponse = this.get('authResponse') || {};
110 _.extend(data, {
111 facebookAccessToken: authResponse.accessToken,
112 facebookUserId: authResponse.userID,
113 });
114 };
115 bindMethods(this, 'attachProviderData');
116 },
117 });
118 var FormProvider = Backbone.Model.extend({
119 initialize: function(data, globalOptions) {
120 FormProvider.__super__.initialize.apply(this, arguments);
121 var ready = attachReady(this);
122 attachStart(this, function() {
123 ready.resolve();
124 });
125 this.login = function() {
126 // TODO: possibly fetch USERNAME/PASSWORD from browser.localStorage
127 this.trigger('provider:logged-in');
128 return this;
129 };
130 this.logout = function() {
131 this.trigger('provider:logged-out');
132 return this;
133 };
134 this.attachProviderData = function(data) {
135 _.extend(data, {
136 USERNAME: this.get('USERNAME'),
137 PASSWORD: this.get('PASSWORD'),
138 });
139 };
140 bindMethods(this, 'login', 'logout', 'attachProviderData');
141 },
142 });
143 var Auth = Backbone.Model.extend({
144 defaults: function() {
145 return {
146 isLoggedIn: false,
147 permissions: new Backbone.Model(),
148 user: new Backbone.Model(),
149 providers: new Backbone.Model(),
150 };
151 },
152 initialize: function(data, globalOptions) {
153 Auth.__super__.initialize.apply(this, arguments);
154 (function(self, cookieName) {
155 if (!cookieName) {
156 return;
157 }
158 var sessionId = self.get('sessionId');
159 if (sessionId) {
160 $.cookie('cookieName', sessionId);
161 } else {
162 self.set('sessionId', $.cookie(cookieName));
163 }
164 self.on('change:sessionId', function() {
165 var sessionId = this.get('sessionId');
166 if (sessionId) {
167 $.cookie(cookieName, sessionId);
168 } else {
169 $.removeCookie(cookieName);
170 }
171 }, self);
172 })(this, globalOptions.cookieName);
173 var ready = attachReady(this);
174 attachStart(this, function() {
175 _.invoke(this.get('providers').values(), 'start');
176 });
177
178 var self = this;
179 function applyLoginResults(resultData) {
180 var userData = resultData.user;
181 var user = self.get('user');
182 self.set('isLoggedIn', !!userData);
183 if (userData) {
184 user.set(userData);
185 } else {
186 user.clear();
187 }
188 self.set('sessionId', resultData.sessionId);
189 var permissions = self.get('permissions');
190 var toErase = {}, toSet = {};
191 _.each(permissions.keys(), function(key) {
192 toErase[key] = false;
193 });
194 _.each(resultData.permissions, function(key) {
195 delete toErase[key];
196 toSet[key] = true;
197 });
198 permissions.set(toErase, {unset: true}).set(toSet);
199 }
200 function api(apiName, data) {
201 var apiHandler = globalOptions.api || function(apiName, data) {};
202 return apiHandler(apiName, data, {sessionId: self.get('sessionId')}).done(applyLoginResults);
203 }
204 var addProviderData = _.bind(function addProviderData() {
205 var args = _.toArray(arguments);
206 var providerToApply, data;
207 if (typeof args[0] === 'object') {
208 data = args.shift();
209 }
210 if (typeof args[0] === 'string') {
211 providerToApply = args[0];
212 }
213 data = _.extend({}, data);
214 var providers = this.get('providers');
215 _.each(providerToApply ? [providerToApply] : providers.keys(), function(providerKey) {
216 providers.get(providerKey).attachProviderData(data);
217 });
218 return data;
219 }, this);
220 var providerReadies = _.map(this.get('providers').values(), function(provider) {
221 return provider.ready();
222 });
223
224 $.when.apply($, providerReadies).done(_.bind(function() {
225 api('auth:startVisit', addProviderData({
226 userAgent: navigator.userAgent,
227 })).always(function() {
228 ready.resolve();
229 });
230 var providers = this.get('providers');
231 _.each(providers.keys(), function(providerKey) {
232 providers.get(providerKey).on('provider:logged-in', function() {
233 api('auth:login:' + providerKey, addProviderData(providerKey));
234 });
235 });
236 }, this));
237 this.login = function(how) {
238 var provider = how ? this.get('providers').get(how) : null;
239 if (provider) {
240 provider.login();
241 }
242 };
243 this.logout = function() {
244 _.invoke(this.get('providers').values(), 'logout');
245 api('auth:logout');
246 };
247 bindMethods(this, 'login', 'logout');
248 },
249 }, {
250 FacebookProvider: FacebookProvider,
251 FormProvider: FormProvider,
252 });
253 return Auth;
254 });
1 define(function(require) {
2 'use strict';
3
4 var $ = require('jquery');
5 window.jQuery = $;
6 var _ = require('underscore');
7 var Backbone = require('backbone');
8 var Auth = require('Auth');
9
10 describe('Auth', function() {
11 it('exists', function() {
12 expect(Auth).toBeDefined();
13 });
14 });
15 });
1 /* global require:true */
2 var require;
3 require = (function() {
4 'use strict';
5
6 var require = {
7 baseUrl: 'scripts',
8 config: {
9 'Auth': {},
10
11 },
12 shim: {
13 backbone: {
14 deps: ['underscore'],
15 exports: 'Backbone',
16 },
17 underscore: {
18 exports: '_',
19 },
20 },
21 paths: {
22 backbone: '../lib/bower/backbone/backbone',
23 underscore: '../lib/bower/underscore/underscore',
24 jquery: '../lib/bower/jquery/dist/jquery',
25 },
26 };
27
28 return require;
29 })();
1 /* global require */
2 define(function(require) {
3 'use strict';
4 });