├── .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) { // 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 | --------------------------------------------------------------------------------