├── .gitignore
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── dist
├── backand.debug.js
├── backand.min.js
└── backand.min.js.map
├── package.json
└── src
├── backand.js
├── globals.js
├── interceptor.js
├── services
├── auth.js
├── http_buffer.js
├── socket.js
└── user.js
└── utils
└── storage.js
/.gitignore:
--------------------------------------------------------------------------------
1 | logs/*
2 | !.gitkeep
3 | node_modules/
4 | Bower/node_modules/
5 | tmp
6 | .DS_Store
7 | .idea
8 | bower_components
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### 1.8.11 (2016-09-30)
4 |
5 | #### Features
6 |
7 | * Add automatic sign in to the socialSignin(). This option allow in the social sign-in to force Backand to signup
8 | user if they are not.
9 |
10 | ### 1.8.10 (2016-09-26)
11 |
12 | #### Features
13 |
14 | * Change socket URL to a new DNS
15 |
16 |
17 | ### 1.8.9 (2016-08-20)
18 |
19 | #### Features
20 |
21 | * Fix Facebook inApp login
22 |
23 |
24 | ### 1.8.2 (2015-11-19)
25 |
26 | #### Features
27 |
28 | * Add support for socket.io to get evenyts in real-time
29 | * **runSocket** Default false. Need to set to true to enable socket.io communications
30 | * **socketLogin** Need to be called to in order to establish connection to socket.io server - managed by the SDK internally
31 | * **on** listen on an action in order to het the data from the server side
32 |
33 | ### 1.8.1 (2015-11-11)
34 |
35 | #### Features
36 |
37 | * **callSignupOnSingInSocialError** The default is true. This allow to make only one call with social signin. In case the user is not sign up the SDK catch the error and call the sign up internally.
38 | * Fix issue with Facebook social login on Mobile devices
39 |
40 | ### 1.8.0 (2015-09-17)
41 |
42 |
43 | #### Features
44 |
45 | * **manageHttpInterceptor:** in config stage, tells Backand to manage all necessary authorization and authentication tokens for each request made to Backand
46 | * **isManagingHttpInterceptor:** returns whether Backand manages all necessary authorization and authentication tokens for each request made to Backand
47 | * **manageRefreshToken:** in config stage, tells Backand to manage re-authenticating using a refresh token when the session has expired
48 | * **isManagingRefreshToken:** returns whether Backand manages re-authenticating using a refresh token when the session has expired
49 | * **runSigninAfterSignup:** tells Backand to perform signing in after a user signs up
50 | * **EVENTS:** broadcasting EVENTS.SIGNIN, EVENTS.SIGNOUT, EVENTS.SIGNUP
51 | * **signup:** added parameter for additional data to be sent to Backand
52 | * **socialSignin:** added parameter for configuring sign in pop-up window spec
53 | * **socialSignup:**
54 | * added parameter for configuring sign in pop-up window spec
55 | * added parameter for additional data to be sent to Backand
56 |
57 |
58 | #### Breaking Changes
59 |
60 | * **getTokenName:** deprecated
61 | * **setTokenName:** deprecated
62 | * **manageDefaultHeaders:** deprecated, manageHttpInterceptor replaces the functionality
63 | * **isManagingDefaultHeaders:** deprecated, isManagingHttpInterceptor replaces the functionality
64 | * **signin:** removed parameter 'appName'. To set the application name, use setAppName()
65 | * **signup:** removed parameter 'appName'. To set the application name, use setAppName()
66 | * **requestResetPassword:** removed parameter 'appName'. To set the application name, use setAppName()
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | // Project configuration.
3 | grunt.initConfig({
4 | pkg: grunt.file.readJSON('package.json'),
5 | today: grunt.template.today('yyyy-mm-dd'),
6 | uglify: {
7 | options: {
8 | banner: '/*\n* Angular SDK to use with backand \n* @version <%= pkg.version %> - <%= today %>\n* @link https://www.backand.com \n* @author Itay Herskovits \n* @license MIT License, http://www.opensource.org/licenses/MIT\n */\n',
9 | sourceMap: true
10 | },
11 | js: {
12 | src: 'dist/<%= pkg.name %>.debug.js',
13 | dest: 'dist/<%= pkg.name %>.min.js'
14 | }
15 |
16 | },
17 | concat: {
18 | options: {
19 | banner: '/*\n* Angular SDK to use with backand \n* @version <%= pkg.version %> - <%= today %>\n* @link https://www.backand.com \n* @author Itay Herskovits \n* @license MIT License, http://www.opensource.org/licenses/MIT\n */\n'
20 | },
21 |
22 | js: {
23 | options: {
24 | banner: '(function () {\n',
25 | footer: '})();\n',
26 | separator: ';'
27 | },
28 | src: [
29 | 'src/utils/*.js',
30 | 'src/globals.js',
31 | 'src/backand.js',
32 | 'src/*.js',
33 | 'src/*/*.js'
34 | ],
35 | dest: 'dist/<%= pkg.name %>.debug.js'
36 | }
37 | },
38 |
39 | watch: {
40 | scripts: {
41 | files: ['src/**/*.js'],
42 | tasks: ['concat', 'uglify'],
43 | options: {
44 | spawn: false
45 | }
46 | }
47 | }
48 |
49 | });
50 |
51 |
52 | // Load the plugins
53 | grunt.loadNpmTasks('grunt-contrib-uglify');
54 | grunt.loadNpmTasks('grunt-contrib-concat');
55 | grunt.loadNpmTasks('grunt-contrib-watch');
56 |
57 |
58 | // Default task(s).
59 | grunt.registerTask('default', ['concat', 'uglify', 'watch']);
60 |
61 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Backand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angularbknd-sdk
2 | ###**NOTE - This SDK is deprecated, please use our new SDK at https://github.com/backand/vanilla-sdk**
3 |
4 | Backand SDK for Angular
5 |
6 | Install:
7 | `npm install`
8 | `bower install`
9 |
10 | Build:
11 | `grunt`
12 |
13 | Register on bower (only once at first time - done):
14 | `bower register angularbknd-sdk https://github.com/backand/angularbknd-sdk.git`
15 |
16 | To upgrade version:
17 |
18 | 1. Update the code of `backand.js`
19 | 2. Update the `bower.json` version
20 | * Check dependencies versions, if need to be upgraded
21 | * minor releases for bug fixes
22 | * major releases for changes in the API signature
23 | 3. Update the `package.json` version
24 | 4. Run `grunt` to build the min files
25 | 5. Update Changelog with the new release changes
26 | 6. Commit your changes `git commit -am "Made some awesome new changes, now its even awesome"`
27 | 7. Tag the commit `git tag -a 1.6.1 -m "Release version 1.6.1"`
28 | 8. Push to GitHub `git push origin master --tags`. To delete existing tag `git tag -d 1.6.1` and clear bower cache.
29 | 9. Create new folder in S3 bucket with the name of the version and upload the 2 files
30 | 10. Update documentation to point to the new release
31 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angularbknd-sdk",
3 | "description": "Backand SDK for Angular 1.x",
4 | "version": "1.8.11",
5 | "main": [
6 | "dist/backand.min.js"
7 | ],
8 | "ignore": [
9 | "Bower/*",
10 | "example/*",
11 | "Bower",
12 | "src",
13 | "example",
14 | "package.json",
15 | "Gruntfile.js"
16 | ],
17 | "homepage": "https://github.com/backand/angularbknd-sdk",
18 | "license": "MIT",
19 | "private": false,
20 | "dependencies": {
21 | "angular": "latest"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/dist/backand.debug.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Angular SDK to use with backand
3 | * @version 1.8.11 - 2016-09-30
4 | * @link https://www.backand.com
5 | * @author Itay Herskovits
6 | * @license MIT License, http://www.opensource.org/licenses/MIT
7 | */
8 |
9 | (function () {
10 | var BKStorage = (function () {
11 | 'use strict';
12 |
13 | var prefix = 'BACKAND';
14 |
15 | function Store (storeName, type) {
16 |
17 | var storageAPI;
18 |
19 | if( ['local', 'session'].indexOf(type) === -1 ) type = 'local';
20 |
21 | if (typeof window !== 'undefined' && typeof window[type + 'Storage'] !== 'undefined') {
22 | storageAPI = window[type + 'Storage'];
23 | } else {
24 | // We can fallback to other solution here inMemory Management
25 | // It could be cookies if needed
26 | storageAPI = {
27 | value: null,
28 | getItem: function (name, params) {
29 | return this.value;
30 | },
31 | setItem: function (name, params) {
32 | this.value = params;
33 | },
34 | removeItem: function (name, params) {
35 | this.value = null;
36 | }
37 | };
38 | }
39 |
40 | this.command = function (action, params) {
41 | return storageAPI[action + 'Item'](prefix + storeName, params || null);
42 | };
43 | }
44 |
45 | Store.prototype.get = function () {
46 | return JSON.parse(this.command('get'));
47 | };
48 |
49 | Store.prototype.set = function (value) {
50 | return this.command('set', JSON.stringify(value));
51 | };
52 |
53 | Store.prototype.clear = function () {
54 | this.command('set');
55 | return this;
56 | };
57 |
58 | return {
59 | register: function (storeName, type) {
60 | if(!storeName) {
61 | throw Error('Invalid Store Name');
62 | }
63 | this[storeName] = new Store(storeName, type);
64 | return this;
65 | },
66 |
67 | remove: function (storeName) {
68 | this[storeName].command('remove');
69 | delete this[storeName];
70 | return this;
71 | }
72 | };
73 |
74 | })();
75 | ;var http;
76 |
77 | var config = {
78 | apiUrl: 'https://api.backand.com',
79 | socketUrl: 'https://socket.backand.com',
80 | anonymousToken: null,
81 | signUpToken: null,
82 | isManagingHttpInterceptor: true,
83 | isManagingRefreshToken: true,
84 | runSigninAfterSignup: true,
85 | callSignupOnSingInSocialError : true, // tell code to run signup after signIn error because user is not registered to application
86 | appName: null,
87 | userProfileName: 'backand_user',
88 | isMobile: false,
89 | runSocket: false
90 | };
91 |
92 | var EVENTS = {
93 | SIGNIN: 'BackandSignIn',
94 | SIGNOUT: 'BackandSignOut',
95 | SIGNUP: 'BackandSignUp'
96 | };
97 |
98 | var socialProviders = {
99 | github: {name: 'github', label: 'Github', url: 'www.github.com', css: 'github', id: 1},
100 | google: {name: 'google', label: 'Google', url: 'www.google.com', css: 'google-plus', id: 2},
101 | facebook: {name: 'facebook', label: 'Facebook', url: 'www.facebook.com', css: 'facebook', id: 3},
102 | twitter: {name: 'twitter', label: 'Twitter', url: 'www.twitter.com', css: 'twitter', id: 4}
103 | };
104 |
105 | function getSocialUrl(providerName, isSignup, isAutoSignUp) {
106 | var provider = socialProviders[providerName];
107 | var action = isSignup ? 'up' : 'in';
108 | var autoSignUpParam = '';
109 | if (!isSignup && isAutoSignUp) {
110 | autoSignUpParam = "&signupIfNotSignedIn=true";
111 | }
112 | return 'user/socialSign' + action +
113 | '?provider=' + provider.label + autoSignUpParam +
114 | '&response_type=token&client_id=self&redirect_uri=' + provider.url +
115 | '&state=';
116 | }
117 |
118 | BKStorage.register('token');
119 | BKStorage.register('user');
120 |
121 | // get token or error message from url in social sign-in popup
122 | (function () {
123 | var dataRegex = /\?(data|error)=(.+)/;
124 | var dataMatch = dataRegex.exec(location.href);
125 | if (dataMatch && dataMatch[1] && dataMatch[2]) {
126 | var userData = {};
127 | userData[dataMatch[1]] = JSON.parse(decodeURI(dataMatch[2].replace(/#.*/, '')));
128 | window.opener.postMessage(JSON.stringify(userData), location.origin);
129 | }
130 | }());
131 | ;'use strict';
132 |
133 | angular.module('backand', [])
134 | .provider('Backand', function () {
135 |
136 | // Provider functions (should be called on module config block)
137 | this.getApiUrl = function () {
138 | return config.apiUrl;
139 | };
140 |
141 | this.setApiUrl = function (newApiUrl) {
142 | config.apiUrl = newApiUrl;
143 | return this;
144 | };
145 |
146 | this.setSocketUrl = function (newSocketUrl) {
147 | config.socketUrl = newSocketUrl;
148 | return this;
149 | };
150 |
151 | // deprecated
152 | this.getTokenName = function () {
153 | return null;
154 | };
155 |
156 | // deprecated
157 | this.setTokenName = function () {
158 | return this;
159 | };
160 |
161 | this.setAnonymousToken = function (anonymousToken) {
162 | config.anonymousToken = anonymousToken;
163 | return this;
164 | };
165 |
166 | this.setSignUpToken = function (signUpToken) {
167 | config.signUpToken = signUpToken;
168 | return this;
169 | };
170 |
171 | this.setAppName = function (appName) {
172 | config.appName = appName;
173 | return this;
174 | };
175 |
176 | // deprecated
177 | this.manageDefaultHeaders = function (isManagingDefaultHeaders) {
178 | return this;
179 | };
180 |
181 | this.manageHttpInterceptor = function (isManagingHttpInterceptor) {
182 | config.isManagingHttpInterceptor = isManagingHttpInterceptor == undefined ? true : isManagingHttpInterceptor;
183 | return this;
184 | };
185 |
186 | this.manageRefreshToken = function (isManagingRefreshToken) {
187 | config.isManagingRefreshToken = isManagingRefreshToken == undefined ? true : isManagingRefreshToken;
188 | return this;
189 | };
190 |
191 | this.runSigninAfterSignup = function (runSigninAfterSignup) {
192 | config.runSigninAfterSignup = runSigninAfterSignup == undefined ? true : runSigninAfterSignup;
193 | return this;
194 | };
195 |
196 | this.runSocket = function (runSocket) {
197 | config.runSocket = runSocket == undefined ? false : runSocket;
198 | return this;
199 | };
200 |
201 | // $get returns the service
202 | this.$get = ['BackandAuthService', 'BackandUserService','BackandSocketService', function (BackandAuthService, BackandUserService, BackandSocketService) {
203 | return new BackandService(BackandAuthService, BackandUserService, BackandSocketService);
204 | }];
205 |
206 | // Backand Service
207 | function BackandService(BackandAuthService, BackandUserService, BackandSocketService) {
208 | var self = this;
209 |
210 | self.EVENTS = EVENTS;
211 |
212 | self.setAppName = function (appName) {
213 | config.appName = appName;
214 | };
215 |
216 | self.signin = function (username, password) {
217 | return BackandAuthService.signin(username, password)
218 | };
219 |
220 | self.signout = function () {
221 | return BackandAuthService.signout();
222 | };
223 |
224 | self.signup = function (firstName, lastName, email, password, confirmPassword, parameters) {
225 | return BackandAuthService.signup(firstName, lastName, email, password, confirmPassword, parameters);
226 | };
227 |
228 | self.getSocialProviders = function () {
229 | return socialProviders;
230 | };
231 |
232 | self.socialSignin = function (provider, spec, isAutoSignUp) {
233 | return BackandAuthService.socialSignin(provider, spec, isAutoSignUp)
234 | };
235 |
236 | self.socialSignup = function (provider, parameters, spec, email) {
237 | return BackandAuthService.socialSignup(provider, parameters, spec, email)
238 | };
239 |
240 | self.socialSignInToken = function(provider, token){
241 | return BackandAuthService.socialSigninWithToken(provider, token)
242 | };
243 |
244 | self.socialSignUpToken = function (provider, token) {
245 | return BackandAuthService.socialSigninWithToken(provider, token, true)
246 | };
247 |
248 | self.socialSignInCode = function(provider, code){
249 | var returnUrl = window.location.origin;
250 |
251 | if(!provider || !code) {
252 | throw new Error("provide and code have to be valid values");
253 | }
254 |
255 | return BackandAuthService.socialSigninWithCode(provider, returnUrl, code);
256 | }
257 |
258 |
259 | self.socialSignUpCode = function(provider, code, username, firstname, lastname){
260 | if(!provider || !code) {
261 | throw new Error("provide and code have to be valid values");
262 | }
263 | var returnUrl = window.location.origin;
264 |
265 | return BackandAuthService.socialSignupWithCode(provider, returnUrl, code,
266 | username, firstname, lastname);
267 | }
268 |
269 | self.requestResetPassword = function (email) {
270 | return BackandAuthService.requestResetPassword(email);
271 | };
272 |
273 | self.resetPassword = function (newPassword, resetToken) {
274 | return BackandAuthService.resetPassword(newPassword, resetToken);
275 | };
276 |
277 | self.changePassword = function (oldPassword, newPassword) {
278 | return BackandAuthService.changePassword(oldPassword, newPassword)
279 | };
280 |
281 | self.setIsMobile = function(val){
282 | config.isMobile = val;
283 | };
284 |
285 | self.setRunSignupAfterErrorInSigninSocial = function(val){
286 | config.callSignupOnSingInSocialError = val;
287 | };
288 |
289 | self.getUserDetails = function (force) {
290 | return BackandUserService.getUserDetails(force)
291 | };
292 |
293 | self.getUsername = function () {
294 | return BackandUserService.getUsername();
295 | };
296 |
297 | self.getUserRole = function () {
298 | return BackandUserService.getUserRole();
299 | };
300 |
301 | self.getToken = function () {
302 | return BKStorage.token.get();
303 | };
304 |
305 | // deprecated
306 | self.getTokenName = function () {
307 | return null;
308 | };
309 |
310 | self.getApiUrl = function () {
311 | return config.apiUrl;
312 | };
313 |
314 | // deprecated
315 | self.isManagingDefaultHeaders = function () {
316 | return null;
317 | };
318 |
319 | self.isManagingHttpInterceptor = function () {
320 | return config.isManagingHttpInterceptor;
321 | };
322 |
323 | self.isManagingRefreshToken = function () {
324 | return config.isManagingRefreshToken && BKStorage.user.get() && BKStorage.user.get().refresh_token;
325 | };
326 |
327 | //Socket.io service
328 | self.isRunSocket = function () {
329 | return config.runSocket;
330 | };
331 |
332 | self.socketLogin = function(){
333 | if(config.runSocket)
334 | BackandSocketService.login(BKStorage.token.get(), config.anonymousToken, config.appName, config.socketUrl);
335 | };
336 |
337 | self.on = function(eventName, callback){
338 | BackandSocketService.on(eventName, callback);
339 | };
340 |
341 | // backward compatibility
342 | self.socialSignIn = self.socialSignin;
343 | self.socialSignUp = self.socialSignup;
344 | }
345 | })
346 | .run(['$injector', function ($injector) {
347 | $injector.invoke(['$http', function ($http) {
348 | // Cannot inject http to provider, so doing it here:
349 | http = $http;
350 | }]);
351 | $injector.invoke(['Backand', function (Backand) {
352 | // Cannot inject http to provider, so doing it here:
353 | Backand.socketLogin();
354 | }]);
355 | }]);
356 | ;angular.module('backand')
357 | .factory('BackandHttpInterceptor', ['$q', 'Backand', 'BackandHttpBufferService', 'BackandAuthService', HttpInterceptor])
358 | .config(['$httpProvider', function ($httpProvider) {
359 | $httpProvider.interceptors.push('BackandHttpInterceptor');
360 | }]);
361 |
362 | function HttpInterceptor ($q, Backand, BackandHttpBufferService, BackandAuthService) {
363 | return {
364 | request: function(httpConfig) {
365 | // Exclusions
366 | if (config.isManagingHttpInterceptor
367 | && httpConfig.url.match(Backand.getApiUrl())
368 | && !httpConfig.url.match(Backand.getApiUrl() + '/token')) {
369 |
370 | var token = BKStorage.token.get();
371 |
372 | if (token) {
373 | httpConfig.headers['Authorization'] = token;
374 | }
375 |
376 | if (config.anonymousToken) {
377 | httpConfig.headers['AnonymousToken'] = config.anonymousToken;
378 | }
379 | }
380 |
381 | return httpConfig;
382 | },
383 |
384 | responseError: function (rejection) {
385 |
386 | if (config.isManagingHttpInterceptor
387 | && rejection.config.url !== Backand.getApiUrl() + 'token'
388 | && config.isManagingRefreshToken
389 | && rejection.status === 401
390 | && rejection.data
391 | && rejection.data.Message === 'invalid or expired token') {
392 |
393 | BackandAuthService.refreshToken(Backand.getUsername());
394 |
395 | var deferred = $q.defer();
396 |
397 | BackandHttpBufferService.append(rejection.config, deferred);
398 | return deferred.promise;
399 | }
400 |
401 | return $q.reject(rejection);
402 | }
403 | }
404 | }
405 | ;angular.module('backand')
406 | .service('BackandAuthService', ['$q', '$rootScope', 'BackandHttpBufferService', 'BackandSocketService', BackandAuthService]);
407 |
408 | function BackandAuthService($q, $rootScope, BackandHttpBufferService, BackandSocketService) {
409 | var self = this;
410 | var authenticating = false;
411 | var NOT_SIGNEDIN_ERROR = 'The user is not signed up to';
412 | var dummyReturnAddress = 'http://www.backandaaaa.com';
413 |
414 | var urls = {
415 | signup: '/1/user/signup',
416 | token: '/token',
417 | requestResetPassword: '/1/user/requestResetPassword',
418 | resetPassword: '/1/user/resetPassword',
419 | changePassword: '/1/user/changePassword',
420 | socialLoginWithCode: '/1/user/PROVIDER/code',
421 | socialSingupWithCode: '/1/user/PROVIDER/signupCode',
422 | socialLoginWithToken: '/1/user/PROVIDER/token'
423 | };
424 |
425 | // basic authentication
426 |
427 | self.signin = function (username, password) {
428 | var userData = {
429 | grant_type: 'password',
430 | username: username,
431 | password: password,
432 | appname: config.appName
433 | };
434 | return authenticate(userData)
435 | };
436 |
437 | self.signout = function () {
438 | BKStorage.token.clear();
439 | BKStorage.user.clear();
440 |
441 | BackandHttpBufferService.rejectAll('signed out');
442 | $rootScope.$broadcast(EVENTS.SIGNOUT);
443 | return $q.when(true);
444 | };
445 |
446 | self.signup = function (firstName, lastName, email, password, confirmPassword, parameters) {
447 | return http({
448 | method: 'POST',
449 | url: config.apiUrl + urls.signup,
450 | headers: {
451 | 'SignUpToken': config.signUpToken
452 | },
453 | data: {
454 | firstName: firstName,
455 | lastName: lastName,
456 | email: email,
457 | password: password,
458 | confirmPassword: confirmPassword,
459 | parameters: parameters
460 | }
461 | }).then(function (response) {
462 | $rootScope.$broadcast(EVENTS.SIGNUP);
463 |
464 | if (config.runSigninAfterSignup
465 | && response.data.currentStatus === 1) {
466 | return self.signin(email, password);
467 | }
468 |
469 | return response;
470 | })
471 | };
472 |
473 | // social authentication
474 | self.socialSignin = function (provider, spec, isAutoSignUp) {
475 | return socialAuth(provider, false, spec, null, isAutoSignUp);
476 | };
477 |
478 | self.socialSignup = function (provider, parameters, spec, email) {
479 | self.signupParameters = parameters;
480 | self.inSocialSignup = true;
481 | return socialAuth(provider, true, spec, email);
482 | };
483 |
484 | self.socialSigninWithCode = function (provider, returnUrl, code) {
485 | if (authenticating) {
486 | return;
487 | }
488 |
489 | var authData = {
490 | "code": code,
491 | "clientId": '',
492 | "redirectUri": returnUrl,
493 | "appName": config.appName
494 | }
495 |
496 | authenticating = true;
497 | BKStorage.token.clear();
498 | return http({
499 | method: 'POST',
500 | url: config.apiUrl + urls.socialLoginWithCode.replace('PROVIDER', provider),
501 | headers: {
502 | 'Content-Type': 'application/json'
503 | },
504 | data: authData
505 |
506 | }).then(function (response) {
507 | if (response.data && response.data.access_token) {
508 | config.token = 'bearer ' + response.data.access_token;
509 |
510 | BKStorage.token.set(config.token);
511 | BKStorage.user.set(response.data);
512 |
513 | if (self.loginPromise) {
514 | self.loginPromise.resolve(config.token);
515 | }
516 |
517 | BackandHttpBufferService.retryAll();
518 | $rootScope.$broadcast(EVENTS.SIGNIN);
519 |
520 | if (config.runSocket)
521 | BackandSocketService.login(BKStorage.token.get(), config.anonymousToken, config.appName, config.socketUrl);
522 |
523 |
524 | } else if (self.loginPromise) {
525 | self.loginPromise.reject('token is undefined');
526 | }
527 | return response.data;
528 |
529 | }).catch(function (err) {
530 | if (self.loginPromise) {
531 | self.loginPromise.reject(err);
532 | }
533 | return $q.reject(err.data);
534 |
535 | }).finally(function () {
536 | authenticating = false;
537 | });
538 | }
539 |
540 | self.socialSignupWithCode = function (provider, returnUrl, code, username, firstname, lastname) {
541 | if (!code || !provider) {
542 | throw new Error("can't signup without code from provider")
543 | }
544 |
545 | var authData = {
546 | "code": code,
547 | "clientId": '',
548 | "redirectUri": returnUrl || null,
549 | "appName": config.appName,
550 | "firstName": firstname || null,
551 | "lastName": lastname || null,
552 | "userName": username || null
553 | }
554 |
555 | authenticating = true;
556 | BKStorage.token.clear();
557 | return http({
558 | method: 'POST',
559 | url: config.apiUrl + urls.socialSingupWithCode.replace('PROVIDER', provider),
560 | headers: {
561 | 'Content-Type': 'application/json'
562 | },
563 | data: authData
564 |
565 | }).then(function (response) {
566 | if (response.data && response.data.access_token) {
567 | config.token = 'bearer ' + response.data.access_token;
568 |
569 | BKStorage.token.set(config.token);
570 | BKStorage.user.set(response.data);
571 |
572 | if (self.loginPromise) {
573 | self.loginPromise.resolve(config.token);
574 | }
575 |
576 | BackandHttpBufferService.retryAll();
577 | $rootScope.$broadcast(EVENTS.SIGNIN);
578 |
579 | if (config.runSocket)
580 | BackandSocketService.login(BKStorage.token.get(), config.anonymousToken, config.appName, config.socketUrl);
581 |
582 |
583 | } else if (self.loginPromise) {
584 | self.loginPromise.reject('token is undefined');
585 | }
586 | return response.data;
587 |
588 | }).catch(function (err) {
589 | if (self.loginPromise) {
590 | self.loginPromise.reject(err);
591 | }
592 | return $q.reject(err.data);
593 |
594 | })
595 | }
596 |
597 | self.socialSigninWithToken = function (provider, token, isAutoSignUp) {
598 | if (authenticating) {
599 | return;
600 | }
601 |
602 | var url = config.apiUrl + urls.socialLoginWithToken.replace('PROVIDER', provider) + "?accessToken=" + encodeURIComponent(token) + "&appName=" + encodeURI(config.appName);
603 | if (isAutoSignUp) {
604 | url = url + "&signupIfNotSignedIn=true";
605 | }
606 | console.log(url);
607 | authenticating = true;
608 | BKStorage.token.clear();
609 | return http({
610 | method: 'GET',
611 | url: url,
612 | headers: {
613 | 'Content-Type': 'application/json'
614 | }
615 | }).then(function (response) {
616 | if (response.data && response.data.access_token) {
617 | config.token = 'bearer ' + response.data.access_token;
618 |
619 | BKStorage.token.set(config.token);
620 | BKStorage.user.set(response.data);
621 |
622 | if (self.loginPromise) {
623 | self.loginPromise.resolve(config.token);
624 | }
625 |
626 | BackandHttpBufferService.retryAll();
627 | $rootScope.$broadcast(EVENTS.SIGNIN);
628 |
629 | if (config.runSocket)
630 | BackandSocketService.login(BKStorage.token.get(), config.anonymousToken, config.appName, config.socketUrl);
631 |
632 |
633 | } else if (self.loginPromise) {
634 | self.loginPromise.reject('token is undefined');
635 | }
636 | return response.data;
637 |
638 | }).catch(function (err) {
639 | if (self.loginPromise) {
640 | self.loginPromise.reject(err);
641 | }
642 | return $q.reject(err.data);
643 |
644 | }).finally(function () {
645 | authenticating = false;
646 | });
647 | };
648 |
649 | function mobileSocialLoginInner(ref, isSignUp, provider, spec) {
650 | ref.addEventListener('loadstart', function (e) {
651 | if (e.url.indexOf(dummyReturnAddress) == 0) { // mean startWith
652 |
653 | try {
654 | ref.close();
655 | }
656 | catch (err) {
657 | }
658 |
659 | var url = e.url;
660 | // handle case of misformatted json from server
661 | if (url.indexOf('#') === (url.length - 1)) // last char
662 | {
663 | url = url.slice(0, url.length - 1);
664 | }
665 |
666 | // error return from server
667 | if (url.indexOf('error=') > -1) {
668 | // handle case of strange chars in the end of url on login error
669 | var urlParsed = new URL(url);
670 |
671 | var dataStr = decodeURI(urlParsed.search).split('error=')[1];
672 | dataStr = dataStr.replace(/%3A/g, ':').replace(/%2C/g, ',');
673 | var userData = JSON.parse(dataStr);
674 | userData.message = userData.message ? userData.message.replace(/\+/g, ' ') : '';
675 | if (!isSignUp && config.callSignupOnSingInSocialError && userData.message.indexOf(NOT_SIGNEDIN_ERROR) > -1) { // check is right error
676 | socialAuth(provider, true, spec);
677 | return;
678 | }
679 |
680 | var rejection = {
681 | data: userData.message + ' (signing in with ' + userData.provider + ')'
682 | };
683 |
684 | rejection.error_description = rejection.data;
685 | self.loginPromise.reject(rejection);
686 | return;
687 | }
688 |
689 | // login is OK
690 | var dataStr = decodeURI(url).split('/#/?data=')[1];
691 | dataStr = dataStr.replace(/%3A/g, ':').replace(/%2C/g, ',');
692 | var userData = JSON.parse(dataStr);
693 | userData.message = userData.message ? userData.message.replace(/\+/g, ' ') : '';
694 | if (self.inSocialSignup) {
695 | self.inSocialSignup = false;
696 | $rootScope.$broadcast(EVENTS.SIGNUP);
697 | }
698 | signinWithToken(userData);
699 | }
700 | }
701 | );
702 | }
703 |
704 | function socialAuth(provider, isSignUp, spec, email, isAutoSignUp) {
705 |
706 | if (!socialProviders[provider]) {
707 | throw Error('Unknown Social Provider');
708 | }
709 |
710 | if (!self.loginPromise)
711 | self.loginPromise = $q.defer();
712 | else
713 | self.signUpPromise = $q.defer();
714 |
715 | if (config.isMobile) {
716 |
717 | var ref = window.open(
718 | config.apiUrl + '/1/'
719 | + getSocialUrl(provider, isSignUp, isAutoSignUp)
720 | + '&appname=' + config.appName + (email ? ("&email=" + email) : '')
721 | + '&returnAddress=' + dummyReturnAddress,
722 | 'id1',
723 | spec || 'left=1, top=1, width=600, height=600');
724 |
725 | mobileSocialLoginInner(ref, isSignUp, provider, spec);
726 | }
727 | else {
728 | self.socialAuthWindow = window.open(
729 | config.apiUrl + '/1/'
730 | + getSocialUrl(provider, isSignUp, isAutoSignUp)
731 | + '&appname=' + config.appName + (email ? ("&email=" + email) : '')
732 | + '&returnAddress=',
733 | 'id1',
734 | spec || 'left=1, top=1, width=600, height=600');
735 |
736 | window.addEventListener('message', (function (provider, spec) {
737 | return function (e) {
738 | window.removeEventListener('message', arguments.callee);
739 | setUserDataFromToken(e, provider, spec)
740 | }
741 | })(provider, spec), false);
742 | }
743 | return self.loginPromise.promise;
744 | }
745 |
746 | function setUserDataFromToken(event, provider, spec) {
747 | console.log(event, provider, spec);
748 | self.socialAuthWindow.close();
749 | self.socialAuthWindow = null;
750 |
751 | if (event.origin !== location.origin) {
752 | return;
753 | }
754 |
755 | var userData = JSON.parse(event.data);
756 | if (userData.error) {
757 |
758 | if (config.callSignupOnSingInSocialError && userData.error.message.indexOf(NOT_SIGNEDIN_ERROR) > -1) { // check is right error
759 | socialAuth(provider, true, spec);
760 | return;
761 | }
762 |
763 | var rejection = {
764 | data: userData.error.message + ' (signing in with ' + userData.error.provider + ')'
765 | };
766 | rejection.error_description = rejection.data;
767 | self.loginPromise.reject(rejection);
768 |
769 | }
770 | else if (userData.data) {
771 | if (self.inSocialSignup) {
772 | self.inSocialSignup = false;
773 | $rootScope.$broadcast(EVENTS.SIGNUP);
774 | }
775 | return signinWithToken(userData.data);
776 |
777 | }
778 | else {
779 | self.loginPromise.reject();
780 | }
781 | }
782 |
783 | // tokens authentication
784 | function signinWithToken(userData) {
785 | var tokenData = {
786 | grant_type: 'password',
787 | accessToken: userData.access_token,
788 | appName: config.appName
789 | };
790 |
791 | if (self.signupParameters) {
792 | tokenData.parameters = self.signupParameters;
793 | self.signupParameters = null;
794 | }
795 |
796 | return authenticate(tokenData)
797 | }
798 |
799 | self.refreshToken = function (username) {
800 | BKStorage.token.clear();
801 |
802 | var user = BKStorage.user.get();
803 | var refreshToken;
804 | if (!user || !(refreshToken = BKStorage.user.get().refresh_token)) {
805 | return;
806 | }
807 |
808 | var tokenData = {
809 | grant_type: 'password',
810 | refreshToken: refreshToken,
811 | username: username,
812 | appName: config.appName
813 | };
814 | return authenticate(tokenData);
815 | };
816 |
817 |
818 | function authenticate(authData) {
819 | if (authenticating) {
820 | return;
821 | }
822 | authenticating = true;
823 | BKStorage.token.clear();
824 | return http({
825 | method: 'POST',
826 | url: config.apiUrl + urls.token,
827 | headers: {
828 | 'Content-Type': 'application/x-www-form-urlencoded'
829 | },
830 | transformRequest: function (obj) {
831 | var str = [];
832 | angular.forEach(obj, function (value, key) {
833 | str.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
834 | });
835 | return str.join("&");
836 | },
837 | data: authData
838 |
839 | }).then(function (response) {
840 | if (response.data && response.data.access_token) {
841 | config.token = 'bearer ' + response.data.access_token;
842 |
843 | BKStorage.token.set(config.token);
844 | BKStorage.user.set(response.data);
845 |
846 | if (self.loginPromise) {
847 | self.loginPromise.resolve(config.token);
848 | }
849 | if (self.signUpPromise){
850 | self.signUpPromise.resolve(config.token);
851 | }
852 |
853 | BackandHttpBufferService.retryAll();
854 | $rootScope.$broadcast(EVENTS.SIGNIN);
855 |
856 | if (config.runSocket)
857 | BackandSocketService.login(BKStorage.token.get(), config.anonymousToken, config.appName, config.socketUrl);
858 |
859 |
860 | } else if (self.loginPromise) {
861 | self.loginPromise.reject('token is undefined');
862 | }
863 | return response.data;
864 |
865 | }).catch(function (err) {
866 | if (self.loginPromise) {
867 | self.loginPromise.reject(err);
868 | }
869 | return $q.reject(err.data);
870 |
871 | }).finally(function () {
872 | authenticating = false;
873 | });
874 | }
875 |
876 |
877 | // password management
878 |
879 | self.requestResetPassword = function (email) {
880 | return http({
881 | method: 'POST',
882 | url: config.apiUrl + urls.requestResetPassword,
883 | data: {
884 | appName: config.appName,
885 | username: email
886 | }
887 | })
888 | };
889 |
890 | self.resetPassword = function (newPassword, resetToken) {
891 | return http({
892 | method: 'POST',
893 | url: config.apiUrl + urls.resetPassword,
894 | data: {
895 | newPassword: newPassword,
896 | resetToken: resetToken
897 | }
898 | });
899 | };
900 |
901 | self.changePassword = function (oldPassword, newPassword) {
902 | return http({
903 | method: 'POST',
904 | url: config.apiUrl + urls.changePassword,
905 | data: {
906 | oldPassword: oldPassword,
907 | newPassword: newPassword
908 | }
909 | });
910 | };
911 |
912 |
913 | }
914 | ;(function () {
915 | angular.module('backand').service('BackandHttpBufferService', HttpBufferService);
916 |
917 | function HttpBufferService() {
918 | var self = this;
919 | var buffer = [];
920 |
921 | function retryHttpRequest(config, deferred) {
922 | function successCallback(response) {
923 | deferred.resolve(response);
924 | }
925 | function errorCallback(response) {
926 | deferred.reject(response);
927 | }
928 |
929 | http(config).then(successCallback, errorCallback);
930 | }
931 |
932 | self.append = function (config, deferred) {
933 | buffer.push({
934 | config: config,
935 | deferred: deferred
936 | });
937 | };
938 |
939 | self.rejectAll = function (reason) {
940 | if (reason) {
941 | for (var i = 0; i < buffer.length; ++i) {
942 | buffer[i].deferred.reject(reason);
943 | }
944 | }
945 | buffer = [];
946 | };
947 |
948 | function updater (config) {
949 | delete config.headers.Authorization;
950 | return config;
951 | }
952 |
953 | self.retryAll = function () {
954 | for (var i = 0; i < buffer.length; ++i) {
955 | retryHttpRequest(updater(buffer[i].config), buffer[i].deferred);
956 | }
957 | buffer = [];
958 | }
959 |
960 | }
961 |
962 | })();
963 | ;/**
964 | * Created by Itay on 11/17/15.
965 | */
966 | angular.module('backand')
967 | .service('BackandSocketService', ['$rootScope', BackandSocketService]);
968 |
969 | function BackandSocketService ($rootScope) {
970 |
971 | var self = this;
972 |
973 | self.socket = {on: function(){}}; //io.connect('http://localhost:4000');
974 |
975 | self.login = function(token, anonymousToken, appName, url){
976 | self.socket = io.connect(url, {'forceNew':true });
977 |
978 | self.socket.on('connect', function(){
979 | console.log('connected');
980 | self.socket.emit("login", token, anonymousToken, appName);
981 | });
982 |
983 | self.socket.on('disconnect', function() {
984 | console.log('disconnect');
985 | });
986 |
987 | self.socket.on('reconnecting', function() {
988 | console.log('reconnecting');
989 | });
990 |
991 | };
992 |
993 | self.on = function (eventName, callback) {
994 | self.socket.on(eventName, function () {
995 | var args = arguments;
996 | $rootScope.$apply(function () {
997 | callback.apply(self.socket, args);
998 | });
999 | });
1000 | };
1001 |
1002 | self.emit = function (eventName, data, callback) {
1003 | self.socket.emit(eventName, data, function () {
1004 | var args = arguments;
1005 | $rootScope.$apply(function () {
1006 | if (callback) {
1007 | callback.apply(self.socket, args);
1008 | }
1009 | });
1010 | })
1011 | }
1012 | };angular.module('backand').service('BackandUserService', ['$q', BackandUserService]);
1013 |
1014 | function BackandUserService ($q) {
1015 | var self = this;
1016 |
1017 | self.getUserDetails = function (force) {
1018 | var deferred = $q.defer();
1019 | if (force) {
1020 | http({
1021 | method: 'GET',
1022 | url: config.apiUrl + '/api/account/profile'
1023 | })
1024 | .success(function (profile) {
1025 | BKStorage.user.set(angular.extend(BKStorage.user.get(), profile));
1026 | deferred.resolve(BKStorage.user.get());
1027 | })
1028 | } else {
1029 | deferred.resolve(BKStorage.user.get());
1030 | }
1031 | return deferred.promise;
1032 | };
1033 |
1034 | self.getUsername = function () {
1035 | var userDetails;
1036 | return (userDetails = BKStorage.user.get()) ? userDetails.username : null;
1037 | };
1038 |
1039 | self.getUserRole = function () {
1040 | var userDetails;
1041 | return (userDetails = BKStorage.user.get()) ? userDetails.role : null;
1042 | };
1043 |
1044 | }
1045 | })();
1046 |
--------------------------------------------------------------------------------
/dist/backand.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Angular SDK to use with backand
3 | * @version 1.8.11 - 2016-09-30
4 | * @link https://www.backand.com
5 | * @author Itay Herskovits
6 | * @license MIT License, http://www.opensource.org/licenses/MIT
7 | */
8 |
9 | !function(){function a(a,b,c){var d=j[a],e=b?"up":"in",f="";return!b&&c&&(f="&signupIfNotSignedIn=true"),"user/socialSign"+e+"?provider="+d.label+f+"&response_type=token&client_id=self&redirect_uri="+d.url+"&state="}function b(a,b,c,d){return{request:function(a){if(h.isManagingHttpInterceptor&&a.url.match(b.getApiUrl())&&!a.url.match(b.getApiUrl()+"/token")){var c=g.token.get();c&&(a.headers.Authorization=c),h.anonymousToken&&(a.headers.AnonymousToken=h.anonymousToken)}return a},responseError:function(e){if(h.isManagingHttpInterceptor&&e.config.url!==b.getApiUrl()+"token"&&h.isManagingRefreshToken&&401===e.status&&e.data&&"invalid or expired token"===e.data.Message){d.refreshToken(b.getUsername());var f=a.defer();return c.append(e.config,f),f.promise}return a.reject(e)}}}function c(b,c,d,e){function k(a,b,d,e){a.addEventListener("loadstart",function(f){if(0==f.url.indexOf(s)){try{a.close()}catch(g){}var j=f.url;if(j.indexOf("#")===j.length-1&&(j=j.slice(0,j.length-1)),j.indexOf("error=")>-1){var k=new URL(j),m=decodeURI(k.search).split("error=")[1];m=m.replace(/%3A/g,":").replace(/%2C/g,",");var o=JSON.parse(m);if(o.message=o.message?o.message.replace(/\+/g," "):"",!b&&h.callSignupOnSingInSocialError&&o.message.indexOf(r)>-1)return void l(d,!0,e);var q={data:o.message+" (signing in with "+o.provider+")"};return q.error_description=q.data,void p.loginPromise.reject(q)}var m=decodeURI(j).split("/#/?data=")[1];m=m.replace(/%3A/g,":").replace(/%2C/g,",");var o=JSON.parse(m);o.message=o.message?o.message.replace(/\+/g," "):"",p.inSocialSignup&&(p.inSocialSignup=!1,c.$broadcast(i.SIGNUP)),n(o)}})}function l(c,d,e,f,g){if(!j[c])throw Error("Unknown Social Provider");if(p.loginPromise?p.signUpPromise=b.defer():p.loginPromise=b.defer(),h.isMobile){var i=window.open(h.apiUrl+"/1/"+a(c,d,g)+"&appname="+h.appName+(f?"&email="+f:"")+"&returnAddress="+s,"id1",e||"left=1, top=1, width=600, height=600");k(i,d,c,e)}else p.socialAuthWindow=window.open(h.apiUrl+"/1/"+a(c,d,g)+"&appname="+h.appName+(f?"&email="+f:"")+"&returnAddress=","id1",e||"left=1, top=1, width=600, height=600"),window.addEventListener("message",function(a,b){return function(c){window.removeEventListener("message",arguments.callee),m(c,a,b)}}(c,e),!1);return p.loginPromise.promise}function m(a,b,d){if(console.log(a,b,d),p.socialAuthWindow.close(),p.socialAuthWindow=null,a.origin===location.origin){var e=JSON.parse(a.data);if(e.error){if(h.callSignupOnSingInSocialError&&e.error.message.indexOf(r)>-1)return void l(b,!0,d);var f={data:e.error.message+" (signing in with "+e.error.provider+")"};f.error_description=f.data,p.loginPromise.reject(f)}else{if(e.data)return p.inSocialSignup&&(p.inSocialSignup=!1,c.$broadcast(i.SIGNUP)),n(e.data);p.loginPromise.reject()}}}function n(a){var b={grant_type:"password",accessToken:a.access_token,appName:h.appName};return p.signupParameters&&(b.parameters=p.signupParameters,p.signupParameters=null),o(b)}function o(a){return q?void 0:(q=!0,g.token.clear(),f({method:"POST",url:h.apiUrl+t.token,headers:{"Content-Type":"application/x-www-form-urlencoded"},transformRequest:function(a){var b=[];return angular.forEach(a,function(a,c){b.push(encodeURIComponent(c)+"="+encodeURIComponent(a))}),b.join("&")},data:a}).then(function(a){return a.data&&a.data.access_token?(h.token="bearer "+a.data.access_token,g.token.set(h.token),g.user.set(a.data),p.loginPromise&&p.loginPromise.resolve(h.token),p.signUpPromise&&p.signUpPromise.resolve(h.token),d.retryAll(),c.$broadcast(i.SIGNIN),h.runSocket&&e.login(g.token.get(),h.anonymousToken,h.appName,h.socketUrl)):p.loginPromise&&p.loginPromise.reject("token is undefined"),a.data})["catch"](function(a){return p.loginPromise&&p.loginPromise.reject(a),b.reject(a.data)})["finally"](function(){q=!1}))}var p=this,q=!1,r="The user is not signed up to",s="http://www.backandaaaa.com",t={signup:"/1/user/signup",token:"/token",requestResetPassword:"/1/user/requestResetPassword",resetPassword:"/1/user/resetPassword",changePassword:"/1/user/changePassword",socialLoginWithCode:"/1/user/PROVIDER/code",socialSingupWithCode:"/1/user/PROVIDER/signupCode",socialLoginWithToken:"/1/user/PROVIDER/token"};p.signin=function(a,b){var c={grant_type:"password",username:a,password:b,appname:h.appName};return o(c)},p.signout=function(){return g.token.clear(),g.user.clear(),d.rejectAll("signed out"),c.$broadcast(i.SIGNOUT),b.when(!0)},p.signup=function(a,b,d,e,g,j){return f({method:"POST",url:h.apiUrl+t.signup,headers:{SignUpToken:h.signUpToken},data:{firstName:a,lastName:b,email:d,password:e,confirmPassword:g,parameters:j}}).then(function(a){return c.$broadcast(i.SIGNUP),h.runSigninAfterSignup&&1===a.data.currentStatus?p.signin(d,e):a})},p.socialSignin=function(a,b,c){return l(a,!1,b,null,c)},p.socialSignup=function(a,b,c,d){return p.signupParameters=b,p.inSocialSignup=!0,l(a,!0,c,d)},p.socialSigninWithCode=function(a,j,k){if(!q){var l={code:k,clientId:"",redirectUri:j,appName:h.appName};return q=!0,g.token.clear(),f({method:"POST",url:h.apiUrl+t.socialLoginWithCode.replace("PROVIDER",a),headers:{"Content-Type":"application/json"},data:l}).then(function(a){return a.data&&a.data.access_token?(h.token="bearer "+a.data.access_token,g.token.set(h.token),g.user.set(a.data),p.loginPromise&&p.loginPromise.resolve(h.token),d.retryAll(),c.$broadcast(i.SIGNIN),h.runSocket&&e.login(g.token.get(),h.anonymousToken,h.appName,h.socketUrl)):p.loginPromise&&p.loginPromise.reject("token is undefined"),a.data})["catch"](function(a){return p.loginPromise&&p.loginPromise.reject(a),b.reject(a.data)})["finally"](function(){q=!1})}},p.socialSignupWithCode=function(a,j,k,l,m,n){if(!k||!a)throw new Error("can't signup without code from provider");var o={code:k,clientId:"",redirectUri:j||null,appName:h.appName,firstName:m||null,lastName:n||null,userName:l||null};return q=!0,g.token.clear(),f({method:"POST",url:h.apiUrl+t.socialSingupWithCode.replace("PROVIDER",a),headers:{"Content-Type":"application/json"},data:o}).then(function(a){return a.data&&a.data.access_token?(h.token="bearer "+a.data.access_token,g.token.set(h.token),g.user.set(a.data),p.loginPromise&&p.loginPromise.resolve(h.token),d.retryAll(),c.$broadcast(i.SIGNIN),h.runSocket&&e.login(g.token.get(),h.anonymousToken,h.appName,h.socketUrl)):p.loginPromise&&p.loginPromise.reject("token is undefined"),a.data})["catch"](function(a){return p.loginPromise&&p.loginPromise.reject(a),b.reject(a.data)})},p.socialSigninWithToken=function(a,j,k){if(!q){var l=h.apiUrl+t.socialLoginWithToken.replace("PROVIDER",a)+"?accessToken="+encodeURIComponent(j)+"&appName="+encodeURI(h.appName);return k&&(l+="&signupIfNotSignedIn=true"),console.log(l),q=!0,g.token.clear(),f({method:"GET",url:l,headers:{"Content-Type":"application/json"}}).then(function(a){return a.data&&a.data.access_token?(h.token="bearer "+a.data.access_token,g.token.set(h.token),g.user.set(a.data),p.loginPromise&&p.loginPromise.resolve(h.token),d.retryAll(),c.$broadcast(i.SIGNIN),h.runSocket&&e.login(g.token.get(),h.anonymousToken,h.appName,h.socketUrl)):p.loginPromise&&p.loginPromise.reject("token is undefined"),a.data})["catch"](function(a){return p.loginPromise&&p.loginPromise.reject(a),b.reject(a.data)})["finally"](function(){q=!1})}},p.refreshToken=function(a){g.token.clear();var b,c=g.user.get();if(c&&(b=g.user.get().refresh_token)){var d={grant_type:"password",refreshToken:b,username:a,appName:h.appName};return o(d)}},p.requestResetPassword=function(a){return f({method:"POST",url:h.apiUrl+t.requestResetPassword,data:{appName:h.appName,username:a}})},p.resetPassword=function(a,b){return f({method:"POST",url:h.apiUrl+t.resetPassword,data:{newPassword:a,resetToken:b}})},p.changePassword=function(a,b){return f({method:"POST",url:h.apiUrl+t.changePassword,data:{oldPassword:a,newPassword:b}})}}function d(a){var b=this;b.socket={on:function(){}},b.login=function(a,c,d,e){b.socket=io.connect(e,{forceNew:!0}),b.socket.on("connect",function(){console.log("connected"),b.socket.emit("login",a,c,d)}),b.socket.on("disconnect",function(){console.log("disconnect")}),b.socket.on("reconnecting",function(){console.log("reconnecting")})},b.on=function(c,d){b.socket.on(c,function(){var c=arguments;a.$apply(function(){d.apply(b.socket,c)})})},b.emit=function(c,d,e){b.socket.emit(c,d,function(){var c=arguments;a.$apply(function(){e&&e.apply(b.socket,c)})})}}function e(a){var b=this;b.getUserDetails=function(b){var c=a.defer();return b?f({method:"GET",url:h.apiUrl+"/api/account/profile"}).success(function(a){g.user.set(angular.extend(g.user.get(),a)),c.resolve(g.user.get())}):c.resolve(g.user.get()),c.promise},b.getUsername=function(){var a;return(a=g.user.get())?a.username:null},b.getUserRole=function(){var a;return(a=g.user.get())?a.role:null}}var f,g=function(){"use strict";function a(a,c){var d;-1===["local","session"].indexOf(c)&&(c="local"),d="undefined"!=typeof window&&"undefined"!=typeof window[c+"Storage"]?window[c+"Storage"]:{value:null,getItem:function(a,b){return this.value},setItem:function(a,b){this.value=b},removeItem:function(a,b){this.value=null}},this.command=function(c,e){return d[c+"Item"](b+a,e||null)}}var b="BACKAND";return a.prototype.get=function(){return JSON.parse(this.command("get"))},a.prototype.set=function(a){return this.command("set",JSON.stringify(a))},a.prototype.clear=function(){return this.command("set"),this},{register:function(b,c){if(!b)throw Error("Invalid Store Name");return this[b]=new a(b,c),this},remove:function(a){return this[a].command("remove"),delete this[a],this}}}(),h={apiUrl:"https://api.backand.com",socketUrl:"https://socket.backand.com",anonymousToken:null,signUpToken:null,isManagingHttpInterceptor:!0,isManagingRefreshToken:!0,runSigninAfterSignup:!0,callSignupOnSingInSocialError:!0,appName:null,userProfileName:"backand_user",isMobile:!1,runSocket:!1},i={SIGNIN:"BackandSignIn",SIGNOUT:"BackandSignOut",SIGNUP:"BackandSignUp"},j={github:{name:"github",label:"Github",url:"www.github.com",css:"github",id:1},google:{name:"google",label:"Google",url:"www.google.com",css:"google-plus",id:2},facebook:{name:"facebook",label:"Facebook",url:"www.facebook.com",css:"facebook",id:3},twitter:{name:"twitter",label:"Twitter",url:"www.twitter.com",css:"twitter",id:4}};g.register("token"),g.register("user"),function(){var a=/\?(data|error)=(.+)/,b=a.exec(location.href);if(b&&b[1]&&b[2]){var c={};c[b[1]]=JSON.parse(decodeURI(b[2].replace(/#.*/,""))),window.opener.postMessage(JSON.stringify(c),location.origin)}}(),angular.module("backand",[]).provider("Backand",function(){function a(a,b,c){var d=this;d.EVENTS=i,d.setAppName=function(a){h.appName=a},d.signin=function(b,c){return a.signin(b,c)},d.signout=function(){return a.signout()},d.signup=function(b,c,d,e,f,g){return a.signup(b,c,d,e,f,g)},d.getSocialProviders=function(){return j},d.socialSignin=function(b,c,d){return a.socialSignin(b,c,d)},d.socialSignup=function(b,c,d,e){return a.socialSignup(b,c,d,e)},d.socialSignInToken=function(b,c){return a.socialSigninWithToken(b,c)},d.socialSignUpToken=function(b,c){return a.socialSigninWithToken(b,c,!0)},d.socialSignInCode=function(b,c){var d=window.location.origin;if(!b||!c)throw new Error("provide and code have to be valid values");return a.socialSigninWithCode(b,d,c)},d.socialSignUpCode=function(b,c,d,e,f){if(!b||!c)throw new Error("provide and code have to be valid values");var g=window.location.origin;return a.socialSignupWithCode(b,g,c,d,e,f)},d.requestResetPassword=function(b){return a.requestResetPassword(b)},d.resetPassword=function(b,c){return a.resetPassword(b,c)},d.changePassword=function(b,c){return a.changePassword(b,c)},d.setIsMobile=function(a){h.isMobile=a},d.setRunSignupAfterErrorInSigninSocial=function(a){h.callSignupOnSingInSocialError=a},d.getUserDetails=function(a){return b.getUserDetails(a)},d.getUsername=function(){return b.getUsername()},d.getUserRole=function(){return b.getUserRole()},d.getToken=function(){return g.token.get()},d.getTokenName=function(){return null},d.getApiUrl=function(){return h.apiUrl},d.isManagingDefaultHeaders=function(){return null},d.isManagingHttpInterceptor=function(){return h.isManagingHttpInterceptor},d.isManagingRefreshToken=function(){return h.isManagingRefreshToken&&g.user.get()&&g.user.get().refresh_token},d.isRunSocket=function(){return h.runSocket},d.socketLogin=function(){h.runSocket&&c.login(g.token.get(),h.anonymousToken,h.appName,h.socketUrl)},d.on=function(a,b){c.on(a,b)},d.socialSignIn=d.socialSignin,d.socialSignUp=d.socialSignup}this.getApiUrl=function(){return h.apiUrl},this.setApiUrl=function(a){return h.apiUrl=a,this},this.setSocketUrl=function(a){return h.socketUrl=a,this},this.getTokenName=function(){return null},this.setTokenName=function(){return this},this.setAnonymousToken=function(a){return h.anonymousToken=a,this},this.setSignUpToken=function(a){return h.signUpToken=a,this},this.setAppName=function(a){return h.appName=a,this},this.manageDefaultHeaders=function(a){return this},this.manageHttpInterceptor=function(a){return h.isManagingHttpInterceptor=void 0==a?!0:a,this},this.manageRefreshToken=function(a){return h.isManagingRefreshToken=void 0==a?!0:a,this},this.runSigninAfterSignup=function(a){return h.runSigninAfterSignup=void 0==a?!0:a,this},this.runSocket=function(a){return h.runSocket=void 0==a?!1:a,this},this.$get=["BackandAuthService","BackandUserService","BackandSocketService",function(b,c,d){return new a(b,c,d)}]}).run(["$injector",function(a){a.invoke(["$http",function(a){f=a}]),a.invoke(["Backand",function(a){a.socketLogin()}])}]),angular.module("backand").factory("BackandHttpInterceptor",["$q","Backand","BackandHttpBufferService","BackandAuthService",b]).config(["$httpProvider",function(a){a.interceptors.push("BackandHttpInterceptor")}]),angular.module("backand").service("BackandAuthService",["$q","$rootScope","BackandHttpBufferService","BackandSocketService",c]),function(){function a(){function a(a,b){function c(a){b.resolve(a)}function d(a){b.reject(a)}f(a).then(c,d)}function b(a){return delete a.headers.Authorization,a}var c=this,d=[];c.append=function(a,b){d.push({config:a,deferred:b})},c.rejectAll=function(a){if(a)for(var b=0;b -1) {
264 | // handle case of strange chars in the end of url on login error
265 | var urlParsed = new URL(url);
266 |
267 | var dataStr = decodeURI(urlParsed.search).split('error=')[1];
268 | dataStr = dataStr.replace(/%3A/g, ':').replace(/%2C/g, ',');
269 | var userData = JSON.parse(dataStr);
270 | userData.message = userData.message ? userData.message.replace(/\+/g, ' ') : '';
271 | if (!isSignUp && config.callSignupOnSingInSocialError && userData.message.indexOf(NOT_SIGNEDIN_ERROR) > -1) { // check is right error
272 | socialAuth(provider, true, spec);
273 | return;
274 | }
275 |
276 | var rejection = {
277 | data: userData.message + ' (signing in with ' + userData.provider + ')'
278 | };
279 |
280 | rejection.error_description = rejection.data;
281 | self.loginPromise.reject(rejection);
282 | return;
283 | }
284 |
285 | // login is OK
286 | var dataStr = decodeURI(url).split('/#/?data=')[1];
287 | dataStr = dataStr.replace(/%3A/g, ':').replace(/%2C/g, ',');
288 | var userData = JSON.parse(dataStr);
289 | userData.message = userData.message ? userData.message.replace(/\+/g, ' ') : '';
290 | if (self.inSocialSignup) {
291 | self.inSocialSignup = false;
292 | $rootScope.$broadcast(EVENTS.SIGNUP);
293 | }
294 | signinWithToken(userData);
295 | }
296 | }
297 | );
298 | }
299 |
300 | function socialAuth(provider, isSignUp, spec, email, isAutoSignUp) {
301 |
302 | if (!socialProviders[provider]) {
303 | throw Error('Unknown Social Provider');
304 | }
305 |
306 | if (!self.loginPromise)
307 | self.loginPromise = $q.defer();
308 | else
309 | self.signUpPromise = $q.defer();
310 |
311 | if (config.isMobile) {
312 |
313 | var ref = window.open(
314 | config.apiUrl + '/1/'
315 | + getSocialUrl(provider, isSignUp, isAutoSignUp)
316 | + '&appname=' + config.appName + (email ? ("&email=" + email) : '')
317 | + '&returnAddress=' + dummyReturnAddress,
318 | 'id1',
319 | spec || 'left=1, top=1, width=600, height=600');
320 |
321 | mobileSocialLoginInner(ref, isSignUp, provider, spec);
322 | }
323 | else {
324 | self.socialAuthWindow = window.open(
325 | config.apiUrl + '/1/'
326 | + getSocialUrl(provider, isSignUp, isAutoSignUp)
327 | + '&appname=' + config.appName + (email ? ("&email=" + email) : '')
328 | + '&returnAddress=',
329 | 'id1',
330 | spec || 'left=1, top=1, width=600, height=600');
331 |
332 | window.addEventListener('message', (function (provider, spec) {
333 | return function (e) {
334 | window.removeEventListener('message', arguments.callee);
335 | setUserDataFromToken(e, provider, spec)
336 | }
337 | })(provider, spec), false);
338 | }
339 | return self.loginPromise.promise;
340 | }
341 |
342 | function setUserDataFromToken(event, provider, spec) {
343 | console.log(event, provider, spec);
344 | self.socialAuthWindow.close();
345 | self.socialAuthWindow = null;
346 |
347 | if (event.origin !== location.origin) {
348 | return;
349 | }
350 |
351 | var userData = JSON.parse(event.data);
352 | if (userData.error) {
353 |
354 | if (config.callSignupOnSingInSocialError && userData.error.message.indexOf(NOT_SIGNEDIN_ERROR) > -1) { // check is right error
355 | socialAuth(provider, true, spec);
356 | return;
357 | }
358 |
359 | var rejection = {
360 | data: userData.error.message + ' (signing in with ' + userData.error.provider + ')'
361 | };
362 | rejection.error_description = rejection.data;
363 | self.loginPromise.reject(rejection);
364 |
365 | }
366 | else if (userData.data) {
367 | if (self.inSocialSignup) {
368 | self.inSocialSignup = false;
369 | $rootScope.$broadcast(EVENTS.SIGNUP);
370 | }
371 | return signinWithToken(userData.data);
372 |
373 | }
374 | else {
375 | self.loginPromise.reject();
376 | }
377 | }
378 |
379 | // tokens authentication
380 | function signinWithToken(userData) {
381 | var tokenData = {
382 | grant_type: 'password',
383 | accessToken: userData.access_token,
384 | appName: config.appName
385 | };
386 |
387 | if (self.signupParameters) {
388 | tokenData.parameters = self.signupParameters;
389 | self.signupParameters = null;
390 | }
391 |
392 | return authenticate(tokenData)
393 | }
394 |
395 | self.refreshToken = function (username) {
396 | BKStorage.token.clear();
397 |
398 | var user = BKStorage.user.get();
399 | var refreshToken;
400 | if (!user || !(refreshToken = BKStorage.user.get().refresh_token)) {
401 | return;
402 | }
403 |
404 | var tokenData = {
405 | grant_type: 'password',
406 | refreshToken: refreshToken,
407 | username: username,
408 | appName: config.appName
409 | };
410 | return authenticate(tokenData);
411 | };
412 |
413 |
414 | function authenticate(authData) {
415 | if (authenticating) {
416 | return;
417 | }
418 | authenticating = true;
419 | BKStorage.token.clear();
420 | return http({
421 | method: 'POST',
422 | url: config.apiUrl + urls.token,
423 | headers: {
424 | 'Content-Type': 'application/x-www-form-urlencoded'
425 | },
426 | transformRequest: function (obj) {
427 | var str = [];
428 | angular.forEach(obj, function (value, key) {
429 | str.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
430 | });
431 | return str.join("&");
432 | },
433 | data: authData
434 |
435 | }).then(function (response) {
436 | if (response.data && response.data.access_token) {
437 | config.token = 'bearer ' + response.data.access_token;
438 |
439 | BKStorage.token.set(config.token);
440 | BKStorage.user.set(response.data);
441 |
442 | if (self.loginPromise) {
443 | self.loginPromise.resolve(config.token);
444 | }
445 | if (self.signUpPromise){
446 | self.signUpPromise.resolve(config.token);
447 | }
448 |
449 | BackandHttpBufferService.retryAll();
450 | $rootScope.$broadcast(EVENTS.SIGNIN);
451 |
452 | if (config.runSocket)
453 | BackandSocketService.login(BKStorage.token.get(), config.anonymousToken, config.appName, config.socketUrl);
454 |
455 |
456 | } else if (self.loginPromise) {
457 | self.loginPromise.reject('token is undefined');
458 | }
459 | return response.data;
460 |
461 | }).catch(function (err) {
462 | if (self.loginPromise) {
463 | self.loginPromise.reject(err);
464 | }
465 | return $q.reject(err.data);
466 |
467 | }).finally(function () {
468 | authenticating = false;
469 | });
470 | }
471 |
472 |
473 | // password management
474 |
475 | self.requestResetPassword = function (email) {
476 | return http({
477 | method: 'POST',
478 | url: config.apiUrl + urls.requestResetPassword,
479 | data: {
480 | appName: config.appName,
481 | username: email
482 | }
483 | })
484 | };
485 |
486 | self.resetPassword = function (newPassword, resetToken) {
487 | return http({
488 | method: 'POST',
489 | url: config.apiUrl + urls.resetPassword,
490 | data: {
491 | newPassword: newPassword,
492 | resetToken: resetToken
493 | }
494 | });
495 | };
496 |
497 | self.changePassword = function (oldPassword, newPassword) {
498 | return http({
499 | method: 'POST',
500 | url: config.apiUrl + urls.changePassword,
501 | data: {
502 | oldPassword: oldPassword,
503 | newPassword: newPassword
504 | }
505 | });
506 | };
507 |
508 |
509 | }
510 |
--------------------------------------------------------------------------------
/src/services/http_buffer.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | angular.module('backand').service('BackandHttpBufferService', HttpBufferService);
3 |
4 | function HttpBufferService() {
5 | var self = this;
6 | var buffer = [];
7 |
8 | function retryHttpRequest(config, deferred) {
9 | function successCallback(response) {
10 | deferred.resolve(response);
11 | }
12 | function errorCallback(response) {
13 | deferred.reject(response);
14 | }
15 |
16 | http(config).then(successCallback, errorCallback);
17 | }
18 |
19 | self.append = function (config, deferred) {
20 | buffer.push({
21 | config: config,
22 | deferred: deferred
23 | });
24 | };
25 |
26 | self.rejectAll = function (reason) {
27 | if (reason) {
28 | for (var i = 0; i < buffer.length; ++i) {
29 | buffer[i].deferred.reject(reason);
30 | }
31 | }
32 | buffer = [];
33 | };
34 |
35 | function updater (config) {
36 | delete config.headers.Authorization;
37 | return config;
38 | }
39 |
40 | self.retryAll = function () {
41 | for (var i = 0; i < buffer.length; ++i) {
42 | retryHttpRequest(updater(buffer[i].config), buffer[i].deferred);
43 | }
44 | buffer = [];
45 | }
46 |
47 | }
48 |
49 | })();
50 |
--------------------------------------------------------------------------------
/src/services/socket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Itay on 11/17/15.
3 | */
4 | angular.module('backand')
5 | .service('BackandSocketService', ['$rootScope', BackandSocketService]);
6 |
7 | function BackandSocketService ($rootScope) {
8 |
9 | var self = this;
10 |
11 | self.socket = {on: function(){}}; //io.connect('http://localhost:4000');
12 |
13 | self.login = function(token, anonymousToken, appName, url){
14 | self.socket = io.connect(url, {'forceNew':true });
15 |
16 | self.socket.on('connect', function(){
17 | console.log('connected');
18 | self.socket.emit("login", token, anonymousToken, appName);
19 | });
20 |
21 | self.socket.on('disconnect', function() {
22 | console.log('disconnect');
23 | });
24 |
25 | self.socket.on('reconnecting', function() {
26 | console.log('reconnecting');
27 | });
28 |
29 | };
30 |
31 | self.on = function (eventName, callback) {
32 | self.socket.on(eventName, function () {
33 | var args = arguments;
34 | $rootScope.$apply(function () {
35 | callback.apply(self.socket, args);
36 | });
37 | });
38 | };
39 |
40 | self.emit = function (eventName, data, callback) {
41 | self.socket.emit(eventName, data, function () {
42 | var args = arguments;
43 | $rootScope.$apply(function () {
44 | if (callback) {
45 | callback.apply(self.socket, args);
46 | }
47 | });
48 | })
49 | }
50 | }
--------------------------------------------------------------------------------
/src/services/user.js:
--------------------------------------------------------------------------------
1 | angular.module('backand').service('BackandUserService', ['$q', BackandUserService]);
2 |
3 | function BackandUserService ($q) {
4 | var self = this;
5 |
6 | self.getUserDetails = function (force) {
7 | var deferred = $q.defer();
8 | if (force) {
9 | http({
10 | method: 'GET',
11 | url: config.apiUrl + '/api/account/profile'
12 | })
13 | .success(function (profile) {
14 | BKStorage.user.set(angular.extend(BKStorage.user.get(), profile));
15 | deferred.resolve(BKStorage.user.get());
16 | })
17 | } else {
18 | deferred.resolve(BKStorage.user.get());
19 | }
20 | return deferred.promise;
21 | };
22 |
23 | self.getUsername = function () {
24 | var userDetails;
25 | return (userDetails = BKStorage.user.get()) ? userDetails.username : null;
26 | };
27 |
28 | self.getUserRole = function () {
29 | var userDetails;
30 | return (userDetails = BKStorage.user.get()) ? userDetails.role : null;
31 | };
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/utils/storage.js:
--------------------------------------------------------------------------------
1 | var BKStorage = (function () {
2 | 'use strict';
3 |
4 | var prefix = 'BACKAND';
5 |
6 | function Store (storeName, type) {
7 |
8 | var storageAPI;
9 |
10 | if( ['local', 'session'].indexOf(type) === -1 ) type = 'local';
11 |
12 | if (typeof window !== 'undefined' && typeof window[type + 'Storage'] !== 'undefined') {
13 | storageAPI = window[type + 'Storage'];
14 | } else {
15 | // We can fallback to other solution here inMemory Management
16 | // It could be cookies if needed
17 | storageAPI = {
18 | value: null,
19 | getItem: function (name, params) {
20 | return this.value;
21 | },
22 | setItem: function (name, params) {
23 | this.value = params;
24 | },
25 | removeItem: function (name, params) {
26 | this.value = null;
27 | }
28 | };
29 | }
30 |
31 | this.command = function (action, params) {
32 | return storageAPI[action + 'Item'](prefix + storeName, params || null);
33 | };
34 | }
35 |
36 | Store.prototype.get = function () {
37 | return JSON.parse(this.command('get'));
38 | };
39 |
40 | Store.prototype.set = function (value) {
41 | return this.command('set', JSON.stringify(value));
42 | };
43 |
44 | Store.prototype.clear = function () {
45 | this.command('set');
46 | return this;
47 | };
48 |
49 | return {
50 | register: function (storeName, type) {
51 | if(!storeName) {
52 | throw Error('Invalid Store Name');
53 | }
54 | this[storeName] = new Store(storeName, type);
55 | return this;
56 | },
57 |
58 | remove: function (storeName) {
59 | this[storeName].command('remove');
60 | delete this[storeName];
61 | return this;
62 | }
63 | };
64 |
65 | })();
66 |
--------------------------------------------------------------------------------