├── .DS_Store ├── .babelrc ├── .coveralls.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .tern-project ├── .travis.yml ├── README.md ├── bower.json ├── build └── build.js ├── dist ├── vue-social-auth.common.js ├── vue-social-auth.es2017.js ├── vue-social-auth.js └── vue-social-auth.min.js ├── gulpfile.js ├── nuxt ├── index.js └── plugin.template.js ├── package.json └── src ├── authenticate.js ├── index.js ├── oauth ├── oauth1.js ├── oauth2.js └── popup.js ├── options.js ├── promise.js ├── storage.js ├── storage ├── cookie-storage.js ├── local-storage.js ├── memory-storage.js └── session-storage.js └── utils.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diadal/vue-social-auth/2eadbcf6a038f36e9f22e7393e621965cd3a0da5/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "es2017", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ], 10 | "plugins": ["external-helpers"] 11 | } -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: G9JVOf8Hp3YbCmoFPU2ILSW03p2FbRMKn -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | flow 2 | dist 3 | packages 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "sourceType": "module" 6 | }, 7 | "env": { 8 | "browser": true 9 | }, 10 | "extends": "standard", 11 | "plugins": [ 12 | "html" 13 | ], 14 | "rules": { 15 | "arrow-parens": 0, 16 | "generator-star-spacing": 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Dependency directory 3 | node_modules 4 | bower_components 5 | 6 | Thumbs.db 7 | .DS_Store 8 | 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | *.suo 16 | *.ntvs* 17 | *.njsproj 18 | *.sln 19 | *.iml 20 | .vscode 21 | settings.json 22 | jsconfig.json 23 | -------------------------------------------------------------------------------- /.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "es_modules": {}, 4 | "node": {} 5 | }, 6 | "libs": [ 7 | "ecma5", 8 | "ecma6", 9 | "react", 10 | "browser" 11 | ], 12 | "ecmaVersion": 6 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "13" 5 | - "12" 6 | - "11" 7 | - "10" 8 | - "8" 9 | - "6" 10 | script: node src/index.js 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Known Vulnerabilities](https://snyk.io/test/github/diadal/vue-social-auth/badge.svg)](https://snyk.io/test/github/diadal/vue-social-auth) 2 | 3 | **NOTE:** The new Version works better 4 | 5 | ## ♻️ [Update Version Universal-Social-Auth](https://github.com/diadal/universal-social-auth) 6 | 7 | # Laravel vue-social-auth 8 | 9 | **vue-social-auth** is easily configurable solution for [Vue.js](https://vuejs.org/) & [Laravel](https://laravel.com/) with Socialite that provides Social login using Github, Facebook, Google, Vkontakte and other OAuth providers. 10 | 11 | 12 | The best part about this library is that it is not strictly coupled to one request handling library like [vue-axios](https://github.com/imcvampire/vue-axios). You will be able to use it with different libraries. 13 | 14 | For now it is tested to work with [vue-resource](https://github.com/pagekit/vue-resource) and [axios](https://github.com/mzabriskie/axios) (using [vue-axios](https://github.com/imcvampire/vue-axios) wrapper). 15 | 16 | **WARNING:** Default request library is `axios` using `vue-axios` wrapper plugin. 17 | 18 | **NOTE:** It also work with any Php with `Socialite` 19 | 20 | ## Supported OAuth providers and configurations 21 | 22 | ## Installation 23 | 24 | ## firstly install `Socialite` 25 | 26 | **NOTE:** make sure you config your `Socialite` configuration data in `services.php` & `.env` for more details 27 | check [https://socialiteproviders.netlify.com/](https://socialiteproviders.netlify.com/) 28 | 29 | ```bash 30 | composer require laravel/socialite 31 | 32 | ``` 33 | ## Next install vue-social-auth 34 | 35 | ```bash 36 | npm install vue-social-auth 37 | ``` 38 | 39 | ## Usage 40 | ```javascript 41 | import Vue from 'vue' 42 | import axios from 'axios'; 43 | import VueAxios from 'vue-axios' 44 | import VueSocialauth from 'vue-social-auth' 45 | 46 | Vue.use(VueAxios, axios) 47 | Vue.use(VueSocialauth, { 48 | 49 | providers: { 50 | github: { 51 | clientId: '', 52 | redirectUri: '/auth/github/callback' // Your client app URL 53 | } 54 | } 55 | }) 56 | ``` 57 | 58 | ```html 59 | 60 | 61 | 62 | 63 | 64 | ``` 65 | 66 | ## ♻️ Usage with Nuxt.js 67 | 68 | Add `vue-social-auth/nuxt` to modules section of `nuxt.config.js` 69 | 70 | Module automatically add to $auth or property selected 71 | 72 | ```js 73 | { 74 | modules: [ 75 | // Optionally passing options in module configuration 76 | ['vue-social-auth/nuxt', { 77 | property: '$auth', // Optional property if the $auth property is being used by another module 78 | providers: { 79 | github: { 80 | clientId: '', 81 | redirectUri: '/auth/github/callback' // Your client app URL 82 | } 83 | } 84 | }] 85 | ], 86 | 87 | // Optionally passing options in module top level configuration 88 | vueSocialAuth: { 89 | property: '$auth' 90 | providers: { 91 | // ... 92 | } 93 | } 94 | } 95 | ``` 96 | 97 | 98 | ### View Component 99 | ```javascript 100 | 141 | ``` 142 | 143 | #### Vue Router 144 | 145 | ```javascript 146 | 147 | { 148 | path: '/auth/:provider/callback', 149 | component: { 150 | template: '
' 151 | } 152 | }, 153 | 154 | ``` 155 | 156 | ### Vue is Done let move to backend config `Laravel` with `Socialite` 157 | 158 | #### Laravel Router 159 | 160 | ```php 161 | 162 | Route::post('sociallogin/{provider}', 'Auth\AuthController@SocialSignup'); 163 | Route::get('auth/{provider}/callback', 'OutController@index')->where('provider', '.*'); 164 | 165 | 166 | ``` 167 | 168 | #### OutController 169 | 170 | ```php 171 | 172 | stateless()->user(); 226 | 227 | return response()->json($user); 228 | } 229 | 230 | } 231 | 232 | 233 | ``` 234 | 235 | 236 | #### services.php 237 | 238 | ```php 239 | 240 | [ 247 | 'client_id' => env('TWITTER_ID'), 248 | 'client_secret' => env('TWITTER_SECRET'), 249 | 'redirect' => env('TWITTER_URL'), 250 | ], 251 | 252 | 'facebook' => [ 253 | 'client_id' => env('FACEBOOK_ID'), 254 | 'client_secret' => env('FACEBOOK_SECRET'), 255 | 'redirect' => env('FACEBOOK_URL'), 256 | ], 257 | 258 | 'github' => [ 259 | 'client_id' => env('GITHUB_ID'), 260 | 'client_secret' => env('GITHUB_SECRET'), 261 | 'redirect' => env('GITHUB_URL'), 262 | ], 263 | 264 | 'google' => [ 265 | 'client_id' => env('GOOGLE_ID'), 266 | 'client_secret' => env('GOOGLE_SECRET'), 267 | 'redirect' => env('GOOGLE_URL'), 268 | ], 269 | 270 | 'vkontakte' => [ 271 | 'client_id' => env('VKONTAKTE_KEY'), 272 | 'client_secret' => env('VKONTAKTE_SECRET'), 273 | 'redirect' => env('VKONTAKTE_REDIRECT_URI'), 274 | ], 275 | ]; 276 | 277 | 278 | ``` 279 | 280 | #### .env 281 | 282 | ```text 283 | 284 | TWITTER_ID=Your ID 285 | TWITTER_SECRET=Your Secret 286 | TWITTER_URL=https://example.com/auth/twitter/callback 287 | 288 | FACEBOOK_ID=Your ID 289 | FACEBOOK_SECRET=Your Secret 290 | FACEBOOK_URL=https://example.com/auth/facebook/callback 291 | 292 | GITHUB_ID=Your ID 293 | GITHUB_SECRET=Your Secret 294 | GITHUB_URL=https://example.com/auth/github/callback 295 | 296 | GOOGLE_ID=Your ID 297 | GOOGLE_SECRET=Your Secret 298 | GOOGLE_URL=https://example.com/auth/google/callback 299 | 300 | VKONTAKTE_KEY=Your ID 301 | VKONTAKTE_SECRET=Your Secret 302 | VKONTAKTE_REDIRECT_URI=https://example.com/auth/vkontakte/callback 303 | 304 | ``` 305 | 306 | #### VerifyCsrfToken Middleware 307 | 308 | you may need to disable Csrf for the route if you receive `Error: Request failed with status code 419` 309 | 310 | ```php 311 | 312 | 86 | * @copyright Method taken from https://github.com/sahat/satellizer 87 | * 88 | * @param {String} baseUrl Base url 89 | * @param {String} url URI 90 | * @return {String} 91 | */ 92 | function joinUrl(baseUrl, url) { 93 | if (/^(?:[a-z]+:)?\/\//i.test(url)) { 94 | return url; 95 | } 96 | var joined = [baseUrl, url].join('/'); 97 | var normalize = function (str) { 98 | return str 99 | .replace(/[\/]+/g, '/') 100 | .replace(/\/\?/g, '?') 101 | .replace(/\/\#/g, '#') 102 | .replace(/\:\//g, '://'); 103 | }; 104 | return normalize(joined); 105 | } 106 | 107 | /** 108 | * Get full path based on current location 109 | * 110 | * @author Sahat Yalkabov 111 | * @copyright Method taken from https://github.com/sahat/satellizer 112 | * 113 | * @param {Location} location 114 | * @return {String} 115 | */ 116 | function getFullUrlPath(location) { 117 | var isHttps = location.protocol === 'https:'; 118 | return location.protocol + '//' + location.hostname + 119 | ':' + (location.port || (isHttps ? '443' : '80')) + 120 | (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); 121 | } 122 | 123 | /** 124 | * Parse query string variables 125 | * 126 | * @author Sahat Yalkabov 127 | * @copyright Method taken from https://github.com/sahat/satellizer 128 | * 129 | * @param {String} Query string 130 | * @return {String} 131 | */ 132 | function parseQueryString(str) { 133 | var obj = {}; 134 | var key; 135 | var value; 136 | (str || '').split('&').forEach(function (keyValue) { 137 | if (keyValue) { 138 | value = keyValue.split('='); 139 | key = decodeURIComponent(value[0]); 140 | obj[key] = (!!value[1]) ? decodeURIComponent(value[1]) : true; 141 | } 142 | }); 143 | return obj; 144 | } 145 | 146 | /** 147 | * Decode base64 string 148 | * @author Sahat Yalkabov 149 | * @copyright Method taken from https://github.com/sahat/satellizer 150 | * 151 | * @param {String} str base64 encoded string 152 | * @return {Object} 153 | */ 154 | 155 | 156 | function parseCookies(str) { 157 | if (str.length === 0) { return {}; } 158 | var parsed = {}; 159 | var pattern = new RegExp('\\s*;\\s*'); 160 | str.split(pattern).forEach(function (i) { 161 | var ref = i.split('='); 162 | var encodedKey = ref[0]; 163 | var encodedValue = ref[1]; 164 | var key = decodeURIComponent(encodedKey); 165 | var value = decodeURIComponent(encodedValue); 166 | parsed[key] = value; 167 | }); 168 | return parsed; 169 | } 170 | 171 | function formatOptions(options) { 172 | var path = options.path; 173 | var domain = options.domain; 174 | var expires = options.expires; 175 | var secure = options.secure; 176 | return [ 177 | typeof path === 'undefined' || path === null 178 | ? '' : ';path=' + path, 179 | typeof domain === 'undefined' || domain === null 180 | ? '' : ';domain=' + domain, 181 | typeof expires === 'undefined' || expires === null 182 | ? '' : ';expires=' + expires.toUTCString(), 183 | typeof secure === 'undefined' || secure === null || secure === false 184 | ? '' : ';secure' 185 | ].join(''); 186 | } 187 | 188 | function formatCookie(key, value, options) { 189 | return [ 190 | encodeURIComponent(key), 191 | '=', 192 | encodeURIComponent(value), 193 | formatOptions(options) 194 | ].join(''); 195 | } 196 | 197 | // Store setTimeout reference so promise-polyfill will be unaffected by 198 | // other code modifying setTimeout (like sinon.useFakeTimers()) 199 | var setTimeoutFunc = setTimeout; 200 | 201 | function noop() {} 202 | 203 | // Polyfill for Function.prototype.bind 204 | function bind(fn, thisArg) { 205 | return function () { 206 | fn.apply(thisArg, arguments); 207 | }; 208 | } 209 | 210 | function Promise$1(fn) { 211 | if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } 212 | if (typeof fn !== 'function') { throw new TypeError('not a function'); } 213 | this._state = 0; 214 | this._handled = false; 215 | this._value = undefined; 216 | this._deferreds = []; 217 | 218 | doResolve(fn, this); 219 | } 220 | 221 | function handle(self, deferred) { 222 | while (self._state === 3) { 223 | self = self._value; 224 | } 225 | if (self._state === 0) { 226 | self._deferreds.push(deferred); 227 | return; 228 | } 229 | self._handled = true; 230 | Promise$1._immediateFn(function () { 231 | var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 232 | if (cb === null) { 233 | (self._state === 1 ? resolve : reject)(deferred.promise, self._value); 234 | return; 235 | } 236 | var ret; 237 | try { 238 | ret = cb(self._value); 239 | } catch (e) { 240 | reject(deferred.promise, e); 241 | return; 242 | } 243 | resolve(deferred.promise, ret); 244 | }); 245 | } 246 | 247 | function resolve(self, newValue) { 248 | try { 249 | // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 250 | if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.'); } 251 | if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { 252 | var then = newValue.then; 253 | if (newValue instanceof Promise$1) { 254 | self._state = 3; 255 | self._value = newValue; 256 | finale(self); 257 | return; 258 | } else if (typeof then === 'function') { 259 | doResolve(bind(then, newValue), self); 260 | return; 261 | } 262 | } 263 | self._state = 1; 264 | self._value = newValue; 265 | finale(self); 266 | } catch (e) { 267 | reject(self, e); 268 | } 269 | } 270 | 271 | function reject(self, newValue) { 272 | self._state = 2; 273 | self._value = newValue; 274 | finale(self); 275 | } 276 | 277 | function finale(self) { 278 | if (self._state === 2 && self._deferreds.length === 0) { 279 | Promise$1._immediateFn(function() { 280 | if (!self._handled) { 281 | Promise$1._unhandledRejectionFn(self._value); 282 | } 283 | }); 284 | } 285 | 286 | for (var i = 0, len = self._deferreds.length; i < len; i++) { 287 | handle(self, self._deferreds[i]); 288 | } 289 | self._deferreds = null; 290 | } 291 | 292 | function Handler(onFulfilled, onRejected, promise) { 293 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 294 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 295 | this.promise = promise; 296 | } 297 | 298 | /** 299 | * Take a potentially misbehaving resolver function and make sure 300 | * onFulfilled and onRejected are only called once. 301 | * 302 | * Makes no guarantees about asynchrony. 303 | */ 304 | function doResolve(fn, self) { 305 | var done = false; 306 | try { 307 | fn(function (value) { 308 | if (done) { return; } 309 | done = true; 310 | resolve(self, value); 311 | }, function (reason) { 312 | if (done) { return; } 313 | done = true; 314 | reject(self, reason); 315 | }); 316 | } catch (ex) { 317 | if (done) { return; } 318 | done = true; 319 | reject(self, ex); 320 | } 321 | } 322 | 323 | Promise$1.prototype['catch'] = function (onRejected) { 324 | return this.then(null, onRejected); 325 | }; 326 | 327 | Promise$1.prototype.then = function (onFulfilled, onRejected) { 328 | var prom = new (this.constructor)(noop); 329 | 330 | handle(this, new Handler(onFulfilled, onRejected, prom)); 331 | return prom; 332 | }; 333 | 334 | Promise$1.all = function (arr) { 335 | var args = Array.prototype.slice.call(arr); 336 | 337 | return new Promise$1(function (resolve, reject) { 338 | if (args.length === 0) { return resolve([]); } 339 | var remaining = args.length; 340 | 341 | function res(i, val) { 342 | try { 343 | if (val && (typeof val === 'object' || typeof val === 'function')) { 344 | var then = val.then; 345 | if (typeof then === 'function') { 346 | then.call(val, function (val) { 347 | res(i, val); 348 | }, reject); 349 | return; 350 | } 351 | } 352 | args[i] = val; 353 | if (--remaining === 0) { 354 | resolve(args); 355 | } 356 | } catch (ex) { 357 | reject(ex); 358 | } 359 | } 360 | 361 | for (var i = 0; i < args.length; i++) { 362 | res(i, args[i]); 363 | } 364 | }); 365 | }; 366 | 367 | Promise$1.resolve = function (value) { 368 | if (value && typeof value === 'object' && value.constructor === Promise$1) { 369 | return value; 370 | } 371 | 372 | return new Promise$1(function (resolve) { 373 | resolve(value); 374 | }); 375 | }; 376 | 377 | Promise$1.reject = function (value) { 378 | return new Promise$1(function (resolve, reject) { 379 | reject(value); 380 | }); 381 | }; 382 | 383 | Promise$1.race = function (values) { 384 | return new Promise$1(function (resolve, reject) { 385 | for (var i = 0, len = values.length; i < len; i++) { 386 | values[i].then(resolve, reject); 387 | } 388 | }); 389 | }; 390 | 391 | // Use polyfill for setImmediate for performance gains 392 | Promise$1._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) || 393 | function (fn) { 394 | setTimeoutFunc(fn, 0); 395 | }; 396 | 397 | Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { 398 | if (typeof console !== 'undefined' && console) { 399 | console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console 400 | } 401 | }; 402 | 403 | /** 404 | * Set the immediate function to execute callbacks 405 | * @param fn {function} Function to execute 406 | * @deprecated 407 | */ 408 | Promise$1._setImmediateFn = function _setImmediateFn(fn) { 409 | Promise$1._immediateFn = fn; 410 | }; 411 | 412 | /** 413 | * Change the function to execute on unhandled rejection 414 | * @param {function} fn Function to execute on unhandled rejection 415 | * @deprecated 416 | */ 417 | Promise$1._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) { 418 | Promise$1._unhandledRejectionFn = fn; 419 | }; 420 | 421 | /** 422 | * Default configuration 423 | */ 424 | var defaultOptions = { 425 | baseUrl: null, 426 | tokenName: 'token', 427 | tokenPrefix: 'vueauth', 428 | tokenHeader: 'Authorization', 429 | tokenType: 'Bearer', 430 | loginUrl: '/auth/login', 431 | registerUrl: '/auth/register', 432 | logoutUrl: null, 433 | storageType: 'localStorage', 434 | storageNamespace: 'vue-social-auth', 435 | cookieStorage: { 436 | domain: window.location.hostname, 437 | path: '/', 438 | secure: false 439 | }, 440 | requestDataKey: 'data', 441 | responseDataKey: 'data', 442 | 443 | /** 444 | * Default request interceptor for Axios library 445 | * @context {VueSocialauth} 446 | */ 447 | bindRequestInterceptor: function ($auth) { 448 | 449 | var tokenHeader = $auth.options.tokenHeader; 450 | 451 | $auth.$http.interceptors.request.use(function (config) { 452 | delete config.headers[tokenHeader]; 453 | return config 454 | }); 455 | 456 | 457 | }, 458 | 459 | /** 460 | * Default response interceptor for Axios library 461 | * @contect {VueSocialauth} 462 | */ 463 | bindResponseInterceptor: function ($auth) { 464 | $auth.$http.interceptors.response.use(function (response) { 465 | return response 466 | }); 467 | }, 468 | 469 | providers: { 470 | facebook: { 471 | name: 'facebook', 472 | url: '/auth/facebook', 473 | authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', 474 | redirectUri: window.location.origin + '/', 475 | requiredUrlParams: ['display', 'scope'], 476 | scope: ['email'], 477 | scopeDelimiter: ',', 478 | display: 'popup', 479 | oauthType: '2.0', 480 | popupOptions: { width: 580, height: 400 } 481 | }, 482 | 483 | google: { 484 | name: 'google', 485 | url: '/auth/google', 486 | authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', 487 | redirectUri: window.location.origin, 488 | requiredUrlParams: ['scope'], 489 | optionalUrlParams: ['display'], 490 | scope: ['profile', 'email'], 491 | scopePrefix: 'openid', 492 | scopeDelimiter: ' ', 493 | display: 'popup', 494 | oauthType: '2.0', 495 | popupOptions: { width: 452, height: 633 } 496 | }, 497 | 498 | github: { 499 | name: 'github', 500 | url: '/auth/github', 501 | authorizationEndpoint: 'https://github.com/login/oauth/authorize', 502 | redirectUri: window.location.origin, 503 | optionalUrlParams: ['scope'], 504 | scope: ['user:email'], 505 | scopeDelimiter: ' ', 506 | oauthType: '2.0', 507 | popupOptions: { width: 1020, height: 618 } 508 | }, 509 | 510 | instagram: { 511 | name: 'instagram', 512 | url: '/auth/instagram', 513 | authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', 514 | redirectUri: window.location.origin, 515 | requiredUrlParams: ['scope'], 516 | scope: ['basic'], 517 | scopeDelimiter: '+', 518 | oauthType: '2.0', 519 | popupOptions: { width: null, height: null } 520 | }, 521 | 522 | twitter: { 523 | name: 'twitter', 524 | url: '/auth/twitter', 525 | authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', 526 | redirectUri: window.location.origin, 527 | oauthType: '1.0', 528 | popupOptions: { width: 495, height: 645 } 529 | }, 530 | 531 | bitbucket: { 532 | name: 'bitbucket', 533 | url: '/auth/bitbucket', 534 | authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', 535 | redirectUri: window.location.origin + '/', 536 | optionalUrlParams: ['scope'], 537 | scope: ['email'], 538 | scopeDelimiter: ' ', 539 | oauthType: '2.0', 540 | popupOptions: { width: 1020, height: 618 } 541 | }, 542 | 543 | linkedin: { 544 | name: 'linkedin', 545 | url: '/auth/linkedin', 546 | authorizationEndpoint: 'https://www.linkedin.com/oauth/v2/authorization', 547 | redirectUri: window.location.origin, 548 | requiredUrlParams: ['state','scope'], 549 | scope: ['r_liteprofile','r_emailaddress'], 550 | scopeDelimiter: ' ', 551 | state: 'STATE', 552 | oauthType: '2.0', 553 | popupOptions: { width: 527, height: 582 } 554 | }, 555 | 556 | vkontakte: { 557 | name: 'vkontakte', 558 | url: '/auth/vkontakte', 559 | authorizationEndpoint: 'https://oauth.vk.com/authorize', 560 | redirectUri: window.location.origin + '/', 561 | requiredUrlParams: ['scope'], 562 | scope: ['email'], 563 | scopeDelimiter: ',', 564 | display: 'popup', 565 | oauthType: '2.0', 566 | popupOptions: { width: 580, height: 400 } 567 | }, 568 | 569 | live: { 570 | name: 'live', 571 | url: '/auth/live', 572 | authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', 573 | redirectUri: window.location.origin, 574 | requiredUrlParams: ['display', 'scope'], 575 | scope: ['wl.emails'], 576 | scopeDelimiter: ' ', 577 | display: 'popup', 578 | oauthType: '2.0', 579 | popupOptions: { width: 500, height: 560 } 580 | }, 581 | 582 | oauth1: { 583 | name: null, 584 | url: '/auth/oauth1', 585 | authorizationEndpoint: null, 586 | redirectUri: window.location.origin, 587 | oauthType: '1.0', 588 | popupOptions: null 589 | }, 590 | 591 | oauth2: { 592 | name: null, 593 | url: '/auth/oauth2', 594 | clientId: null, 595 | redirectUri: window.location.origin, 596 | authorizationEndpoint: null, 597 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 598 | requiredUrlParams: null, 599 | optionalUrlParams: null, 600 | scope: null, 601 | scopePrefix: null, 602 | scopeDelimiter: null, 603 | state: null, 604 | oauthType: '2.0', 605 | popupOptions: null, 606 | responseType: 'code', 607 | responseParams: { 608 | code: 'code', 609 | clientId: 'clientId', 610 | redirectUri: 'redirectUri' 611 | } 612 | } 613 | } 614 | }; 615 | 616 | var CookieStorage = function CookieStorage(defaultOptions) { 617 | this._defaultOptions = objectExtend({ 618 | domain: window.location.hostname, 619 | expires: null, 620 | path: '/', 621 | secure: false 622 | }, defaultOptions); 623 | }; 624 | 625 | CookieStorage.prototype.setItem = function setItem (key, value) { 626 | var options = objectExtend({}, this._defaultOptions); 627 | var cookie = formatCookie(key, value, options); 628 | this._setCookie(cookie); 629 | }; 630 | 631 | CookieStorage.prototype.getItem = function getItem (key) { 632 | var cookies = parseCookies(this._getCookie()); 633 | return cookies.hasOwnProperty(key) ? cookies[key] : null; 634 | }; 635 | 636 | CookieStorage.prototype.removeItem = function removeItem (key) { 637 | var value = ''; 638 | var defaultOptions = objectExtend({}, this._defaultOptions); 639 | var options = objectExtend(defaultOptions, { 640 | expires: new Date(0) 641 | }); 642 | var cookie = formatCookie(key, value, options); 643 | this._setCookie(cookie); 644 | }; 645 | 646 | CookieStorage.prototype._getCookie = function _getCookie () { 647 | return typeof document === 'undefined' 648 | ? '' : typeof document.cookie === 'undefined' 649 | ? '' : document.cookie; 650 | }; 651 | 652 | CookieStorage.prototype._setCookie = function _setCookie (cookie) { 653 | document.cookie = cookie; 654 | }; 655 | 656 | var LocalStorage = function LocalStorage(namespace) { 657 | this.namespace = namespace || null; 658 | }; 659 | 660 | LocalStorage.prototype.setItem = function setItem (key, value) { 661 | window.localStorage.setItem(this._getStorageKey(key), value); 662 | }; 663 | 664 | LocalStorage.prototype.getItem = function getItem (key) { 665 | return window.localStorage.getItem(this._getStorageKey(key)) 666 | }; 667 | 668 | LocalStorage.prototype.removeItem = function removeItem (key) { 669 | window.localStorage.removeItem(this._getStorageKey(key)); 670 | }; 671 | 672 | LocalStorage.prototype._getStorageKey = function _getStorageKey (key) { 673 | if (this.namespace) { 674 | return [this.namespace, key].join('.') 675 | } 676 | return key; 677 | }; 678 | 679 | var MemoryStorage = function MemoryStorage(namespace) { 680 | this.namespace = namespace || null; 681 | this._storage = {}; 682 | }; 683 | 684 | MemoryStorage.prototype.setItem = function setItem (key, value) { 685 | this._storage[this._getStorageKey(key)] = value; 686 | }; 687 | 688 | MemoryStorage.prototype.getItem = function getItem (key) { 689 | return this._storage[this._getStorageKey(key)] 690 | }; 691 | 692 | MemoryStorage.prototype.removeItem = function removeItem (key) { 693 | delete this._storage[this._getStorageKey(key)]; 694 | }; 695 | 696 | MemoryStorage.prototype._getStorageKey = function _getStorageKey (key) { 697 | if (this.namespace) { 698 | return [this.namespace, key].join('.') 699 | } 700 | return key; 701 | }; 702 | 703 | var LocalStorage$2 = function LocalStorage(namespace) { 704 | this.namespace = namespace || null; 705 | }; 706 | 707 | LocalStorage$2.prototype.setItem = function setItem (key, value) { 708 | window.sessionStorage.setItem(this._getStorageKey(key), value); 709 | }; 710 | 711 | LocalStorage$2.prototype.getItem = function getItem (key) { 712 | return window.sessionStorage.getItem(this._getStorageKey(key)) 713 | }; 714 | 715 | LocalStorage$2.prototype.removeItem = function removeItem (key) { 716 | window.sessionStorage.removeItem(this._getStorageKey(key)); 717 | }; 718 | 719 | LocalStorage$2.prototype._getStorageKey = function _getStorageKey (key) { 720 | if (this.namespace) { 721 | return [this.namespace, key].join('.') 722 | } 723 | return key; 724 | }; 725 | 726 | function StorageFactory(options) { 727 | switch (options.storageType) { 728 | case 'localStorage': 729 | try { 730 | window.localStorage.setItem('testKey', 'test'); 731 | window.localStorage.removeItem('testKey'); 732 | return new LocalStorage(options.storageNamespace) 733 | } catch(e) {} 734 | 735 | case 'sessionStorage': 736 | try { 737 | window.sessionStorage.setItem('testKey', 'test'); 738 | window.sessionStorage.removeItem('testKey'); 739 | return new LocalStorage$2(options.storageNamespace) 740 | } catch (e) {} 741 | 742 | case 'cookieStorage': 743 | return new CookieStorage(options.cookieStorage); 744 | 745 | case 'memoryStorage': 746 | default: 747 | return new MemoryStorage(options.storageNamespace) 748 | break; 749 | } 750 | } 751 | 752 | /** 753 | * OAuth2 popup management class 754 | * 755 | * @author Sahat Yalkabov 756 | * @copyright Class mostly taken from https://github.com/sahat/satellizer 757 | * and adjusted to fit vue-social-auth library 758 | */ 759 | var OAuthPopup = function OAuthPopup(url, name, popupOptions) { 760 | this.popup = null; 761 | this.url = url; 762 | this.name = name; 763 | this.popupOptions = popupOptions; 764 | }; 765 | 766 | OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { 767 | try { 768 | this.popup = window.open(this.url, this.name, this._stringifyOptions()); 769 | if (this.popup && this.popup.focus) { 770 | this.popup.focus(); 771 | } 772 | 773 | if (skipPooling) { 774 | return Promise$1.resolve() 775 | } else { 776 | return this.pooling(redirectUri) 777 | } 778 | } catch(e) { 779 | return Promise$1.reject(new Error('OAuth popup error occurred')) 780 | } 781 | }; 782 | 783 | OAuthPopup.prototype.pooling = function pooling (redirectUri) { 784 | var this$1 = this; 785 | 786 | return new Promise$1(function (resolve, reject) { 787 | var redirectUriParser = document.createElement('a'); 788 | redirectUriParser.href = redirectUri; 789 | var redirectUriPath = getFullUrlPath(redirectUriParser); 790 | 791 | var poolingInterval = setInterval(function () { 792 | if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { 793 | clearInterval(poolingInterval); 794 | poolingInterval = null; 795 | reject(new Error('Auth popup window closed')); 796 | } 797 | 798 | try { 799 | var popupWindowPath = getFullUrlPath(this$1.popup.location); 800 | 801 | if (popupWindowPath === redirectUriPath) { 802 | if (this$1.popup.location.search || this$1.popup.location.hash) { 803 | var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); 804 | var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); 805 | var params = objectExtend({}, query); 806 | params = objectExtend(params, hash); 807 | 808 | if (params.error) { 809 | reject(new Error(params.error)); 810 | } else { 811 | resolve(params); 812 | } 813 | } else { 814 | reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); 815 | } 816 | 817 | clearInterval(poolingInterval); 818 | poolingInterval = null; 819 | this$1.popup.close(); 820 | } 821 | } catch(e) { 822 | // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. 823 | } 824 | }, 250); 825 | }) 826 | }; 827 | 828 | OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { 829 | var this$1 = this; 830 | 831 | var options = []; 832 | for (var optionKey in this$1.popupOptions) { 833 | if (!isUndefined(this$1.popupOptions[optionKey])) { 834 | options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); 835 | } 836 | } 837 | return options.join(',') 838 | }; 839 | 840 | var defaultProviderConfig = { 841 | name: null, 842 | url: null, 843 | authorizationEndpoint: null, 844 | scope: null, 845 | scopePrefix: null, 846 | scopeDelimiter: null, 847 | redirectUri: null, 848 | requiredUrlParams: null, 849 | defaultUrlParams: null, 850 | oauthType: '1.0', 851 | popupOptions: {} 852 | }; 853 | 854 | var OAuth = function OAuth($http, storage, providerConfig, options) { 855 | this.$http = $http; 856 | this.storage = storage; 857 | this.providerConfig = objectExtend({}, defaultProviderConfig); 858 | this.providerConfig = objectExtend(this.providerConfig, providerConfig); 859 | this.options = options; 860 | }; 861 | 862 | /** 863 | * Initialize OAuth1 process 864 | * @param{Object} userData User data 865 | * @return {Promise} 866 | */ 867 | OAuth.prototype.init = function init (userData) { 868 | var this$1 = this; 869 | 870 | this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); 871 | 872 | if (window && !window['cordova']) { 873 | this.oauthPopup.open(this.providerConfig.redirectUri, true); 874 | } 875 | 876 | return this.getRequestToken().then(function (response) { 877 | return this$1.openPopup(response).then(function (popupResponse) { 878 | return this$1.exchangeForToken(popupResponse, userData) 879 | }) 880 | }) 881 | }; 882 | 883 | /** 884 | * Get OAuth1 request token 885 | * @return {Promise} 886 | */ 887 | OAuth.prototype.getRequestToken = function getRequestToken () { 888 | var requestOptions = {}; 889 | requestOptions.method = 'POST'; 890 | requestOptions[this.options.requestDataKey] = objectExtend({}, this.providerConfig); 891 | requestOptions.withCredentials = this.options.withCredentials; 892 | if (this.options.baseUrl) { 893 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url); 894 | } else { 895 | requestOptions.url = this.providerConfig.url; 896 | } 897 | 898 | return this.$http(requestOptions) 899 | }; 900 | 901 | /** 902 | * Open OAuth1 popup 903 | * @param{Object} response Response object containing request token 904 | * @return {Promise} 905 | */ 906 | OAuth.prototype.openPopup = function openPopup (response) { 907 | var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); 908 | 909 | this.oauthPopup.popup.location = url; 910 | if (window && window['cordova']) { 911 | return this.oauthPopup.open(this.providerConfig.redirectUri) 912 | } else { 913 | return this.oauthPopup.pooling(this.providerConfig.redirectUri) 914 | } 915 | }; 916 | 917 | /** 918 | * Exchange token and token verifier for access token 919 | * @param{Object} oauth OAuth data containing token and token verifier 920 | * @param{Object} userData User data 921 | * @return {Promise} 922 | */ 923 | OAuth.prototype.exchangeForToken = function exchangeForToken (oauth, userData) { 924 | var payload = objectExtend({}, userData); 925 | payload = objectExtend(payload, oauth); 926 | var requestOptions = {}; 927 | requestOptions.method = 'POST'; 928 | requestOptions[this.options.requestDataKey] = payload; 929 | requestOptions.withCredentials = this.options.withCredentials; 930 | if (this.options.baseUrl) { 931 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url); 932 | } else { 933 | requestOptions.url = this.providerConfig.url; 934 | } 935 | return this.$http(requestOptions) 936 | }; 937 | 938 | OAuth.prototype.buildQueryString = function buildQueryString (params) { 939 | var parsedParams = []; 940 | for (var key in params) { 941 | var value = params[key]; 942 | parsedParams.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); 943 | } 944 | return parsedParams.join('&'); 945 | }; 946 | 947 | /** 948 | * Default provider configuration 949 | * @type {Object} 950 | */ 951 | var defaultProviderConfig$1 = { 952 | name: null, 953 | url: null, 954 | clientId: null, 955 | authorizationEndpoint: null, 956 | redirectUri: null, 957 | scope: null, 958 | scopePrefix: null, 959 | scopeDelimiter: null, 960 | state: null, 961 | requiredUrlParams: null, 962 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 963 | responseType: 'code', 964 | responseParams: { 965 | code: 'code', 966 | clientId: 'clientId', 967 | redirectUri: 'redirectUri' 968 | }, 969 | oauthType: '2.0', 970 | popupOptions: {} 971 | }; 972 | 973 | var OAuth2 = function OAuth2($http, storage, providerConfig, options) { 974 | this.$http = $http; 975 | this.storage = storage; 976 | this.providerConfig = objectExtend({}, defaultProviderConfig$1); 977 | this.providerConfig = objectExtend(this.providerConfig, providerConfig); 978 | this.options = options; 979 | }; 980 | 981 | OAuth2.prototype.init = function init (userData) { 982 | var this$1 = this; 983 | 984 | var stateName = this.providerConfig.name + '_state'; 985 | if (isFunction(this.providerConfig.state)) { 986 | this.storage.setItem(stateName, this.providerConfig.state()); 987 | } else if (isString(this.providerConfig.state)) { 988 | this.storage.setItem(stateName, this.providerConfig.state); 989 | } 990 | 991 | var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); 992 | 993 | this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); 994 | 995 | return new Promise(function (resolve, reject) { 996 | this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { 997 | if (this$1.providerConfig.responseType === 'code' || !this$1.providerConfig.url) { 998 | return resolve(response) 999 | } 1000 | 1001 | if (response.state && response.state !== this$1.storage.getItem(stateName)) { 1002 | return reject(new Error('State parameter value does not match original OAuth request state value')) 1003 | } 1004 | 1005 | resolve(this$1.exchangeForToken(response, userData)); 1006 | }).catch(function (err) { 1007 | reject(err); 1008 | }); 1009 | }) 1010 | }; 1011 | 1012 | /** 1013 | * Exchange temporary oauth data for access token 1014 | * @author Sahat Yalkabov 1015 | * @copyright Method taken from https://github.com/sahat/satellizer 1016 | * 1017 | * @param{[type]} oauth [description] 1018 | * @param{[type]} userData [description] 1019 | * @return {[type]} [description] 1020 | */ 1021 | OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) { 1022 | var this$1 = this; 1023 | 1024 | var payload = objectExtend({}, userData); 1025 | 1026 | for (var key in defaultProviderConfig$1.responseParams) { 1027 | var value = defaultProviderConfig$1[key]; 1028 | 1029 | switch(key) { 1030 | case 'code': 1031 | payload[key] = oauth.code; 1032 | break 1033 | case 'clientId': 1034 | payload[key] = this$1.providerConfig.clientId; 1035 | break 1036 | case 'redirectUri': 1037 | payload[key] = this$1.providerConfig.redirectUri; 1038 | break 1039 | default: 1040 | payload[key] = oauth[key]; 1041 | } 1042 | } 1043 | 1044 | if (oauth.state) { 1045 | payload.state = oauth.state; 1046 | } 1047 | 1048 | var exchangeTokenUrl; 1049 | if (this.options.baseUrl) { 1050 | exchangeTokenUrl = joinUrl(this.options.baseUrl, this.providerConfig.url); 1051 | } else { 1052 | exchangeTokenUrl = this.providerConfig.url; 1053 | } 1054 | 1055 | return this.$http.post(exchangeTokenUrl, payload, { 1056 | withCredentials: this.options.withCredentials 1057 | }) 1058 | }; 1059 | 1060 | /** 1061 | * Stringify oauth params 1062 | * @author Sahat Yalkabov 1063 | * @copyright Method taken from https://github.com/sahat/satellizer 1064 | * 1065 | * @return {String} 1066 | */ 1067 | OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { 1068 | var this$1 = this; 1069 | 1070 | var keyValuePairs = []; 1071 | var paramCategories = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; 1072 | 1073 | paramCategories.forEach(function (categoryName) { 1074 | if (!this$1.providerConfig[categoryName]) { return } 1075 | if (!Array.isArray(this$1.providerConfig[categoryName])) { return } 1076 | 1077 | this$1.providerConfig[categoryName].forEach(function (paramName) { 1078 | var camelCaseParamName = camelCase(paramName); 1079 | var paramValue = isFunction(this$1.providerConfig[paramName]) ? this$1.providerConfig[paramName]() : this$1.providerConfig[camelCaseParamName]; 1080 | 1081 | if (paramName === 'redirect_uri' && !paramValue) { return } 1082 | 1083 | if (paramName === 'state') { 1084 | var stateName = this$1.providerConfig.name + '_state'; 1085 | paramValue = encodeURIComponent(this$1.storage.getItem(stateName)); 1086 | } 1087 | if (paramName === 'scope' && Array.isArray(paramValue)) { 1088 | paramValue = paramValue.join(this$1.providerConfig.scopeDelimiter); 1089 | if (this$1.providerConfig.scopePrefix) { 1090 | paramValue = [this$1.providerConfig.scopePrefix, paramValue].join(this$1.providerConfig.scopeDelimiter); 1091 | } 1092 | } 1093 | 1094 | keyValuePairs.push([paramName, paramValue]); 1095 | }); 1096 | }); 1097 | 1098 | return keyValuePairs.map(function (param) { 1099 | return param.join('=') 1100 | }).join('&') 1101 | }; 1102 | 1103 | var VueSocialauth = function VueSocialauth($http, overrideOptions) { 1104 | var options = objectExtend({}, defaultOptions); 1105 | options = objectExtend(options, overrideOptions); 1106 | var storage = StorageFactory(options); 1107 | 1108 | Object.defineProperties(this, { 1109 | $http: { 1110 | get: function get() { 1111 | return $http 1112 | } 1113 | }, 1114 | 1115 | options: { 1116 | get: function get() { 1117 | return options 1118 | } 1119 | }, 1120 | 1121 | storage: { 1122 | get: function get() { 1123 | return storage 1124 | } 1125 | }, 1126 | 1127 | tokenName: { 1128 | get: function get() { 1129 | if (this.options.tokenPrefix) { 1130 | return [this.options.tokenPrefix, this.options.tokenName].join('_') 1131 | } else { 1132 | return this.options.tokenName 1133 | } 1134 | } 1135 | } 1136 | }); 1137 | 1138 | // Setup request interceptors 1139 | if (this.options.bindRequestInterceptor && isFunction(this.options.bindRequestInterceptor) && 1140 | this.options.bindResponseInterceptor && isFunction(this.options.bindResponseInterceptor)) { 1141 | 1142 | this.options.bindRequestInterceptor.call(this, this); 1143 | this.options.bindResponseInterceptor.call(this, this); 1144 | } else { 1145 | throw new Error('Both request and response interceptors must be functions') 1146 | } 1147 | }; 1148 | 1149 | /** 1150 | * Check if user is authenticated 1151 | * @author Sahat Yalkabov 1152 | * @copyright Method taken from https://github.com/sahat/satellizer 1153 | * @return {Boolean} 1154 | */ 1155 | // isAuthenticated() { 1156 | // let token = this.storage.getItem(this.tokenName) 1157 | 1158 | // if (token) {// Token is present 1159 | // if (token.split('.').length === 3) {// Token with a valid JWT format XXX.YYY.ZZZ 1160 | // try { // Could be a valid JWT or an access token with the same format 1161 | // const base64Url = token.split('.')[1]; 1162 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 1163 | // const exp = JSON.parse(window.atob(base64)).exp; 1164 | // if (typeof exp === 'number') {// JWT with an optonal expiration claims 1165 | // return Math.round(new Date().getTime() / 1000) < exp; 1166 | // } 1167 | // } catch (e) { 1168 | // return true;// Pass: Non-JWT token that looks like JWT 1169 | // } 1170 | // } 1171 | // return true;// Pass: All other tokens 1172 | // } 1173 | // return false 1174 | // } 1175 | 1176 | /** 1177 | * Get token if user is authenticated 1178 | * @return {String} Authentication token 1179 | */ 1180 | VueSocialauth.prototype.getToken = function getToken () { 1181 | return this.storage.getItem(this.tokenName) 1182 | }; 1183 | 1184 | /** 1185 | * Set new authentication token 1186 | * @param {String|Object} token 1187 | */ 1188 | // setToken(response) { 1189 | // if (response[this.options.responseDataKey]) { 1190 | // response = response[this.options.responseDataKey]; 1191 | // } 1192 | 1193 | // let token; 1194 | // if (response.access_token) { 1195 | // if (isObject(response.access_token) && isObject(response.access_token[this.options.responseDataKey])) { 1196 | // response = response.access_token 1197 | // } else if (isString(response.access_token)) { 1198 | // token = response.access_token 1199 | // } 1200 | // } 1201 | 1202 | // if (!token && response) { 1203 | // token = response[this.options.tokenName] 1204 | // } 1205 | 1206 | // if (token) { 1207 | // this.storage.setItem(this.tokenName, token) 1208 | // } 1209 | // } 1210 | 1211 | // getPayload() { 1212 | // const token = this.storage.getItem(this.tokenName); 1213 | 1214 | // if (token && token.split('.').length === 3) { 1215 | // try { 1216 | // const base64Url = token.split('.')[1]; 1217 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 1218 | // return JSON.parse(decodeBase64(base64)); 1219 | // } catch (e) {} 1220 | // } 1221 | // } 1222 | 1223 | /** 1224 | * Login user using email and password 1225 | * @param{Object} user User data 1226 | * @param{Object} requestOptions Request options 1227 | * @return {Promise} Request promise 1228 | */ 1229 | // login(user, requestOptions) { 1230 | // requestOptions = requestOptions || {} 1231 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.loginUrl) 1232 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 1233 | // requestOptions.method = requestOptions.method || 'POST' 1234 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1235 | 1236 | // return this.$http(requestOptions).then((response) => { 1237 | // this.setToken(response) 1238 | // return response 1239 | // }) 1240 | // } 1241 | 1242 | /** 1243 | * Register new user 1244 | * @param{Object} user User data 1245 | * @param{Object} requestOptions Request options 1246 | * @return {Promise} Request promise 1247 | */ 1248 | // register(user, requestOptions) { 1249 | // requestOptions = requestOptions || {} 1250 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.registerUrl) 1251 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 1252 | // requestOptions.method = requestOptions.method || 'POST' 1253 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1254 | 1255 | // return this.$http(requestOptions).then((response) => { 1256 | // this.setToken(response) 1257 | // return response 1258 | // }) 1259 | // } 1260 | 1261 | /** 1262 | * Logout current user 1263 | * @param{Object} requestOptionsLogout request options object 1264 | * @return {Promise} Request promise 1265 | */ 1266 | // logout(requestOptions) { 1267 | // if (!this.isAuthenticated()) { 1268 | // return Promise.reject(new Error('There is no currently authenticated user')) 1269 | // } 1270 | 1271 | // requestOptions = requestOptions || {} 1272 | // requestOptions.url = requestOptions.logoutUrl || this.options.logoutUrl 1273 | 1274 | // if (requestOptions.url) { 1275 | // requestOptions.method = requestOptions.method || 'POST' 1276 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1277 | 1278 | // return this.$http(requestOptions).then((response) => { 1279 | // this.storage.removeItem(this.tokenName) 1280 | // }) 1281 | // } else { 1282 | // this.storage.removeItem(this.tokenName) 1283 | // return Promise.resolve(); 1284 | // } 1285 | // } 1286 | 1287 | /** 1288 | * Authenticate user using authentication provider 1289 | * 1290 | * @param{String} provider Provider name 1291 | * @param{Object} userData User data 1292 | * @param{Object} requestOptions Request options 1293 | * @return {Promise} Request promise 1294 | */ 1295 | VueSocialauth.prototype.authenticate = function authenticate (provider, userData, requestOptions) { 1296 | var this$1 = this; 1297 | 1298 | return new Promise$1(function (resolve, reject) { 1299 | var providerConfig = this$1.options.providers[provider]; 1300 | if (!providerConfig) { 1301 | return reject(new Error('Unknown provider')) 1302 | } 1303 | 1304 | var providerInstance; 1305 | switch (providerConfig.oauthType) { 1306 | case '1.0': 1307 | providerInstance = new OAuth(this$1.$http, this$1.storage, providerConfig, this$1.options); 1308 | break 1309 | case '2.0': 1310 | providerInstance = new OAuth2(this$1.$http, this$1.storage, providerConfig, this$1.options); 1311 | break 1312 | default: 1313 | return reject(new Error('Invalid OAuth type')) 1314 | break 1315 | } 1316 | 1317 | return providerInstance.init(userData).then(function (response) { 1318 | return resolve(response) 1319 | 1320 | }).catch(function (err) { return reject(err); }) 1321 | }) 1322 | }; 1323 | 1324 | /** 1325 | * VueSocialauth plugin 1326 | * @param {Object} Vue 1327 | * @param {Object} options 1328 | */ 1329 | function plugin(Vue, options) { 1330 | if (plugin.installed) { 1331 | return 1332 | } 1333 | plugin.installed = true; 1334 | 1335 | var property = options.property || '$auth'; 1336 | 1337 | var vueAuthInstance = null; 1338 | Object.defineProperties(Vue.prototype, ( obj = {}, obj[property] = { 1339 | get: function get() { 1340 | if (!vueAuthInstance) { 1341 | // Request handler library not found, throw error 1342 | // verified vue or nuxt instance 1343 | if (this.$axios) { 1344 | vueAuthInstance = new VueSocialauth(this.$axios, options); 1345 | } else if (this.$http) { 1346 | vueAuthInstance = new VueSocialauth(this.$http, options); 1347 | } else { 1348 | throw new Error('Request handler instance not found') 1349 | } 1350 | } 1351 | return vueAuthInstance 1352 | } 1353 | }, obj )); 1354 | var obj; 1355 | } 1356 | 1357 | /** 1358 | * External factory helper for ES5 and CommonJS 1359 | * @param {Object} $http Instance of request handling library 1360 | * @param {Object} options Configuration object 1361 | * @return {VueSocialauth} VueSocialauth instance 1362 | */ 1363 | plugin.factory = function ($http, options) { 1364 | return new VueSocialauth($http, options) 1365 | }; 1366 | 1367 | module.exports = plugin; 1368 | -------------------------------------------------------------------------------- /dist/vue-social-auth.es2017.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-social-auth v0.0.1 3 | * https://github.com/diadal/vue-social-auth 4 | * Released under the MIT License. 5 | */ 6 | 7 | if (typeof Object.assign != 'function') { 8 | Object.assign = function(target, varArgs) { 9 | 'use strict'; 10 | var arguments$1 = arguments; 11 | 12 | if (target == null) { 13 | throw new TypeError('Cannot convert undefined or null to object'); 14 | } 15 | 16 | var to = Object(target); 17 | 18 | for (var index = 1; index < arguments.length; index++) { 19 | var nextSource = arguments$1[index]; 20 | 21 | if (nextSource != null) { // Skip over if undefined or null 22 | for (var nextKey in nextSource) { 23 | // Avoid bugs when hasOwnProperty is shadowed 24 | if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 25 | to[nextKey] = nextSource[nextKey]; 26 | } 27 | } 28 | } 29 | } 30 | return to; 31 | }; 32 | } 33 | 34 | function camelCase(name) { 35 | return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { 36 | return offset ? letter.toUpperCase() : letter; 37 | }); 38 | } 39 | 40 | function isUndefined(value) { 41 | return typeof value === 'undefined' 42 | } 43 | 44 | 45 | 46 | 47 | 48 | function isString(value) { 49 | return typeof value === 'string' 50 | } 51 | 52 | 53 | 54 | function isFunction(value) { 55 | return typeof value === 'function' 56 | } 57 | 58 | function objectExtend(a, b) { 59 | 60 | // Don't touch 'null' or 'undefined' objects. 61 | if (a == null || b == null) { 62 | return a; 63 | } 64 | 65 | Object.keys(b).forEach(function (key) { 66 | if (Object.prototype.toString.call(b[key]) == '[object Object]') { 67 | if (Object.prototype.toString.call(a[key]) != '[object Object]') { 68 | a[key] = b[key]; 69 | } else { 70 | a[key] = objectExtend(a[key], b[key]); 71 | } 72 | } else { 73 | a[key] = b[key]; 74 | } 75 | }); 76 | 77 | return a; 78 | } 79 | 80 | /** 81 | * Assemble url from two segments 82 | * 83 | * @author Sahat Yalkabov 84 | * @copyright Method taken from https://github.com/sahat/satellizer 85 | * 86 | * @param {String} baseUrl Base url 87 | * @param {String} url URI 88 | * @return {String} 89 | */ 90 | function joinUrl(baseUrl, url) { 91 | if (/^(?:[a-z]+:)?\/\//i.test(url)) { 92 | return url; 93 | } 94 | var joined = [baseUrl, url].join('/'); 95 | var normalize = function (str) { 96 | return str 97 | .replace(/[\/]+/g, '/') 98 | .replace(/\/\?/g, '?') 99 | .replace(/\/\#/g, '#') 100 | .replace(/\:\//g, '://'); 101 | }; 102 | return normalize(joined); 103 | } 104 | 105 | /** 106 | * Get full path based on current location 107 | * 108 | * @author Sahat Yalkabov 109 | * @copyright Method taken from https://github.com/sahat/satellizer 110 | * 111 | * @param {Location} location 112 | * @return {String} 113 | */ 114 | function getFullUrlPath(location) { 115 | var isHttps = location.protocol === 'https:'; 116 | return location.protocol + '//' + location.hostname + 117 | ':' + (location.port || (isHttps ? '443' : '80')) + 118 | (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); 119 | } 120 | 121 | /** 122 | * Parse query string variables 123 | * 124 | * @author Sahat Yalkabov 125 | * @copyright Method taken from https://github.com/sahat/satellizer 126 | * 127 | * @param {String} Query string 128 | * @return {String} 129 | */ 130 | function parseQueryString(str) { 131 | var obj = {}; 132 | var key; 133 | var value; 134 | (str || '').split('&').forEach(function (keyValue) { 135 | if (keyValue) { 136 | value = keyValue.split('='); 137 | key = decodeURIComponent(value[0]); 138 | obj[key] = (!!value[1]) ? decodeURIComponent(value[1]) : true; 139 | } 140 | }); 141 | return obj; 142 | } 143 | 144 | /** 145 | * Decode base64 string 146 | * @author Sahat Yalkabov 147 | * @copyright Method taken from https://github.com/sahat/satellizer 148 | * 149 | * @param {String} str base64 encoded string 150 | * @return {Object} 151 | */ 152 | 153 | 154 | function parseCookies(str) { 155 | if (str.length === 0) { return {}; } 156 | var parsed = {}; 157 | var pattern = new RegExp('\\s*;\\s*'); 158 | str.split(pattern).forEach(function (i) { 159 | var ref = i.split('='); 160 | var encodedKey = ref[0]; 161 | var encodedValue = ref[1]; 162 | var key = decodeURIComponent(encodedKey); 163 | var value = decodeURIComponent(encodedValue); 164 | parsed[key] = value; 165 | }); 166 | return parsed; 167 | } 168 | 169 | function formatOptions(options) { 170 | var path = options.path; 171 | var domain = options.domain; 172 | var expires = options.expires; 173 | var secure = options.secure; 174 | return [ 175 | typeof path === 'undefined' || path === null 176 | ? '' : ';path=' + path, 177 | typeof domain === 'undefined' || domain === null 178 | ? '' : ';domain=' + domain, 179 | typeof expires === 'undefined' || expires === null 180 | ? '' : ';expires=' + expires.toUTCString(), 181 | typeof secure === 'undefined' || secure === null || secure === false 182 | ? '' : ';secure' 183 | ].join(''); 184 | } 185 | 186 | function formatCookie(key, value, options) { 187 | return [ 188 | encodeURIComponent(key), 189 | '=', 190 | encodeURIComponent(value), 191 | formatOptions(options) 192 | ].join(''); 193 | } 194 | 195 | // Store setTimeout reference so promise-polyfill will be unaffected by 196 | // other code modifying setTimeout (like sinon.useFakeTimers()) 197 | var setTimeoutFunc = setTimeout; 198 | 199 | function noop() {} 200 | 201 | // Polyfill for Function.prototype.bind 202 | function bind(fn, thisArg) { 203 | return function () { 204 | fn.apply(thisArg, arguments); 205 | }; 206 | } 207 | 208 | function Promise$1(fn) { 209 | if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } 210 | if (typeof fn !== 'function') { throw new TypeError('not a function'); } 211 | this._state = 0; 212 | this._handled = false; 213 | this._value = undefined; 214 | this._deferreds = []; 215 | 216 | doResolve(fn, this); 217 | } 218 | 219 | function handle(self, deferred) { 220 | while (self._state === 3) { 221 | self = self._value; 222 | } 223 | if (self._state === 0) { 224 | self._deferreds.push(deferred); 225 | return; 226 | } 227 | self._handled = true; 228 | Promise$1._immediateFn(function () { 229 | var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 230 | if (cb === null) { 231 | (self._state === 1 ? resolve : reject)(deferred.promise, self._value); 232 | return; 233 | } 234 | var ret; 235 | try { 236 | ret = cb(self._value); 237 | } catch (e) { 238 | reject(deferred.promise, e); 239 | return; 240 | } 241 | resolve(deferred.promise, ret); 242 | }); 243 | } 244 | 245 | function resolve(self, newValue) { 246 | try { 247 | // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 248 | if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.'); } 249 | if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { 250 | var then = newValue.then; 251 | if (newValue instanceof Promise$1) { 252 | self._state = 3; 253 | self._value = newValue; 254 | finale(self); 255 | return; 256 | } else if (typeof then === 'function') { 257 | doResolve(bind(then, newValue), self); 258 | return; 259 | } 260 | } 261 | self._state = 1; 262 | self._value = newValue; 263 | finale(self); 264 | } catch (e) { 265 | reject(self, e); 266 | } 267 | } 268 | 269 | function reject(self, newValue) { 270 | self._state = 2; 271 | self._value = newValue; 272 | finale(self); 273 | } 274 | 275 | function finale(self) { 276 | if (self._state === 2 && self._deferreds.length === 0) { 277 | Promise$1._immediateFn(function() { 278 | if (!self._handled) { 279 | Promise$1._unhandledRejectionFn(self._value); 280 | } 281 | }); 282 | } 283 | 284 | for (var i = 0, len = self._deferreds.length; i < len; i++) { 285 | handle(self, self._deferreds[i]); 286 | } 287 | self._deferreds = null; 288 | } 289 | 290 | function Handler(onFulfilled, onRejected, promise) { 291 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 292 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 293 | this.promise = promise; 294 | } 295 | 296 | /** 297 | * Take a potentially misbehaving resolver function and make sure 298 | * onFulfilled and onRejected are only called once. 299 | * 300 | * Makes no guarantees about asynchrony. 301 | */ 302 | function doResolve(fn, self) { 303 | var done = false; 304 | try { 305 | fn(function (value) { 306 | if (done) { return; } 307 | done = true; 308 | resolve(self, value); 309 | }, function (reason) { 310 | if (done) { return; } 311 | done = true; 312 | reject(self, reason); 313 | }); 314 | } catch (ex) { 315 | if (done) { return; } 316 | done = true; 317 | reject(self, ex); 318 | } 319 | } 320 | 321 | Promise$1.prototype['catch'] = function (onRejected) { 322 | return this.then(null, onRejected); 323 | }; 324 | 325 | Promise$1.prototype.then = function (onFulfilled, onRejected) { 326 | var prom = new (this.constructor)(noop); 327 | 328 | handle(this, new Handler(onFulfilled, onRejected, prom)); 329 | return prom; 330 | }; 331 | 332 | Promise$1.all = function (arr) { 333 | var args = Array.prototype.slice.call(arr); 334 | 335 | return new Promise$1(function (resolve, reject) { 336 | if (args.length === 0) { return resolve([]); } 337 | var remaining = args.length; 338 | 339 | function res(i, val) { 340 | try { 341 | if (val && (typeof val === 'object' || typeof val === 'function')) { 342 | var then = val.then; 343 | if (typeof then === 'function') { 344 | then.call(val, function (val) { 345 | res(i, val); 346 | }, reject); 347 | return; 348 | } 349 | } 350 | args[i] = val; 351 | if (--remaining === 0) { 352 | resolve(args); 353 | } 354 | } catch (ex) { 355 | reject(ex); 356 | } 357 | } 358 | 359 | for (var i = 0; i < args.length; i++) { 360 | res(i, args[i]); 361 | } 362 | }); 363 | }; 364 | 365 | Promise$1.resolve = function (value) { 366 | if (value && typeof value === 'object' && value.constructor === Promise$1) { 367 | return value; 368 | } 369 | 370 | return new Promise$1(function (resolve) { 371 | resolve(value); 372 | }); 373 | }; 374 | 375 | Promise$1.reject = function (value) { 376 | return new Promise$1(function (resolve, reject) { 377 | reject(value); 378 | }); 379 | }; 380 | 381 | Promise$1.race = function (values) { 382 | return new Promise$1(function (resolve, reject) { 383 | for (var i = 0, len = values.length; i < len; i++) { 384 | values[i].then(resolve, reject); 385 | } 386 | }); 387 | }; 388 | 389 | // Use polyfill for setImmediate for performance gains 390 | Promise$1._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) || 391 | function (fn) { 392 | setTimeoutFunc(fn, 0); 393 | }; 394 | 395 | Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { 396 | if (typeof console !== 'undefined' && console) { 397 | console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console 398 | } 399 | }; 400 | 401 | /** 402 | * Set the immediate function to execute callbacks 403 | * @param fn {function} Function to execute 404 | * @deprecated 405 | */ 406 | Promise$1._setImmediateFn = function _setImmediateFn(fn) { 407 | Promise$1._immediateFn = fn; 408 | }; 409 | 410 | /** 411 | * Change the function to execute on unhandled rejection 412 | * @param {function} fn Function to execute on unhandled rejection 413 | * @deprecated 414 | */ 415 | Promise$1._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) { 416 | Promise$1._unhandledRejectionFn = fn; 417 | }; 418 | 419 | /** 420 | * Default configuration 421 | */ 422 | var defaultOptions = { 423 | baseUrl: null, 424 | tokenName: 'token', 425 | tokenPrefix: 'vueauth', 426 | tokenHeader: 'Authorization', 427 | tokenType: 'Bearer', 428 | loginUrl: '/auth/login', 429 | registerUrl: '/auth/register', 430 | logoutUrl: null, 431 | storageType: 'localStorage', 432 | storageNamespace: 'vue-social-auth', 433 | cookieStorage: { 434 | domain: window.location.hostname, 435 | path: '/', 436 | secure: false 437 | }, 438 | requestDataKey: 'data', 439 | responseDataKey: 'data', 440 | 441 | /** 442 | * Default request interceptor for Axios library 443 | * @context {VueSocialauth} 444 | */ 445 | bindRequestInterceptor: function ($auth) { 446 | 447 | var tokenHeader = $auth.options.tokenHeader; 448 | 449 | $auth.$http.interceptors.request.use(function (config) { 450 | delete config.headers[tokenHeader]; 451 | return config 452 | }); 453 | 454 | 455 | }, 456 | 457 | /** 458 | * Default response interceptor for Axios library 459 | * @contect {VueSocialauth} 460 | */ 461 | bindResponseInterceptor: function ($auth) { 462 | $auth.$http.interceptors.response.use(function (response) { 463 | return response 464 | }); 465 | }, 466 | 467 | providers: { 468 | facebook: { 469 | name: 'facebook', 470 | url: '/auth/facebook', 471 | authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', 472 | redirectUri: window.location.origin + '/', 473 | requiredUrlParams: ['display', 'scope'], 474 | scope: ['email'], 475 | scopeDelimiter: ',', 476 | display: 'popup', 477 | oauthType: '2.0', 478 | popupOptions: { width: 580, height: 400 } 479 | }, 480 | 481 | google: { 482 | name: 'google', 483 | url: '/auth/google', 484 | authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', 485 | redirectUri: window.location.origin, 486 | requiredUrlParams: ['scope'], 487 | optionalUrlParams: ['display'], 488 | scope: ['profile', 'email'], 489 | scopePrefix: 'openid', 490 | scopeDelimiter: ' ', 491 | display: 'popup', 492 | oauthType: '2.0', 493 | popupOptions: { width: 452, height: 633 } 494 | }, 495 | 496 | github: { 497 | name: 'github', 498 | url: '/auth/github', 499 | authorizationEndpoint: 'https://github.com/login/oauth/authorize', 500 | redirectUri: window.location.origin, 501 | optionalUrlParams: ['scope'], 502 | scope: ['user:email'], 503 | scopeDelimiter: ' ', 504 | oauthType: '2.0', 505 | popupOptions: { width: 1020, height: 618 } 506 | }, 507 | 508 | instagram: { 509 | name: 'instagram', 510 | url: '/auth/instagram', 511 | authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', 512 | redirectUri: window.location.origin, 513 | requiredUrlParams: ['scope'], 514 | scope: ['basic'], 515 | scopeDelimiter: '+', 516 | oauthType: '2.0', 517 | popupOptions: { width: null, height: null } 518 | }, 519 | 520 | twitter: { 521 | name: 'twitter', 522 | url: '/auth/twitter', 523 | authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', 524 | redirectUri: window.location.origin, 525 | oauthType: '1.0', 526 | popupOptions: { width: 495, height: 645 } 527 | }, 528 | 529 | bitbucket: { 530 | name: 'bitbucket', 531 | url: '/auth/bitbucket', 532 | authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', 533 | redirectUri: window.location.origin + '/', 534 | optionalUrlParams: ['scope'], 535 | scope: ['email'], 536 | scopeDelimiter: ' ', 537 | oauthType: '2.0', 538 | popupOptions: { width: 1020, height: 618 } 539 | }, 540 | 541 | linkedin: { 542 | name: 'linkedin', 543 | url: '/auth/linkedin', 544 | authorizationEndpoint: 'https://www.linkedin.com/oauth/v2/authorization', 545 | redirectUri: window.location.origin, 546 | requiredUrlParams: ['state','scope'], 547 | scope: ['r_liteprofile','r_emailaddress'], 548 | scopeDelimiter: ' ', 549 | state: 'STATE', 550 | oauthType: '2.0', 551 | popupOptions: { width: 527, height: 582 } 552 | }, 553 | 554 | vkontakte: { 555 | name: 'vkontakte', 556 | url: '/auth/vkontakte', 557 | authorizationEndpoint: 'https://oauth.vk.com/authorize', 558 | redirectUri: window.location.origin + '/', 559 | requiredUrlParams: ['scope'], 560 | scope: ['email'], 561 | scopeDelimiter: ',', 562 | display: 'popup', 563 | oauthType: '2.0', 564 | popupOptions: { width: 580, height: 400 } 565 | }, 566 | 567 | live: { 568 | name: 'live', 569 | url: '/auth/live', 570 | authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', 571 | redirectUri: window.location.origin, 572 | requiredUrlParams: ['display', 'scope'], 573 | scope: ['wl.emails'], 574 | scopeDelimiter: ' ', 575 | display: 'popup', 576 | oauthType: '2.0', 577 | popupOptions: { width: 500, height: 560 } 578 | }, 579 | 580 | oauth1: { 581 | name: null, 582 | url: '/auth/oauth1', 583 | authorizationEndpoint: null, 584 | redirectUri: window.location.origin, 585 | oauthType: '1.0', 586 | popupOptions: null 587 | }, 588 | 589 | oauth2: { 590 | name: null, 591 | url: '/auth/oauth2', 592 | clientId: null, 593 | redirectUri: window.location.origin, 594 | authorizationEndpoint: null, 595 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 596 | requiredUrlParams: null, 597 | optionalUrlParams: null, 598 | scope: null, 599 | scopePrefix: null, 600 | scopeDelimiter: null, 601 | state: null, 602 | oauthType: '2.0', 603 | popupOptions: null, 604 | responseType: 'code', 605 | responseParams: { 606 | code: 'code', 607 | clientId: 'clientId', 608 | redirectUri: 'redirectUri' 609 | } 610 | } 611 | } 612 | }; 613 | 614 | var CookieStorage = function CookieStorage(defaultOptions) { 615 | this._defaultOptions = objectExtend({ 616 | domain: window.location.hostname, 617 | expires: null, 618 | path: '/', 619 | secure: false 620 | }, defaultOptions); 621 | }; 622 | 623 | CookieStorage.prototype.setItem = function setItem (key, value) { 624 | var options = objectExtend({}, this._defaultOptions); 625 | var cookie = formatCookie(key, value, options); 626 | this._setCookie(cookie); 627 | }; 628 | 629 | CookieStorage.prototype.getItem = function getItem (key) { 630 | var cookies = parseCookies(this._getCookie()); 631 | return cookies.hasOwnProperty(key) ? cookies[key] : null; 632 | }; 633 | 634 | CookieStorage.prototype.removeItem = function removeItem (key) { 635 | var value = ''; 636 | var defaultOptions = objectExtend({}, this._defaultOptions); 637 | var options = objectExtend(defaultOptions, { 638 | expires: new Date(0) 639 | }); 640 | var cookie = formatCookie(key, value, options); 641 | this._setCookie(cookie); 642 | }; 643 | 644 | CookieStorage.prototype._getCookie = function _getCookie () { 645 | return typeof document === 'undefined' 646 | ? '' : typeof document.cookie === 'undefined' 647 | ? '' : document.cookie; 648 | }; 649 | 650 | CookieStorage.prototype._setCookie = function _setCookie (cookie) { 651 | document.cookie = cookie; 652 | }; 653 | 654 | var LocalStorage = function LocalStorage(namespace) { 655 | this.namespace = namespace || null; 656 | }; 657 | 658 | LocalStorage.prototype.setItem = function setItem (key, value) { 659 | window.localStorage.setItem(this._getStorageKey(key), value); 660 | }; 661 | 662 | LocalStorage.prototype.getItem = function getItem (key) { 663 | return window.localStorage.getItem(this._getStorageKey(key)) 664 | }; 665 | 666 | LocalStorage.prototype.removeItem = function removeItem (key) { 667 | window.localStorage.removeItem(this._getStorageKey(key)); 668 | }; 669 | 670 | LocalStorage.prototype._getStorageKey = function _getStorageKey (key) { 671 | if (this.namespace) { 672 | return [this.namespace, key].join('.') 673 | } 674 | return key; 675 | }; 676 | 677 | var MemoryStorage = function MemoryStorage(namespace) { 678 | this.namespace = namespace || null; 679 | this._storage = {}; 680 | }; 681 | 682 | MemoryStorage.prototype.setItem = function setItem (key, value) { 683 | this._storage[this._getStorageKey(key)] = value; 684 | }; 685 | 686 | MemoryStorage.prototype.getItem = function getItem (key) { 687 | return this._storage[this._getStorageKey(key)] 688 | }; 689 | 690 | MemoryStorage.prototype.removeItem = function removeItem (key) { 691 | delete this._storage[this._getStorageKey(key)]; 692 | }; 693 | 694 | MemoryStorage.prototype._getStorageKey = function _getStorageKey (key) { 695 | if (this.namespace) { 696 | return [this.namespace, key].join('.') 697 | } 698 | return key; 699 | }; 700 | 701 | var LocalStorage$2 = function LocalStorage(namespace) { 702 | this.namespace = namespace || null; 703 | }; 704 | 705 | LocalStorage$2.prototype.setItem = function setItem (key, value) { 706 | window.sessionStorage.setItem(this._getStorageKey(key), value); 707 | }; 708 | 709 | LocalStorage$2.prototype.getItem = function getItem (key) { 710 | return window.sessionStorage.getItem(this._getStorageKey(key)) 711 | }; 712 | 713 | LocalStorage$2.prototype.removeItem = function removeItem (key) { 714 | window.sessionStorage.removeItem(this._getStorageKey(key)); 715 | }; 716 | 717 | LocalStorage$2.prototype._getStorageKey = function _getStorageKey (key) { 718 | if (this.namespace) { 719 | return [this.namespace, key].join('.') 720 | } 721 | return key; 722 | }; 723 | 724 | function StorageFactory(options) { 725 | switch (options.storageType) { 726 | case 'localStorage': 727 | try { 728 | window.localStorage.setItem('testKey', 'test'); 729 | window.localStorage.removeItem('testKey'); 730 | return new LocalStorage(options.storageNamespace) 731 | } catch(e) {} 732 | 733 | case 'sessionStorage': 734 | try { 735 | window.sessionStorage.setItem('testKey', 'test'); 736 | window.sessionStorage.removeItem('testKey'); 737 | return new LocalStorage$2(options.storageNamespace) 738 | } catch (e) {} 739 | 740 | case 'cookieStorage': 741 | return new CookieStorage(options.cookieStorage); 742 | 743 | case 'memoryStorage': 744 | default: 745 | return new MemoryStorage(options.storageNamespace) 746 | break; 747 | } 748 | } 749 | 750 | /** 751 | * OAuth2 popup management class 752 | * 753 | * @author Sahat Yalkabov 754 | * @copyright Class mostly taken from https://github.com/sahat/satellizer 755 | * and adjusted to fit vue-social-auth library 756 | */ 757 | var OAuthPopup = function OAuthPopup(url, name, popupOptions) { 758 | this.popup = null; 759 | this.url = url; 760 | this.name = name; 761 | this.popupOptions = popupOptions; 762 | }; 763 | 764 | OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { 765 | try { 766 | this.popup = window.open(this.url, this.name, this._stringifyOptions()); 767 | if (this.popup && this.popup.focus) { 768 | this.popup.focus(); 769 | } 770 | 771 | if (skipPooling) { 772 | return Promise$1.resolve() 773 | } else { 774 | return this.pooling(redirectUri) 775 | } 776 | } catch(e) { 777 | return Promise$1.reject(new Error('OAuth popup error occurred')) 778 | } 779 | }; 780 | 781 | OAuthPopup.prototype.pooling = function pooling (redirectUri) { 782 | var this$1 = this; 783 | 784 | return new Promise$1(function (resolve, reject) { 785 | var redirectUriParser = document.createElement('a'); 786 | redirectUriParser.href = redirectUri; 787 | var redirectUriPath = getFullUrlPath(redirectUriParser); 788 | 789 | var poolingInterval = setInterval(function () { 790 | if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { 791 | clearInterval(poolingInterval); 792 | poolingInterval = null; 793 | reject(new Error('Auth popup window closed')); 794 | } 795 | 796 | try { 797 | var popupWindowPath = getFullUrlPath(this$1.popup.location); 798 | 799 | if (popupWindowPath === redirectUriPath) { 800 | if (this$1.popup.location.search || this$1.popup.location.hash) { 801 | var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); 802 | var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); 803 | var params = objectExtend({}, query); 804 | params = objectExtend(params, hash); 805 | 806 | if (params.error) { 807 | reject(new Error(params.error)); 808 | } else { 809 | resolve(params); 810 | } 811 | } else { 812 | reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); 813 | } 814 | 815 | clearInterval(poolingInterval); 816 | poolingInterval = null; 817 | this$1.popup.close(); 818 | } 819 | } catch(e) { 820 | // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. 821 | } 822 | }, 250); 823 | }) 824 | }; 825 | 826 | OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { 827 | var this$1 = this; 828 | 829 | var options = []; 830 | for (var optionKey in this$1.popupOptions) { 831 | if (!isUndefined(this$1.popupOptions[optionKey])) { 832 | options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); 833 | } 834 | } 835 | return options.join(',') 836 | }; 837 | 838 | var defaultProviderConfig = { 839 | name: null, 840 | url: null, 841 | authorizationEndpoint: null, 842 | scope: null, 843 | scopePrefix: null, 844 | scopeDelimiter: null, 845 | redirectUri: null, 846 | requiredUrlParams: null, 847 | defaultUrlParams: null, 848 | oauthType: '1.0', 849 | popupOptions: {} 850 | }; 851 | 852 | var OAuth = function OAuth($http, storage, providerConfig, options) { 853 | this.$http = $http; 854 | this.storage = storage; 855 | this.providerConfig = objectExtend({}, defaultProviderConfig); 856 | this.providerConfig = objectExtend(this.providerConfig, providerConfig); 857 | this.options = options; 858 | }; 859 | 860 | /** 861 | * Initialize OAuth1 process 862 | * @param{Object} userData User data 863 | * @return {Promise} 864 | */ 865 | OAuth.prototype.init = function init (userData) { 866 | var this$1 = this; 867 | 868 | this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); 869 | 870 | if (window && !window['cordova']) { 871 | this.oauthPopup.open(this.providerConfig.redirectUri, true); 872 | } 873 | 874 | return this.getRequestToken().then(function (response) { 875 | return this$1.openPopup(response).then(function (popupResponse) { 876 | return this$1.exchangeForToken(popupResponse, userData) 877 | }) 878 | }) 879 | }; 880 | 881 | /** 882 | * Get OAuth1 request token 883 | * @return {Promise} 884 | */ 885 | OAuth.prototype.getRequestToken = function getRequestToken () { 886 | var requestOptions = {}; 887 | requestOptions.method = 'POST'; 888 | requestOptions[this.options.requestDataKey] = objectExtend({}, this.providerConfig); 889 | requestOptions.withCredentials = this.options.withCredentials; 890 | if (this.options.baseUrl) { 891 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url); 892 | } else { 893 | requestOptions.url = this.providerConfig.url; 894 | } 895 | 896 | return this.$http(requestOptions) 897 | }; 898 | 899 | /** 900 | * Open OAuth1 popup 901 | * @param{Object} response Response object containing request token 902 | * @return {Promise} 903 | */ 904 | OAuth.prototype.openPopup = function openPopup (response) { 905 | var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); 906 | 907 | this.oauthPopup.popup.location = url; 908 | if (window && window['cordova']) { 909 | return this.oauthPopup.open(this.providerConfig.redirectUri) 910 | } else { 911 | return this.oauthPopup.pooling(this.providerConfig.redirectUri) 912 | } 913 | }; 914 | 915 | /** 916 | * Exchange token and token verifier for access token 917 | * @param{Object} oauth OAuth data containing token and token verifier 918 | * @param{Object} userData User data 919 | * @return {Promise} 920 | */ 921 | OAuth.prototype.exchangeForToken = function exchangeForToken (oauth, userData) { 922 | var payload = objectExtend({}, userData); 923 | payload = objectExtend(payload, oauth); 924 | var requestOptions = {}; 925 | requestOptions.method = 'POST'; 926 | requestOptions[this.options.requestDataKey] = payload; 927 | requestOptions.withCredentials = this.options.withCredentials; 928 | if (this.options.baseUrl) { 929 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url); 930 | } else { 931 | requestOptions.url = this.providerConfig.url; 932 | } 933 | return this.$http(requestOptions) 934 | }; 935 | 936 | OAuth.prototype.buildQueryString = function buildQueryString (params) { 937 | var parsedParams = []; 938 | for (var key in params) { 939 | var value = params[key]; 940 | parsedParams.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); 941 | } 942 | return parsedParams.join('&'); 943 | }; 944 | 945 | /** 946 | * Default provider configuration 947 | * @type {Object} 948 | */ 949 | var defaultProviderConfig$1 = { 950 | name: null, 951 | url: null, 952 | clientId: null, 953 | authorizationEndpoint: null, 954 | redirectUri: null, 955 | scope: null, 956 | scopePrefix: null, 957 | scopeDelimiter: null, 958 | state: null, 959 | requiredUrlParams: null, 960 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 961 | responseType: 'code', 962 | responseParams: { 963 | code: 'code', 964 | clientId: 'clientId', 965 | redirectUri: 'redirectUri' 966 | }, 967 | oauthType: '2.0', 968 | popupOptions: {} 969 | }; 970 | 971 | var OAuth2 = function OAuth2($http, storage, providerConfig, options) { 972 | this.$http = $http; 973 | this.storage = storage; 974 | this.providerConfig = objectExtend({}, defaultProviderConfig$1); 975 | this.providerConfig = objectExtend(this.providerConfig, providerConfig); 976 | this.options = options; 977 | }; 978 | 979 | OAuth2.prototype.init = function init (userData) { 980 | var this$1 = this; 981 | 982 | var stateName = this.providerConfig.name + '_state'; 983 | if (isFunction(this.providerConfig.state)) { 984 | this.storage.setItem(stateName, this.providerConfig.state()); 985 | } else if (isString(this.providerConfig.state)) { 986 | this.storage.setItem(stateName, this.providerConfig.state); 987 | } 988 | 989 | var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); 990 | 991 | this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); 992 | 993 | return new Promise(function (resolve, reject) { 994 | this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { 995 | if (this$1.providerConfig.responseType === 'code' || !this$1.providerConfig.url) { 996 | return resolve(response) 997 | } 998 | 999 | if (response.state && response.state !== this$1.storage.getItem(stateName)) { 1000 | return reject(new Error('State parameter value does not match original OAuth request state value')) 1001 | } 1002 | 1003 | resolve(this$1.exchangeForToken(response, userData)); 1004 | }).catch(function (err) { 1005 | reject(err); 1006 | }); 1007 | }) 1008 | }; 1009 | 1010 | /** 1011 | * Exchange temporary oauth data for access token 1012 | * @author Sahat Yalkabov 1013 | * @copyright Method taken from https://github.com/sahat/satellizer 1014 | * 1015 | * @param{[type]} oauth [description] 1016 | * @param{[type]} userData [description] 1017 | * @return {[type]} [description] 1018 | */ 1019 | OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) { 1020 | var this$1 = this; 1021 | 1022 | var payload = objectExtend({}, userData); 1023 | 1024 | for (var key in defaultProviderConfig$1.responseParams) { 1025 | var value = defaultProviderConfig$1[key]; 1026 | 1027 | switch(key) { 1028 | case 'code': 1029 | payload[key] = oauth.code; 1030 | break 1031 | case 'clientId': 1032 | payload[key] = this$1.providerConfig.clientId; 1033 | break 1034 | case 'redirectUri': 1035 | payload[key] = this$1.providerConfig.redirectUri; 1036 | break 1037 | default: 1038 | payload[key] = oauth[key]; 1039 | } 1040 | } 1041 | 1042 | if (oauth.state) { 1043 | payload.state = oauth.state; 1044 | } 1045 | 1046 | var exchangeTokenUrl; 1047 | if (this.options.baseUrl) { 1048 | exchangeTokenUrl = joinUrl(this.options.baseUrl, this.providerConfig.url); 1049 | } else { 1050 | exchangeTokenUrl = this.providerConfig.url; 1051 | } 1052 | 1053 | return this.$http.post(exchangeTokenUrl, payload, { 1054 | withCredentials: this.options.withCredentials 1055 | }) 1056 | }; 1057 | 1058 | /** 1059 | * Stringify oauth params 1060 | * @author Sahat Yalkabov 1061 | * @copyright Method taken from https://github.com/sahat/satellizer 1062 | * 1063 | * @return {String} 1064 | */ 1065 | OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { 1066 | var this$1 = this; 1067 | 1068 | var keyValuePairs = []; 1069 | var paramCategories = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; 1070 | 1071 | paramCategories.forEach(function (categoryName) { 1072 | if (!this$1.providerConfig[categoryName]) { return } 1073 | if (!Array.isArray(this$1.providerConfig[categoryName])) { return } 1074 | 1075 | this$1.providerConfig[categoryName].forEach(function (paramName) { 1076 | var camelCaseParamName = camelCase(paramName); 1077 | var paramValue = isFunction(this$1.providerConfig[paramName]) ? this$1.providerConfig[paramName]() : this$1.providerConfig[camelCaseParamName]; 1078 | 1079 | if (paramName === 'redirect_uri' && !paramValue) { return } 1080 | 1081 | if (paramName === 'state') { 1082 | var stateName = this$1.providerConfig.name + '_state'; 1083 | paramValue = encodeURIComponent(this$1.storage.getItem(stateName)); 1084 | } 1085 | if (paramName === 'scope' && Array.isArray(paramValue)) { 1086 | paramValue = paramValue.join(this$1.providerConfig.scopeDelimiter); 1087 | if (this$1.providerConfig.scopePrefix) { 1088 | paramValue = [this$1.providerConfig.scopePrefix, paramValue].join(this$1.providerConfig.scopeDelimiter); 1089 | } 1090 | } 1091 | 1092 | keyValuePairs.push([paramName, paramValue]); 1093 | }); 1094 | }); 1095 | 1096 | return keyValuePairs.map(function (param) { 1097 | return param.join('=') 1098 | }).join('&') 1099 | }; 1100 | 1101 | var VueSocialauth = function VueSocialauth($http, overrideOptions) { 1102 | var options = objectExtend({}, defaultOptions); 1103 | options = objectExtend(options, overrideOptions); 1104 | var storage = StorageFactory(options); 1105 | 1106 | Object.defineProperties(this, { 1107 | $http: { 1108 | get: function get() { 1109 | return $http 1110 | } 1111 | }, 1112 | 1113 | options: { 1114 | get: function get() { 1115 | return options 1116 | } 1117 | }, 1118 | 1119 | storage: { 1120 | get: function get() { 1121 | return storage 1122 | } 1123 | }, 1124 | 1125 | tokenName: { 1126 | get: function get() { 1127 | if (this.options.tokenPrefix) { 1128 | return [this.options.tokenPrefix, this.options.tokenName].join('_') 1129 | } else { 1130 | return this.options.tokenName 1131 | } 1132 | } 1133 | } 1134 | }); 1135 | 1136 | // Setup request interceptors 1137 | if (this.options.bindRequestInterceptor && isFunction(this.options.bindRequestInterceptor) && 1138 | this.options.bindResponseInterceptor && isFunction(this.options.bindResponseInterceptor)) { 1139 | 1140 | this.options.bindRequestInterceptor.call(this, this); 1141 | this.options.bindResponseInterceptor.call(this, this); 1142 | } else { 1143 | throw new Error('Both request and response interceptors must be functions') 1144 | } 1145 | }; 1146 | 1147 | /** 1148 | * Check if user is authenticated 1149 | * @author Sahat Yalkabov 1150 | * @copyright Method taken from https://github.com/sahat/satellizer 1151 | * @return {Boolean} 1152 | */ 1153 | // isAuthenticated() { 1154 | // let token = this.storage.getItem(this.tokenName) 1155 | 1156 | // if (token) {// Token is present 1157 | // if (token.split('.').length === 3) {// Token with a valid JWT format XXX.YYY.ZZZ 1158 | // try { // Could be a valid JWT or an access token with the same format 1159 | // const base64Url = token.split('.')[1]; 1160 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 1161 | // const exp = JSON.parse(window.atob(base64)).exp; 1162 | // if (typeof exp === 'number') {// JWT with an optonal expiration claims 1163 | // return Math.round(new Date().getTime() / 1000) < exp; 1164 | // } 1165 | // } catch (e) { 1166 | // return true;// Pass: Non-JWT token that looks like JWT 1167 | // } 1168 | // } 1169 | // return true;// Pass: All other tokens 1170 | // } 1171 | // return false 1172 | // } 1173 | 1174 | /** 1175 | * Get token if user is authenticated 1176 | * @return {String} Authentication token 1177 | */ 1178 | VueSocialauth.prototype.getToken = function getToken () { 1179 | return this.storage.getItem(this.tokenName) 1180 | }; 1181 | 1182 | /** 1183 | * Set new authentication token 1184 | * @param {String|Object} token 1185 | */ 1186 | // setToken(response) { 1187 | // if (response[this.options.responseDataKey]) { 1188 | // response = response[this.options.responseDataKey]; 1189 | // } 1190 | 1191 | // let token; 1192 | // if (response.access_token) { 1193 | // if (isObject(response.access_token) && isObject(response.access_token[this.options.responseDataKey])) { 1194 | // response = response.access_token 1195 | // } else if (isString(response.access_token)) { 1196 | // token = response.access_token 1197 | // } 1198 | // } 1199 | 1200 | // if (!token && response) { 1201 | // token = response[this.options.tokenName] 1202 | // } 1203 | 1204 | // if (token) { 1205 | // this.storage.setItem(this.tokenName, token) 1206 | // } 1207 | // } 1208 | 1209 | // getPayload() { 1210 | // const token = this.storage.getItem(this.tokenName); 1211 | 1212 | // if (token && token.split('.').length === 3) { 1213 | // try { 1214 | // const base64Url = token.split('.')[1]; 1215 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 1216 | // return JSON.parse(decodeBase64(base64)); 1217 | // } catch (e) {} 1218 | // } 1219 | // } 1220 | 1221 | /** 1222 | * Login user using email and password 1223 | * @param{Object} user User data 1224 | * @param{Object} requestOptions Request options 1225 | * @return {Promise} Request promise 1226 | */ 1227 | // login(user, requestOptions) { 1228 | // requestOptions = requestOptions || {} 1229 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.loginUrl) 1230 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 1231 | // requestOptions.method = requestOptions.method || 'POST' 1232 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1233 | 1234 | // return this.$http(requestOptions).then((response) => { 1235 | // this.setToken(response) 1236 | // return response 1237 | // }) 1238 | // } 1239 | 1240 | /** 1241 | * Register new user 1242 | * @param{Object} user User data 1243 | * @param{Object} requestOptions Request options 1244 | * @return {Promise} Request promise 1245 | */ 1246 | // register(user, requestOptions) { 1247 | // requestOptions = requestOptions || {} 1248 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.registerUrl) 1249 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 1250 | // requestOptions.method = requestOptions.method || 'POST' 1251 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1252 | 1253 | // return this.$http(requestOptions).then((response) => { 1254 | // this.setToken(response) 1255 | // return response 1256 | // }) 1257 | // } 1258 | 1259 | /** 1260 | * Logout current user 1261 | * @param{Object} requestOptionsLogout request options object 1262 | * @return {Promise} Request promise 1263 | */ 1264 | // logout(requestOptions) { 1265 | // if (!this.isAuthenticated()) { 1266 | // return Promise.reject(new Error('There is no currently authenticated user')) 1267 | // } 1268 | 1269 | // requestOptions = requestOptions || {} 1270 | // requestOptions.url = requestOptions.logoutUrl || this.options.logoutUrl 1271 | 1272 | // if (requestOptions.url) { 1273 | // requestOptions.method = requestOptions.method || 'POST' 1274 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1275 | 1276 | // return this.$http(requestOptions).then((response) => { 1277 | // this.storage.removeItem(this.tokenName) 1278 | // }) 1279 | // } else { 1280 | // this.storage.removeItem(this.tokenName) 1281 | // return Promise.resolve(); 1282 | // } 1283 | // } 1284 | 1285 | /** 1286 | * Authenticate user using authentication provider 1287 | * 1288 | * @param{String} provider Provider name 1289 | * @param{Object} userData User data 1290 | * @param{Object} requestOptions Request options 1291 | * @return {Promise} Request promise 1292 | */ 1293 | VueSocialauth.prototype.authenticate = function authenticate (provider, userData, requestOptions) { 1294 | var this$1 = this; 1295 | 1296 | return new Promise$1(function (resolve, reject) { 1297 | var providerConfig = this$1.options.providers[provider]; 1298 | if (!providerConfig) { 1299 | return reject(new Error('Unknown provider')) 1300 | } 1301 | 1302 | var providerInstance; 1303 | switch (providerConfig.oauthType) { 1304 | case '1.0': 1305 | providerInstance = new OAuth(this$1.$http, this$1.storage, providerConfig, this$1.options); 1306 | break 1307 | case '2.0': 1308 | providerInstance = new OAuth2(this$1.$http, this$1.storage, providerConfig, this$1.options); 1309 | break 1310 | default: 1311 | return reject(new Error('Invalid OAuth type')) 1312 | break 1313 | } 1314 | 1315 | return providerInstance.init(userData).then(function (response) { 1316 | return resolve(response) 1317 | 1318 | }).catch(function (err) { return reject(err); }) 1319 | }) 1320 | }; 1321 | 1322 | /** 1323 | * VueSocialauth plugin 1324 | * @param {Object} Vue 1325 | * @param {Object} options 1326 | */ 1327 | function plugin(Vue, options) { 1328 | if (plugin.installed) { 1329 | return 1330 | } 1331 | plugin.installed = true; 1332 | 1333 | var property = options.property || '$auth'; 1334 | 1335 | var vueAuthInstance = null; 1336 | Object.defineProperties(Vue.prototype, ( obj = {}, obj[property] = { 1337 | get: function get() { 1338 | if (!vueAuthInstance) { 1339 | // Request handler library not found, throw error 1340 | // verified vue or nuxt instance 1341 | if (this.$axios) { 1342 | vueAuthInstance = new VueSocialauth(this.$axios, options); 1343 | } else if (this.$http) { 1344 | vueAuthInstance = new VueSocialauth(this.$http, options); 1345 | } else { 1346 | throw new Error('Request handler instance not found') 1347 | } 1348 | } 1349 | return vueAuthInstance 1350 | } 1351 | }, obj )); 1352 | var obj; 1353 | } 1354 | 1355 | /** 1356 | * External factory helper for ES5 and CommonJS 1357 | * @param {Object} $http Instance of request handling library 1358 | * @param {Object} options Configuration object 1359 | * @return {VueSocialauth} VueSocialauth instance 1360 | */ 1361 | plugin.factory = function ($http, options) { 1362 | return new VueSocialauth($http, options) 1363 | }; 1364 | 1365 | export default plugin; 1366 | export { VueSocialauth }; 1367 | -------------------------------------------------------------------------------- /dist/vue-social-auth.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-social-auth v0.0.1 3 | * https://github.com/diadal/vue-social-auth 4 | * Released under the MIT License. 5 | */ 6 | 7 | (function (global, factory) { 8 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 9 | typeof define === 'function' && define.amd ? define(factory) : 10 | (global.VueSocialauth = factory()); 11 | }(this, (function () { 'use strict'; 12 | 13 | if (typeof Object.assign != 'function') { 14 | Object.assign = function(target, varArgs) { 15 | 'use strict'; 16 | var arguments$1 = arguments; 17 | 18 | if (target == null) { 19 | throw new TypeError('Cannot convert undefined or null to object'); 20 | } 21 | 22 | var to = Object(target); 23 | 24 | for (var index = 1; index < arguments.length; index++) { 25 | var nextSource = arguments$1[index]; 26 | 27 | if (nextSource != null) { // Skip over if undefined or null 28 | for (var nextKey in nextSource) { 29 | // Avoid bugs when hasOwnProperty is shadowed 30 | if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 31 | to[nextKey] = nextSource[nextKey]; 32 | } 33 | } 34 | } 35 | } 36 | return to; 37 | }; 38 | } 39 | 40 | function camelCase(name) { 41 | return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { 42 | return offset ? letter.toUpperCase() : letter; 43 | }); 44 | } 45 | 46 | function isUndefined(value) { 47 | return typeof value === 'undefined' 48 | } 49 | 50 | 51 | 52 | 53 | 54 | function isString(value) { 55 | return typeof value === 'string' 56 | } 57 | 58 | 59 | 60 | function isFunction(value) { 61 | return typeof value === 'function' 62 | } 63 | 64 | function objectExtend(a, b) { 65 | 66 | // Don't touch 'null' or 'undefined' objects. 67 | if (a == null || b == null) { 68 | return a; 69 | } 70 | 71 | Object.keys(b).forEach(function (key) { 72 | if (Object.prototype.toString.call(b[key]) == '[object Object]') { 73 | if (Object.prototype.toString.call(a[key]) != '[object Object]') { 74 | a[key] = b[key]; 75 | } else { 76 | a[key] = objectExtend(a[key], b[key]); 77 | } 78 | } else { 79 | a[key] = b[key]; 80 | } 81 | }); 82 | 83 | return a; 84 | } 85 | 86 | /** 87 | * Assemble url from two segments 88 | * 89 | * @author Sahat Yalkabov 90 | * @copyright Method taken from https://github.com/sahat/satellizer 91 | * 92 | * @param {String} baseUrl Base url 93 | * @param {String} url URI 94 | * @return {String} 95 | */ 96 | function joinUrl(baseUrl, url) { 97 | if (/^(?:[a-z]+:)?\/\//i.test(url)) { 98 | return url; 99 | } 100 | var joined = [baseUrl, url].join('/'); 101 | var normalize = function (str) { 102 | return str 103 | .replace(/[\/]+/g, '/') 104 | .replace(/\/\?/g, '?') 105 | .replace(/\/\#/g, '#') 106 | .replace(/\:\//g, '://'); 107 | }; 108 | return normalize(joined); 109 | } 110 | 111 | /** 112 | * Get full path based on current location 113 | * 114 | * @author Sahat Yalkabov 115 | * @copyright Method taken from https://github.com/sahat/satellizer 116 | * 117 | * @param {Location} location 118 | * @return {String} 119 | */ 120 | function getFullUrlPath(location) { 121 | var isHttps = location.protocol === 'https:'; 122 | return location.protocol + '//' + location.hostname + 123 | ':' + (location.port || (isHttps ? '443' : '80')) + 124 | (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); 125 | } 126 | 127 | /** 128 | * Parse query string variables 129 | * 130 | * @author Sahat Yalkabov 131 | * @copyright Method taken from https://github.com/sahat/satellizer 132 | * 133 | * @param {String} Query string 134 | * @return {String} 135 | */ 136 | function parseQueryString(str) { 137 | var obj = {}; 138 | var key; 139 | var value; 140 | (str || '').split('&').forEach(function (keyValue) { 141 | if (keyValue) { 142 | value = keyValue.split('='); 143 | key = decodeURIComponent(value[0]); 144 | obj[key] = (!!value[1]) ? decodeURIComponent(value[1]) : true; 145 | } 146 | }); 147 | return obj; 148 | } 149 | 150 | /** 151 | * Decode base64 string 152 | * @author Sahat Yalkabov 153 | * @copyright Method taken from https://github.com/sahat/satellizer 154 | * 155 | * @param {String} str base64 encoded string 156 | * @return {Object} 157 | */ 158 | 159 | 160 | function parseCookies(str) { 161 | if (str.length === 0) { return {}; } 162 | var parsed = {}; 163 | var pattern = new RegExp('\\s*;\\s*'); 164 | str.split(pattern).forEach(function (i) { 165 | var ref = i.split('='); 166 | var encodedKey = ref[0]; 167 | var encodedValue = ref[1]; 168 | var key = decodeURIComponent(encodedKey); 169 | var value = decodeURIComponent(encodedValue); 170 | parsed[key] = value; 171 | }); 172 | return parsed; 173 | } 174 | 175 | function formatOptions(options) { 176 | var path = options.path; 177 | var domain = options.domain; 178 | var expires = options.expires; 179 | var secure = options.secure; 180 | return [ 181 | typeof path === 'undefined' || path === null 182 | ? '' : ';path=' + path, 183 | typeof domain === 'undefined' || domain === null 184 | ? '' : ';domain=' + domain, 185 | typeof expires === 'undefined' || expires === null 186 | ? '' : ';expires=' + expires.toUTCString(), 187 | typeof secure === 'undefined' || secure === null || secure === false 188 | ? '' : ';secure' 189 | ].join(''); 190 | } 191 | 192 | function formatCookie(key, value, options) { 193 | return [ 194 | encodeURIComponent(key), 195 | '=', 196 | encodeURIComponent(value), 197 | formatOptions(options) 198 | ].join(''); 199 | } 200 | 201 | // Store setTimeout reference so promise-polyfill will be unaffected by 202 | // other code modifying setTimeout (like sinon.useFakeTimers()) 203 | var setTimeoutFunc = setTimeout; 204 | 205 | function noop() {} 206 | 207 | // Polyfill for Function.prototype.bind 208 | function bind(fn, thisArg) { 209 | return function () { 210 | fn.apply(thisArg, arguments); 211 | }; 212 | } 213 | 214 | function Promise$1(fn) { 215 | if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } 216 | if (typeof fn !== 'function') { throw new TypeError('not a function'); } 217 | this._state = 0; 218 | this._handled = false; 219 | this._value = undefined; 220 | this._deferreds = []; 221 | 222 | doResolve(fn, this); 223 | } 224 | 225 | function handle(self, deferred) { 226 | while (self._state === 3) { 227 | self = self._value; 228 | } 229 | if (self._state === 0) { 230 | self._deferreds.push(deferred); 231 | return; 232 | } 233 | self._handled = true; 234 | Promise$1._immediateFn(function () { 235 | var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 236 | if (cb === null) { 237 | (self._state === 1 ? resolve : reject)(deferred.promise, self._value); 238 | return; 239 | } 240 | var ret; 241 | try { 242 | ret = cb(self._value); 243 | } catch (e) { 244 | reject(deferred.promise, e); 245 | return; 246 | } 247 | resolve(deferred.promise, ret); 248 | }); 249 | } 250 | 251 | function resolve(self, newValue) { 252 | try { 253 | // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 254 | if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.'); } 255 | if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { 256 | var then = newValue.then; 257 | if (newValue instanceof Promise$1) { 258 | self._state = 3; 259 | self._value = newValue; 260 | finale(self); 261 | return; 262 | } else if (typeof then === 'function') { 263 | doResolve(bind(then, newValue), self); 264 | return; 265 | } 266 | } 267 | self._state = 1; 268 | self._value = newValue; 269 | finale(self); 270 | } catch (e) { 271 | reject(self, e); 272 | } 273 | } 274 | 275 | function reject(self, newValue) { 276 | self._state = 2; 277 | self._value = newValue; 278 | finale(self); 279 | } 280 | 281 | function finale(self) { 282 | if (self._state === 2 && self._deferreds.length === 0) { 283 | Promise$1._immediateFn(function() { 284 | if (!self._handled) { 285 | Promise$1._unhandledRejectionFn(self._value); 286 | } 287 | }); 288 | } 289 | 290 | for (var i = 0, len = self._deferreds.length; i < len; i++) { 291 | handle(self, self._deferreds[i]); 292 | } 293 | self._deferreds = null; 294 | } 295 | 296 | function Handler(onFulfilled, onRejected, promise) { 297 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 298 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 299 | this.promise = promise; 300 | } 301 | 302 | /** 303 | * Take a potentially misbehaving resolver function and make sure 304 | * onFulfilled and onRejected are only called once. 305 | * 306 | * Makes no guarantees about asynchrony. 307 | */ 308 | function doResolve(fn, self) { 309 | var done = false; 310 | try { 311 | fn(function (value) { 312 | if (done) { return; } 313 | done = true; 314 | resolve(self, value); 315 | }, function (reason) { 316 | if (done) { return; } 317 | done = true; 318 | reject(self, reason); 319 | }); 320 | } catch (ex) { 321 | if (done) { return; } 322 | done = true; 323 | reject(self, ex); 324 | } 325 | } 326 | 327 | Promise$1.prototype['catch'] = function (onRejected) { 328 | return this.then(null, onRejected); 329 | }; 330 | 331 | Promise$1.prototype.then = function (onFulfilled, onRejected) { 332 | var prom = new (this.constructor)(noop); 333 | 334 | handle(this, new Handler(onFulfilled, onRejected, prom)); 335 | return prom; 336 | }; 337 | 338 | Promise$1.all = function (arr) { 339 | var args = Array.prototype.slice.call(arr); 340 | 341 | return new Promise$1(function (resolve, reject) { 342 | if (args.length === 0) { return resolve([]); } 343 | var remaining = args.length; 344 | 345 | function res(i, val) { 346 | try { 347 | if (val && (typeof val === 'object' || typeof val === 'function')) { 348 | var then = val.then; 349 | if (typeof then === 'function') { 350 | then.call(val, function (val) { 351 | res(i, val); 352 | }, reject); 353 | return; 354 | } 355 | } 356 | args[i] = val; 357 | if (--remaining === 0) { 358 | resolve(args); 359 | } 360 | } catch (ex) { 361 | reject(ex); 362 | } 363 | } 364 | 365 | for (var i = 0; i < args.length; i++) { 366 | res(i, args[i]); 367 | } 368 | }); 369 | }; 370 | 371 | Promise$1.resolve = function (value) { 372 | if (value && typeof value === 'object' && value.constructor === Promise$1) { 373 | return value; 374 | } 375 | 376 | return new Promise$1(function (resolve) { 377 | resolve(value); 378 | }); 379 | }; 380 | 381 | Promise$1.reject = function (value) { 382 | return new Promise$1(function (resolve, reject) { 383 | reject(value); 384 | }); 385 | }; 386 | 387 | Promise$1.race = function (values) { 388 | return new Promise$1(function (resolve, reject) { 389 | for (var i = 0, len = values.length; i < len; i++) { 390 | values[i].then(resolve, reject); 391 | } 392 | }); 393 | }; 394 | 395 | // Use polyfill for setImmediate for performance gains 396 | Promise$1._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) || 397 | function (fn) { 398 | setTimeoutFunc(fn, 0); 399 | }; 400 | 401 | Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { 402 | if (typeof console !== 'undefined' && console) { 403 | console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console 404 | } 405 | }; 406 | 407 | /** 408 | * Set the immediate function to execute callbacks 409 | * @param fn {function} Function to execute 410 | * @deprecated 411 | */ 412 | Promise$1._setImmediateFn = function _setImmediateFn(fn) { 413 | Promise$1._immediateFn = fn; 414 | }; 415 | 416 | /** 417 | * Change the function to execute on unhandled rejection 418 | * @param {function} fn Function to execute on unhandled rejection 419 | * @deprecated 420 | */ 421 | Promise$1._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) { 422 | Promise$1._unhandledRejectionFn = fn; 423 | }; 424 | 425 | /** 426 | * Default configuration 427 | */ 428 | var defaultOptions = { 429 | baseUrl: null, 430 | tokenName: 'token', 431 | tokenPrefix: 'vueauth', 432 | tokenHeader: 'Authorization', 433 | tokenType: 'Bearer', 434 | loginUrl: '/auth/login', 435 | registerUrl: '/auth/register', 436 | logoutUrl: null, 437 | storageType: 'localStorage', 438 | storageNamespace: 'vue-social-auth', 439 | cookieStorage: { 440 | domain: window.location.hostname, 441 | path: '/', 442 | secure: false 443 | }, 444 | requestDataKey: 'data', 445 | responseDataKey: 'data', 446 | 447 | /** 448 | * Default request interceptor for Axios library 449 | * @context {VueSocialauth} 450 | */ 451 | bindRequestInterceptor: function ($auth) { 452 | 453 | var tokenHeader = $auth.options.tokenHeader; 454 | 455 | $auth.$http.interceptors.request.use(function (config) { 456 | delete config.headers[tokenHeader]; 457 | return config 458 | }); 459 | 460 | 461 | }, 462 | 463 | /** 464 | * Default response interceptor for Axios library 465 | * @contect {VueSocialauth} 466 | */ 467 | bindResponseInterceptor: function ($auth) { 468 | $auth.$http.interceptors.response.use(function (response) { 469 | return response 470 | }); 471 | }, 472 | 473 | providers: { 474 | facebook: { 475 | name: 'facebook', 476 | url: '/auth/facebook', 477 | authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', 478 | redirectUri: window.location.origin + '/', 479 | requiredUrlParams: ['display', 'scope'], 480 | scope: ['email'], 481 | scopeDelimiter: ',', 482 | display: 'popup', 483 | oauthType: '2.0', 484 | popupOptions: { width: 580, height: 400 } 485 | }, 486 | 487 | google: { 488 | name: 'google', 489 | url: '/auth/google', 490 | authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', 491 | redirectUri: window.location.origin, 492 | requiredUrlParams: ['scope'], 493 | optionalUrlParams: ['display'], 494 | scope: ['profile', 'email'], 495 | scopePrefix: 'openid', 496 | scopeDelimiter: ' ', 497 | display: 'popup', 498 | oauthType: '2.0', 499 | popupOptions: { width: 452, height: 633 } 500 | }, 501 | 502 | github: { 503 | name: 'github', 504 | url: '/auth/github', 505 | authorizationEndpoint: 'https://github.com/login/oauth/authorize', 506 | redirectUri: window.location.origin, 507 | optionalUrlParams: ['scope'], 508 | scope: ['user:email'], 509 | scopeDelimiter: ' ', 510 | oauthType: '2.0', 511 | popupOptions: { width: 1020, height: 618 } 512 | }, 513 | 514 | instagram: { 515 | name: 'instagram', 516 | url: '/auth/instagram', 517 | authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', 518 | redirectUri: window.location.origin, 519 | requiredUrlParams: ['scope'], 520 | scope: ['basic'], 521 | scopeDelimiter: '+', 522 | oauthType: '2.0', 523 | popupOptions: { width: null, height: null } 524 | }, 525 | 526 | twitter: { 527 | name: 'twitter', 528 | url: '/auth/twitter', 529 | authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', 530 | redirectUri: window.location.origin, 531 | oauthType: '1.0', 532 | popupOptions: { width: 495, height: 645 } 533 | }, 534 | 535 | bitbucket: { 536 | name: 'bitbucket', 537 | url: '/auth/bitbucket', 538 | authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', 539 | redirectUri: window.location.origin + '/', 540 | optionalUrlParams: ['scope'], 541 | scope: ['email'], 542 | scopeDelimiter: ' ', 543 | oauthType: '2.0', 544 | popupOptions: { width: 1020, height: 618 } 545 | }, 546 | 547 | linkedin: { 548 | name: 'linkedin', 549 | url: '/auth/linkedin', 550 | authorizationEndpoint: 'https://www.linkedin.com/oauth/v2/authorization', 551 | redirectUri: window.location.origin, 552 | requiredUrlParams: ['state','scope'], 553 | scope: ['r_liteprofile','r_emailaddress'], 554 | scopeDelimiter: ' ', 555 | state: 'STATE', 556 | oauthType: '2.0', 557 | popupOptions: { width: 527, height: 582 } 558 | }, 559 | 560 | vkontakte: { 561 | name: 'vkontakte', 562 | url: '/auth/vkontakte', 563 | authorizationEndpoint: 'https://oauth.vk.com/authorize', 564 | redirectUri: window.location.origin + '/', 565 | requiredUrlParams: ['scope'], 566 | scope: ['email'], 567 | scopeDelimiter: ',', 568 | display: 'popup', 569 | oauthType: '2.0', 570 | popupOptions: { width: 580, height: 400 } 571 | }, 572 | 573 | live: { 574 | name: 'live', 575 | url: '/auth/live', 576 | authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', 577 | redirectUri: window.location.origin, 578 | requiredUrlParams: ['display', 'scope'], 579 | scope: ['wl.emails'], 580 | scopeDelimiter: ' ', 581 | display: 'popup', 582 | oauthType: '2.0', 583 | popupOptions: { width: 500, height: 560 } 584 | }, 585 | 586 | oauth1: { 587 | name: null, 588 | url: '/auth/oauth1', 589 | authorizationEndpoint: null, 590 | redirectUri: window.location.origin, 591 | oauthType: '1.0', 592 | popupOptions: null 593 | }, 594 | 595 | oauth2: { 596 | name: null, 597 | url: '/auth/oauth2', 598 | clientId: null, 599 | redirectUri: window.location.origin, 600 | authorizationEndpoint: null, 601 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 602 | requiredUrlParams: null, 603 | optionalUrlParams: null, 604 | scope: null, 605 | scopePrefix: null, 606 | scopeDelimiter: null, 607 | state: null, 608 | oauthType: '2.0', 609 | popupOptions: null, 610 | responseType: 'code', 611 | responseParams: { 612 | code: 'code', 613 | clientId: 'clientId', 614 | redirectUri: 'redirectUri' 615 | } 616 | } 617 | } 618 | }; 619 | 620 | var CookieStorage = function CookieStorage(defaultOptions) { 621 | this._defaultOptions = objectExtend({ 622 | domain: window.location.hostname, 623 | expires: null, 624 | path: '/', 625 | secure: false 626 | }, defaultOptions); 627 | }; 628 | 629 | CookieStorage.prototype.setItem = function setItem (key, value) { 630 | var options = objectExtend({}, this._defaultOptions); 631 | var cookie = formatCookie(key, value, options); 632 | this._setCookie(cookie); 633 | }; 634 | 635 | CookieStorage.prototype.getItem = function getItem (key) { 636 | var cookies = parseCookies(this._getCookie()); 637 | return cookies.hasOwnProperty(key) ? cookies[key] : null; 638 | }; 639 | 640 | CookieStorage.prototype.removeItem = function removeItem (key) { 641 | var value = ''; 642 | var defaultOptions = objectExtend({}, this._defaultOptions); 643 | var options = objectExtend(defaultOptions, { 644 | expires: new Date(0) 645 | }); 646 | var cookie = formatCookie(key, value, options); 647 | this._setCookie(cookie); 648 | }; 649 | 650 | CookieStorage.prototype._getCookie = function _getCookie () { 651 | return typeof document === 'undefined' 652 | ? '' : typeof document.cookie === 'undefined' 653 | ? '' : document.cookie; 654 | }; 655 | 656 | CookieStorage.prototype._setCookie = function _setCookie (cookie) { 657 | document.cookie = cookie; 658 | }; 659 | 660 | var LocalStorage = function LocalStorage(namespace) { 661 | this.namespace = namespace || null; 662 | }; 663 | 664 | LocalStorage.prototype.setItem = function setItem (key, value) { 665 | window.localStorage.setItem(this._getStorageKey(key), value); 666 | }; 667 | 668 | LocalStorage.prototype.getItem = function getItem (key) { 669 | return window.localStorage.getItem(this._getStorageKey(key)) 670 | }; 671 | 672 | LocalStorage.prototype.removeItem = function removeItem (key) { 673 | window.localStorage.removeItem(this._getStorageKey(key)); 674 | }; 675 | 676 | LocalStorage.prototype._getStorageKey = function _getStorageKey (key) { 677 | if (this.namespace) { 678 | return [this.namespace, key].join('.') 679 | } 680 | return key; 681 | }; 682 | 683 | var MemoryStorage = function MemoryStorage(namespace) { 684 | this.namespace = namespace || null; 685 | this._storage = {}; 686 | }; 687 | 688 | MemoryStorage.prototype.setItem = function setItem (key, value) { 689 | this._storage[this._getStorageKey(key)] = value; 690 | }; 691 | 692 | MemoryStorage.prototype.getItem = function getItem (key) { 693 | return this._storage[this._getStorageKey(key)] 694 | }; 695 | 696 | MemoryStorage.prototype.removeItem = function removeItem (key) { 697 | delete this._storage[this._getStorageKey(key)]; 698 | }; 699 | 700 | MemoryStorage.prototype._getStorageKey = function _getStorageKey (key) { 701 | if (this.namespace) { 702 | return [this.namespace, key].join('.') 703 | } 704 | return key; 705 | }; 706 | 707 | var LocalStorage$2 = function LocalStorage(namespace) { 708 | this.namespace = namespace || null; 709 | }; 710 | 711 | LocalStorage$2.prototype.setItem = function setItem (key, value) { 712 | window.sessionStorage.setItem(this._getStorageKey(key), value); 713 | }; 714 | 715 | LocalStorage$2.prototype.getItem = function getItem (key) { 716 | return window.sessionStorage.getItem(this._getStorageKey(key)) 717 | }; 718 | 719 | LocalStorage$2.prototype.removeItem = function removeItem (key) { 720 | window.sessionStorage.removeItem(this._getStorageKey(key)); 721 | }; 722 | 723 | LocalStorage$2.prototype._getStorageKey = function _getStorageKey (key) { 724 | if (this.namespace) { 725 | return [this.namespace, key].join('.') 726 | } 727 | return key; 728 | }; 729 | 730 | function StorageFactory(options) { 731 | switch (options.storageType) { 732 | case 'localStorage': 733 | try { 734 | window.localStorage.setItem('testKey', 'test'); 735 | window.localStorage.removeItem('testKey'); 736 | return new LocalStorage(options.storageNamespace) 737 | } catch(e) {} 738 | 739 | case 'sessionStorage': 740 | try { 741 | window.sessionStorage.setItem('testKey', 'test'); 742 | window.sessionStorage.removeItem('testKey'); 743 | return new LocalStorage$2(options.storageNamespace) 744 | } catch (e) {} 745 | 746 | case 'cookieStorage': 747 | return new CookieStorage(options.cookieStorage); 748 | 749 | case 'memoryStorage': 750 | default: 751 | return new MemoryStorage(options.storageNamespace) 752 | break; 753 | } 754 | } 755 | 756 | /** 757 | * OAuth2 popup management class 758 | * 759 | * @author Sahat Yalkabov 760 | * @copyright Class mostly taken from https://github.com/sahat/satellizer 761 | * and adjusted to fit vue-social-auth library 762 | */ 763 | var OAuthPopup = function OAuthPopup(url, name, popupOptions) { 764 | this.popup = null; 765 | this.url = url; 766 | this.name = name; 767 | this.popupOptions = popupOptions; 768 | }; 769 | 770 | OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { 771 | try { 772 | this.popup = window.open(this.url, this.name, this._stringifyOptions()); 773 | if (this.popup && this.popup.focus) { 774 | this.popup.focus(); 775 | } 776 | 777 | if (skipPooling) { 778 | return Promise$1.resolve() 779 | } else { 780 | return this.pooling(redirectUri) 781 | } 782 | } catch(e) { 783 | return Promise$1.reject(new Error('OAuth popup error occurred')) 784 | } 785 | }; 786 | 787 | OAuthPopup.prototype.pooling = function pooling (redirectUri) { 788 | var this$1 = this; 789 | 790 | return new Promise$1(function (resolve, reject) { 791 | var redirectUriParser = document.createElement('a'); 792 | redirectUriParser.href = redirectUri; 793 | var redirectUriPath = getFullUrlPath(redirectUriParser); 794 | 795 | var poolingInterval = setInterval(function () { 796 | if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { 797 | clearInterval(poolingInterval); 798 | poolingInterval = null; 799 | reject(new Error('Auth popup window closed')); 800 | } 801 | 802 | try { 803 | var popupWindowPath = getFullUrlPath(this$1.popup.location); 804 | 805 | if (popupWindowPath === redirectUriPath) { 806 | if (this$1.popup.location.search || this$1.popup.location.hash) { 807 | var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); 808 | var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); 809 | var params = objectExtend({}, query); 810 | params = objectExtend(params, hash); 811 | 812 | if (params.error) { 813 | reject(new Error(params.error)); 814 | } else { 815 | resolve(params); 816 | } 817 | } else { 818 | reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); 819 | } 820 | 821 | clearInterval(poolingInterval); 822 | poolingInterval = null; 823 | this$1.popup.close(); 824 | } 825 | } catch(e) { 826 | // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. 827 | } 828 | }, 250); 829 | }) 830 | }; 831 | 832 | OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { 833 | var this$1 = this; 834 | 835 | var options = []; 836 | for (var optionKey in this$1.popupOptions) { 837 | if (!isUndefined(this$1.popupOptions[optionKey])) { 838 | options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); 839 | } 840 | } 841 | return options.join(',') 842 | }; 843 | 844 | var defaultProviderConfig = { 845 | name: null, 846 | url: null, 847 | authorizationEndpoint: null, 848 | scope: null, 849 | scopePrefix: null, 850 | scopeDelimiter: null, 851 | redirectUri: null, 852 | requiredUrlParams: null, 853 | defaultUrlParams: null, 854 | oauthType: '1.0', 855 | popupOptions: {} 856 | }; 857 | 858 | var OAuth = function OAuth($http, storage, providerConfig, options) { 859 | this.$http = $http; 860 | this.storage = storage; 861 | this.providerConfig = objectExtend({}, defaultProviderConfig); 862 | this.providerConfig = objectExtend(this.providerConfig, providerConfig); 863 | this.options = options; 864 | }; 865 | 866 | /** 867 | * Initialize OAuth1 process 868 | * @param{Object} userData User data 869 | * @return {Promise} 870 | */ 871 | OAuth.prototype.init = function init (userData) { 872 | var this$1 = this; 873 | 874 | this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); 875 | 876 | if (window && !window['cordova']) { 877 | this.oauthPopup.open(this.providerConfig.redirectUri, true); 878 | } 879 | 880 | return this.getRequestToken().then(function (response) { 881 | return this$1.openPopup(response).then(function (popupResponse) { 882 | return this$1.exchangeForToken(popupResponse, userData) 883 | }) 884 | }) 885 | }; 886 | 887 | /** 888 | * Get OAuth1 request token 889 | * @return {Promise} 890 | */ 891 | OAuth.prototype.getRequestToken = function getRequestToken () { 892 | var requestOptions = {}; 893 | requestOptions.method = 'POST'; 894 | requestOptions[this.options.requestDataKey] = objectExtend({}, this.providerConfig); 895 | requestOptions.withCredentials = this.options.withCredentials; 896 | if (this.options.baseUrl) { 897 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url); 898 | } else { 899 | requestOptions.url = this.providerConfig.url; 900 | } 901 | 902 | return this.$http(requestOptions) 903 | }; 904 | 905 | /** 906 | * Open OAuth1 popup 907 | * @param{Object} response Response object containing request token 908 | * @return {Promise} 909 | */ 910 | OAuth.prototype.openPopup = function openPopup (response) { 911 | var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); 912 | 913 | this.oauthPopup.popup.location = url; 914 | if (window && window['cordova']) { 915 | return this.oauthPopup.open(this.providerConfig.redirectUri) 916 | } else { 917 | return this.oauthPopup.pooling(this.providerConfig.redirectUri) 918 | } 919 | }; 920 | 921 | /** 922 | * Exchange token and token verifier for access token 923 | * @param{Object} oauth OAuth data containing token and token verifier 924 | * @param{Object} userData User data 925 | * @return {Promise} 926 | */ 927 | OAuth.prototype.exchangeForToken = function exchangeForToken (oauth, userData) { 928 | var payload = objectExtend({}, userData); 929 | payload = objectExtend(payload, oauth); 930 | var requestOptions = {}; 931 | requestOptions.method = 'POST'; 932 | requestOptions[this.options.requestDataKey] = payload; 933 | requestOptions.withCredentials = this.options.withCredentials; 934 | if (this.options.baseUrl) { 935 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url); 936 | } else { 937 | requestOptions.url = this.providerConfig.url; 938 | } 939 | return this.$http(requestOptions) 940 | }; 941 | 942 | OAuth.prototype.buildQueryString = function buildQueryString (params) { 943 | var parsedParams = []; 944 | for (var key in params) { 945 | var value = params[key]; 946 | parsedParams.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); 947 | } 948 | return parsedParams.join('&'); 949 | }; 950 | 951 | /** 952 | * Default provider configuration 953 | * @type {Object} 954 | */ 955 | var defaultProviderConfig$1 = { 956 | name: null, 957 | url: null, 958 | clientId: null, 959 | authorizationEndpoint: null, 960 | redirectUri: null, 961 | scope: null, 962 | scopePrefix: null, 963 | scopeDelimiter: null, 964 | state: null, 965 | requiredUrlParams: null, 966 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 967 | responseType: 'code', 968 | responseParams: { 969 | code: 'code', 970 | clientId: 'clientId', 971 | redirectUri: 'redirectUri' 972 | }, 973 | oauthType: '2.0', 974 | popupOptions: {} 975 | }; 976 | 977 | var OAuth2 = function OAuth2($http, storage, providerConfig, options) { 978 | this.$http = $http; 979 | this.storage = storage; 980 | this.providerConfig = objectExtend({}, defaultProviderConfig$1); 981 | this.providerConfig = objectExtend(this.providerConfig, providerConfig); 982 | this.options = options; 983 | }; 984 | 985 | OAuth2.prototype.init = function init (userData) { 986 | var this$1 = this; 987 | 988 | var stateName = this.providerConfig.name + '_state'; 989 | if (isFunction(this.providerConfig.state)) { 990 | this.storage.setItem(stateName, this.providerConfig.state()); 991 | } else if (isString(this.providerConfig.state)) { 992 | this.storage.setItem(stateName, this.providerConfig.state); 993 | } 994 | 995 | var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); 996 | 997 | this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); 998 | 999 | return new Promise(function (resolve, reject) { 1000 | this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { 1001 | if (this$1.providerConfig.responseType === 'code' || !this$1.providerConfig.url) { 1002 | return resolve(response) 1003 | } 1004 | 1005 | if (response.state && response.state !== this$1.storage.getItem(stateName)) { 1006 | return reject(new Error('State parameter value does not match original OAuth request state value')) 1007 | } 1008 | 1009 | resolve(this$1.exchangeForToken(response, userData)); 1010 | }).catch(function (err) { 1011 | reject(err); 1012 | }); 1013 | }) 1014 | }; 1015 | 1016 | /** 1017 | * Exchange temporary oauth data for access token 1018 | * @author Sahat Yalkabov 1019 | * @copyright Method taken from https://github.com/sahat/satellizer 1020 | * 1021 | * @param{[type]} oauth [description] 1022 | * @param{[type]} userData [description] 1023 | * @return {[type]} [description] 1024 | */ 1025 | OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) { 1026 | var this$1 = this; 1027 | 1028 | var payload = objectExtend({}, userData); 1029 | 1030 | for (var key in defaultProviderConfig$1.responseParams) { 1031 | var value = defaultProviderConfig$1[key]; 1032 | 1033 | switch(key) { 1034 | case 'code': 1035 | payload[key] = oauth.code; 1036 | break 1037 | case 'clientId': 1038 | payload[key] = this$1.providerConfig.clientId; 1039 | break 1040 | case 'redirectUri': 1041 | payload[key] = this$1.providerConfig.redirectUri; 1042 | break 1043 | default: 1044 | payload[key] = oauth[key]; 1045 | } 1046 | } 1047 | 1048 | if (oauth.state) { 1049 | payload.state = oauth.state; 1050 | } 1051 | 1052 | var exchangeTokenUrl; 1053 | if (this.options.baseUrl) { 1054 | exchangeTokenUrl = joinUrl(this.options.baseUrl, this.providerConfig.url); 1055 | } else { 1056 | exchangeTokenUrl = this.providerConfig.url; 1057 | } 1058 | 1059 | return this.$http.post(exchangeTokenUrl, payload, { 1060 | withCredentials: this.options.withCredentials 1061 | }) 1062 | }; 1063 | 1064 | /** 1065 | * Stringify oauth params 1066 | * @author Sahat Yalkabov 1067 | * @copyright Method taken from https://github.com/sahat/satellizer 1068 | * 1069 | * @return {String} 1070 | */ 1071 | OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { 1072 | var this$1 = this; 1073 | 1074 | var keyValuePairs = []; 1075 | var paramCategories = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; 1076 | 1077 | paramCategories.forEach(function (categoryName) { 1078 | if (!this$1.providerConfig[categoryName]) { return } 1079 | if (!Array.isArray(this$1.providerConfig[categoryName])) { return } 1080 | 1081 | this$1.providerConfig[categoryName].forEach(function (paramName) { 1082 | var camelCaseParamName = camelCase(paramName); 1083 | var paramValue = isFunction(this$1.providerConfig[paramName]) ? this$1.providerConfig[paramName]() : this$1.providerConfig[camelCaseParamName]; 1084 | 1085 | if (paramName === 'redirect_uri' && !paramValue) { return } 1086 | 1087 | if (paramName === 'state') { 1088 | var stateName = this$1.providerConfig.name + '_state'; 1089 | paramValue = encodeURIComponent(this$1.storage.getItem(stateName)); 1090 | } 1091 | if (paramName === 'scope' && Array.isArray(paramValue)) { 1092 | paramValue = paramValue.join(this$1.providerConfig.scopeDelimiter); 1093 | if (this$1.providerConfig.scopePrefix) { 1094 | paramValue = [this$1.providerConfig.scopePrefix, paramValue].join(this$1.providerConfig.scopeDelimiter); 1095 | } 1096 | } 1097 | 1098 | keyValuePairs.push([paramName, paramValue]); 1099 | }); 1100 | }); 1101 | 1102 | return keyValuePairs.map(function (param) { 1103 | return param.join('=') 1104 | }).join('&') 1105 | }; 1106 | 1107 | var VueSocialauth = function VueSocialauth($http, overrideOptions) { 1108 | var options = objectExtend({}, defaultOptions); 1109 | options = objectExtend(options, overrideOptions); 1110 | var storage = StorageFactory(options); 1111 | 1112 | Object.defineProperties(this, { 1113 | $http: { 1114 | get: function get() { 1115 | return $http 1116 | } 1117 | }, 1118 | 1119 | options: { 1120 | get: function get() { 1121 | return options 1122 | } 1123 | }, 1124 | 1125 | storage: { 1126 | get: function get() { 1127 | return storage 1128 | } 1129 | }, 1130 | 1131 | tokenName: { 1132 | get: function get() { 1133 | if (this.options.tokenPrefix) { 1134 | return [this.options.tokenPrefix, this.options.tokenName].join('_') 1135 | } else { 1136 | return this.options.tokenName 1137 | } 1138 | } 1139 | } 1140 | }); 1141 | 1142 | // Setup request interceptors 1143 | if (this.options.bindRequestInterceptor && isFunction(this.options.bindRequestInterceptor) && 1144 | this.options.bindResponseInterceptor && isFunction(this.options.bindResponseInterceptor)) { 1145 | 1146 | this.options.bindRequestInterceptor.call(this, this); 1147 | this.options.bindResponseInterceptor.call(this, this); 1148 | } else { 1149 | throw new Error('Both request and response interceptors must be functions') 1150 | } 1151 | }; 1152 | 1153 | /** 1154 | * Check if user is authenticated 1155 | * @author Sahat Yalkabov 1156 | * @copyright Method taken from https://github.com/sahat/satellizer 1157 | * @return {Boolean} 1158 | */ 1159 | // isAuthenticated() { 1160 | // let token = this.storage.getItem(this.tokenName) 1161 | 1162 | // if (token) {// Token is present 1163 | // if (token.split('.').length === 3) {// Token with a valid JWT format XXX.YYY.ZZZ 1164 | // try { // Could be a valid JWT or an access token with the same format 1165 | // const base64Url = token.split('.')[1]; 1166 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 1167 | // const exp = JSON.parse(window.atob(base64)).exp; 1168 | // if (typeof exp === 'number') {// JWT with an optonal expiration claims 1169 | // return Math.round(new Date().getTime() / 1000) < exp; 1170 | // } 1171 | // } catch (e) { 1172 | // return true;// Pass: Non-JWT token that looks like JWT 1173 | // } 1174 | // } 1175 | // return true;// Pass: All other tokens 1176 | // } 1177 | // return false 1178 | // } 1179 | 1180 | /** 1181 | * Get token if user is authenticated 1182 | * @return {String} Authentication token 1183 | */ 1184 | VueSocialauth.prototype.getToken = function getToken () { 1185 | return this.storage.getItem(this.tokenName) 1186 | }; 1187 | 1188 | /** 1189 | * Set new authentication token 1190 | * @param {String|Object} token 1191 | */ 1192 | // setToken(response) { 1193 | // if (response[this.options.responseDataKey]) { 1194 | // response = response[this.options.responseDataKey]; 1195 | // } 1196 | 1197 | // let token; 1198 | // if (response.access_token) { 1199 | // if (isObject(response.access_token) && isObject(response.access_token[this.options.responseDataKey])) { 1200 | // response = response.access_token 1201 | // } else if (isString(response.access_token)) { 1202 | // token = response.access_token 1203 | // } 1204 | // } 1205 | 1206 | // if (!token && response) { 1207 | // token = response[this.options.tokenName] 1208 | // } 1209 | 1210 | // if (token) { 1211 | // this.storage.setItem(this.tokenName, token) 1212 | // } 1213 | // } 1214 | 1215 | // getPayload() { 1216 | // const token = this.storage.getItem(this.tokenName); 1217 | 1218 | // if (token && token.split('.').length === 3) { 1219 | // try { 1220 | // const base64Url = token.split('.')[1]; 1221 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 1222 | // return JSON.parse(decodeBase64(base64)); 1223 | // } catch (e) {} 1224 | // } 1225 | // } 1226 | 1227 | /** 1228 | * Login user using email and password 1229 | * @param{Object} user User data 1230 | * @param{Object} requestOptions Request options 1231 | * @return {Promise} Request promise 1232 | */ 1233 | // login(user, requestOptions) { 1234 | // requestOptions = requestOptions || {} 1235 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.loginUrl) 1236 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 1237 | // requestOptions.method = requestOptions.method || 'POST' 1238 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1239 | 1240 | // return this.$http(requestOptions).then((response) => { 1241 | // this.setToken(response) 1242 | // return response 1243 | // }) 1244 | // } 1245 | 1246 | /** 1247 | * Register new user 1248 | * @param{Object} user User data 1249 | * @param{Object} requestOptions Request options 1250 | * @return {Promise} Request promise 1251 | */ 1252 | // register(user, requestOptions) { 1253 | // requestOptions = requestOptions || {} 1254 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.registerUrl) 1255 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 1256 | // requestOptions.method = requestOptions.method || 'POST' 1257 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1258 | 1259 | // return this.$http(requestOptions).then((response) => { 1260 | // this.setToken(response) 1261 | // return response 1262 | // }) 1263 | // } 1264 | 1265 | /** 1266 | * Logout current user 1267 | * @param{Object} requestOptionsLogout request options object 1268 | * @return {Promise} Request promise 1269 | */ 1270 | // logout(requestOptions) { 1271 | // if (!this.isAuthenticated()) { 1272 | // return Promise.reject(new Error('There is no currently authenticated user')) 1273 | // } 1274 | 1275 | // requestOptions = requestOptions || {} 1276 | // requestOptions.url = requestOptions.logoutUrl || this.options.logoutUrl 1277 | 1278 | // if (requestOptions.url) { 1279 | // requestOptions.method = requestOptions.method || 'POST' 1280 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 1281 | 1282 | // return this.$http(requestOptions).then((response) => { 1283 | // this.storage.removeItem(this.tokenName) 1284 | // }) 1285 | // } else { 1286 | // this.storage.removeItem(this.tokenName) 1287 | // return Promise.resolve(); 1288 | // } 1289 | // } 1290 | 1291 | /** 1292 | * Authenticate user using authentication provider 1293 | * 1294 | * @param{String} provider Provider name 1295 | * @param{Object} userData User data 1296 | * @param{Object} requestOptions Request options 1297 | * @return {Promise} Request promise 1298 | */ 1299 | VueSocialauth.prototype.authenticate = function authenticate (provider, userData, requestOptions) { 1300 | var this$1 = this; 1301 | 1302 | return new Promise$1(function (resolve, reject) { 1303 | var providerConfig = this$1.options.providers[provider]; 1304 | if (!providerConfig) { 1305 | return reject(new Error('Unknown provider')) 1306 | } 1307 | 1308 | var providerInstance; 1309 | switch (providerConfig.oauthType) { 1310 | case '1.0': 1311 | providerInstance = new OAuth(this$1.$http, this$1.storage, providerConfig, this$1.options); 1312 | break 1313 | case '2.0': 1314 | providerInstance = new OAuth2(this$1.$http, this$1.storage, providerConfig, this$1.options); 1315 | break 1316 | default: 1317 | return reject(new Error('Invalid OAuth type')) 1318 | break 1319 | } 1320 | 1321 | return providerInstance.init(userData).then(function (response) { 1322 | return resolve(response) 1323 | 1324 | }).catch(function (err) { return reject(err); }) 1325 | }) 1326 | }; 1327 | 1328 | /** 1329 | * VueSocialauth plugin 1330 | * @param {Object} Vue 1331 | * @param {Object} options 1332 | */ 1333 | function plugin(Vue, options) { 1334 | if (plugin.installed) { 1335 | return 1336 | } 1337 | plugin.installed = true; 1338 | 1339 | var property = options.property || '$auth'; 1340 | 1341 | var vueAuthInstance = null; 1342 | Object.defineProperties(Vue.prototype, ( obj = {}, obj[property] = { 1343 | get: function get() { 1344 | if (!vueAuthInstance) { 1345 | // Request handler library not found, throw error 1346 | // verified vue or nuxt instance 1347 | if (this.$axios) { 1348 | vueAuthInstance = new VueSocialauth(this.$axios, options); 1349 | } else if (this.$http) { 1350 | vueAuthInstance = new VueSocialauth(this.$http, options); 1351 | } else { 1352 | throw new Error('Request handler instance not found') 1353 | } 1354 | } 1355 | return vueAuthInstance 1356 | } 1357 | }, obj )); 1358 | var obj; 1359 | } 1360 | 1361 | /** 1362 | * External factory helper for ES5 and CommonJS 1363 | * @param {Object} $http Instance of request handling library 1364 | * @param {Object} options Configuration object 1365 | * @return {VueSocialauth} VueSocialauth instance 1366 | */ 1367 | plugin.factory = function ($http, options) { 1368 | return new VueSocialauth($http, options) 1369 | }; 1370 | 1371 | return plugin; 1372 | 1373 | }))); 1374 | -------------------------------------------------------------------------------- /dist/vue-social-auth.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-social-auth v0.0.1 3 | * https://github.com/diadal/vue-social-auth 4 | * Released under the MIT License. 5 | */ 6 | 7 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueSocialauth=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return"string"==typeof t}function n(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function r(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function a(t){var e="https:"===t.protocol;return t.protocol+"//"+t.hostname+":"+(t.port||(e?"443":"80"))+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function s(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function p(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],i=o[1],r=decodeURIComponent(n),a=decodeURIComponent(i);e[r]=a}),e}function u(t){var e=t.path,o=t.domain,n=t.expires,i=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===i||null===i||!1===i?"":";secure"].join("")}function c(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),u(o)].join("")}function l(){}function h(t,e){return function(){t.apply(e,arguments)}}function d(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],w(t,this)}function f(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,d._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?g:m)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void m(e.promise,t)}g(e.promise,n)})}function g(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof d)return t._state=3,t._value=e,void v(t);if("function"==typeof o)return void w(h(o,e),t)}t._state=1,t._value=e,v(t)}catch(e){m(t,e)}}function m(t,e){t._state=2,t._value=e,v(t)}function v(t){2===t._state&&0===t._deferreds.length&&d._immediateFn(function(){t._handled||d._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e { 5 | // aviable only in client side 6 | if (!process.client) { 7 | return 8 | } 9 | 10 | //initialize plugin with options 11 | const pluginOptions = [<%= serialize(options) %>][0] || {} 12 | Vue.use(VueSocialAuth, pluginOptions) 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-social-auth-5", 3 | "version": "0.0.1", 4 | "description": "Social Authentication library for laravel with Vue.js (SPA)", 5 | "main": "dist/vue-social-auth.js", 6 | "module": "dist/vue-social-auth.es2017.js", 7 | "unpkg": "dist/vue-social-auth.js", 8 | "scripts": { 9 | "build": "node build/build.js" 10 | }, 11 | "keywords": [ 12 | "vue", 13 | "vuejs", 14 | "auth", 15 | "laravel vue", 16 | "vue Social Login", 17 | "vue facebook", 18 | "vue google", 19 | "vue github", 20 | "vue vkontakte" 21 | ], 22 | "author": { 23 | "name": "diadal", 24 | "email": "info@diadal.com.ng", 25 | "url": "https://diadal.com.ng" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/diadal/vue-social-auth.git" 30 | }, 31 | "license": "MIT", 32 | "devDependencies": { 33 | "axios": "^0.21.1", 34 | "body-parser": "^1.17.1", 35 | "connect-history-api-fallback": "^1.3.0", 36 | "cors": "^2.8.1", 37 | "express": "^4.15.2", 38 | "gulp": "^4.0.0", 39 | "gulp-connect": "^5.0.0", 40 | "node-dev": "^3.1.3", 41 | "oauth": "^0.9.15", 42 | "oauth-signature": "^1.3.1", 43 | "parallelshell": "^2.0.0", 44 | "request": "^2.81.0", 45 | "rollup": "^0.41.6", 46 | "rollup-plugin-babel": "^2.7.1", 47 | "rollup-plugin-buble": "^0.15.0", 48 | "rollup-plugin-commonjs": "^8.0.2", 49 | "rollup-plugin-node-resolve": "^3.0.0", 50 | "rollup-plugin-uglify": "^1.0.1", 51 | "rollup-stream": "^1.19.0", 52 | "uglify-js": "^2.8.22", 53 | "unix-timestamp": "^0.2.0", 54 | "vinyl-source-stream": "^1.1.0", 55 | "vue": "^2.5.2", 56 | "vue-axios": "^2.0.2", 57 | "vue-router": "^2.3.0", 58 | "vuex": "^2.2.1", 59 | "webpack": "^5.23.0" 60 | }, 61 | "tags": [ 62 | "auth", 63 | "authentication", 64 | "login", 65 | "registration", 66 | "jwt", 67 | "token", 68 | "vuejs", 69 | "vue.js", 70 | "github", 71 | "facebook", 72 | "google", 73 | "twitter", 74 | "oauth", 75 | "oauth 1.0", 76 | "oauth 2.0", 77 | "laravel", 78 | "SPA Social Login" 79 | ], 80 | "dependencies": { 81 | "graceful-fs": "^4.1.15" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/authenticate.js: -------------------------------------------------------------------------------- 1 | import Promise from './promise.js' 2 | import { objectExtend, isString, isObject, isFunction, joinUrl, decodeBase64 } from './utils.js' 3 | import defaultOptions from './options.js' 4 | import StorageFactory from './storage.js' 5 | import OAuth1 from './oauth/oauth1.js' 6 | import OAuth2 from './oauth/oauth2.js' 7 | 8 | export default class VueSocialauth { 9 | constructor($http, overrideOptions) { 10 | let options = objectExtend({}, defaultOptions) 11 | options = objectExtend(options, overrideOptions) 12 | let storage = StorageFactory(options) 13 | 14 | Object.defineProperties(this, { 15 | $http: { 16 | get() { 17 | return $http 18 | } 19 | }, 20 | 21 | options: { 22 | get() { 23 | return options 24 | } 25 | }, 26 | 27 | storage: { 28 | get() { 29 | return storage 30 | } 31 | }, 32 | 33 | tokenName: { 34 | get() { 35 | if (this.options.tokenPrefix) { 36 | return [this.options.tokenPrefix, this.options.tokenName].join('_') 37 | } else { 38 | return this.options.tokenName 39 | } 40 | } 41 | } 42 | }) 43 | 44 | // Setup request interceptors 45 | if (this.options.bindRequestInterceptor && isFunction(this.options.bindRequestInterceptor) && 46 | this.options.bindResponseInterceptor && isFunction(this.options.bindResponseInterceptor)) { 47 | 48 | this.options.bindRequestInterceptor.call(this, this) 49 | this.options.bindResponseInterceptor.call(this, this) 50 | } else { 51 | throw new Error('Both request and response interceptors must be functions') 52 | } 53 | } 54 | 55 | /** 56 | * Check if user is authenticated 57 | * @author Sahat Yalkabov 58 | * @copyright Method taken from https://github.com/sahat/satellizer 59 | * @return {Boolean} 60 | */ 61 | // isAuthenticated() { 62 | // let token = this.storage.getItem(this.tokenName) 63 | 64 | // if (token) { // Token is present 65 | // if (token.split('.').length === 3) { // Token with a valid JWT format XXX.YYY.ZZZ 66 | // try { // Could be a valid JWT or an access token with the same format 67 | // const base64Url = token.split('.')[1]; 68 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 69 | // const exp = JSON.parse(window.atob(base64)).exp; 70 | // if (typeof exp === 'number') { // JWT with an optonal expiration claims 71 | // return Math.round(new Date().getTime() / 1000) < exp; 72 | // } 73 | // } catch (e) { 74 | // return true; // Pass: Non-JWT token that looks like JWT 75 | // } 76 | // } 77 | // return true; // Pass: All other tokens 78 | // } 79 | // return false 80 | // } 81 | 82 | /** 83 | * Get token if user is authenticated 84 | * @return {String} Authentication token 85 | */ 86 | getToken() { 87 | return this.storage.getItem(this.tokenName) 88 | } 89 | 90 | /** 91 | * Set new authentication token 92 | * @param {String|Object} token 93 | */ 94 | // setToken(response) { 95 | // if (response[this.options.responseDataKey]) { 96 | // response = response[this.options.responseDataKey]; 97 | // } 98 | 99 | // let token; 100 | // if (response.access_token) { 101 | // if (isObject(response.access_token) && isObject(response.access_token[this.options.responseDataKey])) { 102 | // response = response.access_token 103 | // } else if (isString(response.access_token)) { 104 | // token = response.access_token 105 | // } 106 | // } 107 | 108 | // if (!token && response) { 109 | // token = response[this.options.tokenName] 110 | // } 111 | 112 | // if (token) { 113 | // this.storage.setItem(this.tokenName, token) 114 | // } 115 | // } 116 | 117 | // getPayload() { 118 | // const token = this.storage.getItem(this.tokenName); 119 | 120 | // if (token && token.split('.').length === 3) { 121 | // try { 122 | // const base64Url = token.split('.')[1]; 123 | // const base64 = base64Url.replace('-', '+').replace('_', '/'); 124 | // return JSON.parse(decodeBase64(base64)); 125 | // } catch (e) {} 126 | // } 127 | // } 128 | 129 | /** 130 | * Login user using email and password 131 | * @param {Object} user User data 132 | * @param {Object} requestOptions Request options 133 | * @return {Promise} Request promise 134 | */ 135 | // login(user, requestOptions) { 136 | // requestOptions = requestOptions || {} 137 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.loginUrl) 138 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 139 | // requestOptions.method = requestOptions.method || 'POST' 140 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 141 | 142 | // return this.$http(requestOptions).then((response) => { 143 | // this.setToken(response) 144 | // return response 145 | // }) 146 | // } 147 | 148 | /** 149 | * Register new user 150 | * @param {Object} user User data 151 | * @param {Object} requestOptions Request options 152 | * @return {Promise} Request promise 153 | */ 154 | // register(user, requestOptions) { 155 | // requestOptions = requestOptions || {} 156 | // requestOptions.url = requestOptions.url ? requestOptions.url : joinUrl(this.options.baseUrl, this.options.registerUrl) 157 | // requestOptions[this.options.requestDataKey] = user || requestOptions[this.options.requestDataKey] 158 | // requestOptions.method = requestOptions.method || 'POST' 159 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 160 | 161 | // return this.$http(requestOptions).then((response) => { 162 | // this.setToken(response) 163 | // return response 164 | // }) 165 | // } 166 | 167 | /** 168 | * Logout current user 169 | * @param {Object} requestOptions Logout request options object 170 | * @return {Promise} Request promise 171 | */ 172 | // logout(requestOptions) { 173 | // if (!this.isAuthenticated()) { 174 | // return Promise.reject(new Error('There is no currently authenticated user')) 175 | // } 176 | 177 | // requestOptions = requestOptions || {} 178 | // requestOptions.url = requestOptions.logoutUrl || this.options.logoutUrl 179 | 180 | // if (requestOptions.url) { 181 | // requestOptions.method = requestOptions.method || 'POST' 182 | // requestOptions.withCredentials = requestOptions.withCredentials || this.options.withCredentials 183 | 184 | // return this.$http(requestOptions).then((response) => { 185 | // this.storage.removeItem(this.tokenName) 186 | // }) 187 | // } else { 188 | // this.storage.removeItem(this.tokenName) 189 | // return Promise.resolve(); 190 | // } 191 | // } 192 | 193 | /** 194 | * Authenticate user using authentication provider 195 | * 196 | * @param {String} provider Provider name 197 | * @param {Object} userData User data 198 | * @param {Object} requestOptions Request options 199 | * @return {Promise} Request promise 200 | */ 201 | authenticate(provider, userData, requestOptions) { 202 | return new Promise((resolve, reject) => { 203 | var providerConfig = this.options.providers[provider] 204 | if (!providerConfig) { 205 | return reject(new Error('Unknown provider')) 206 | } 207 | 208 | let providerInstance; 209 | switch (providerConfig.oauthType) { 210 | case '1.0': 211 | providerInstance = new OAuth1(this.$http, this.storage, providerConfig, this.options) 212 | break 213 | case '2.0': 214 | providerInstance = new OAuth2(this.$http, this.storage, providerConfig, this.options) 215 | break 216 | default: 217 | return reject(new Error('Invalid OAuth type')) 218 | break 219 | } 220 | 221 | return providerInstance.init(userData).then((response) => { 222 | return resolve(response) 223 | 224 | }).catch(err => reject(err)) 225 | }) 226 | } 227 | 228 | } 229 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './utils.js' 2 | import Promise from './promise.js' 3 | import VueSocialauth from './authenticate.js' 4 | 5 | /** 6 | * VueSocialauth plugin 7 | * @param {Object} Vue 8 | * @param {Object} options 9 | */ 10 | function plugin(Vue, options) { 11 | if (plugin.installed) { 12 | return 13 | } 14 | plugin.installed = true 15 | 16 | const property = options.property || '$auth' 17 | 18 | let vueAuthInstance = null; 19 | Object.defineProperties(Vue.prototype, { 20 | [property]: { 21 | get() { 22 | if (!vueAuthInstance) { 23 | // Request handler library not found, throw error 24 | // verified vue or nuxt instance 25 | if (this.$axios) { 26 | vueAuthInstance = new VueSocialauth(this.$axios, options) 27 | } else if (this.$http) { 28 | vueAuthInstance = new VueSocialauth(this.$http, options) 29 | } else { 30 | throw new Error('Request handler instance not found') 31 | } 32 | } 33 | return vueAuthInstance 34 | } 35 | } 36 | }) 37 | } 38 | 39 | /** 40 | * External factory helper for ES5 and CommonJS 41 | * @param {Object} $http Instance of request handling library 42 | * @param {Object} options Configuration object 43 | * @return {VueSocialauth} VueSocialauth instance 44 | */ 45 | plugin.factory = function ($http, options) { 46 | return new VueSocialauth($http, options) 47 | } 48 | 49 | export default plugin 50 | -------------------------------------------------------------------------------- /src/oauth/oauth1.js: -------------------------------------------------------------------------------- 1 | import OAuthPopup from './popup.js' 2 | import { objectExtend, isString, isObject, isFunction, joinUrl } from '../utils.js' 3 | 4 | const defaultProviderConfig = { 5 | name: null, 6 | url: null, 7 | authorizationEndpoint: null, 8 | scope: null, 9 | scopePrefix: null, 10 | scopeDelimiter: null, 11 | redirectUri: null, 12 | requiredUrlParams: null, 13 | defaultUrlParams: null, 14 | oauthType: '1.0', 15 | popupOptions: {} 16 | } 17 | 18 | export default class OAuth { 19 | constructor($http, storage, providerConfig, options) { 20 | this.$http = $http 21 | this.storage = storage 22 | this.providerConfig = objectExtend({}, defaultProviderConfig) 23 | this.providerConfig = objectExtend(this.providerConfig, providerConfig) 24 | this.options = options 25 | } 26 | 27 | /** 28 | * Initialize OAuth1 process 29 | * @param {Object} userData User data 30 | * @return {Promise} 31 | */ 32 | init(userData) { 33 | this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions) 34 | 35 | if (window && !window['cordova']) { 36 | this.oauthPopup.open(this.providerConfig.redirectUri, true) 37 | } 38 | 39 | return this.getRequestToken().then((response) => { 40 | return this.openPopup(response).then((popupResponse) => { 41 | return this.exchangeForToken(popupResponse, userData) 42 | }) 43 | }) 44 | } 45 | 46 | /** 47 | * Get OAuth1 request token 48 | * @return {Promise} 49 | */ 50 | getRequestToken() { 51 | let requestOptions = {} 52 | requestOptions.method = 'POST' 53 | requestOptions[this.options.requestDataKey] = objectExtend({}, this.providerConfig) 54 | requestOptions.withCredentials = this.options.withCredentials 55 | if (this.options.baseUrl) { 56 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url) 57 | } else { 58 | requestOptions.url = this.providerConfig.url 59 | } 60 | 61 | return this.$http(requestOptions) 62 | } 63 | 64 | /** 65 | * Open OAuth1 popup 66 | * @param {Object} response Response object containing request token 67 | * @return {Promise} 68 | */ 69 | openPopup(response) { 70 | const url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); 71 | 72 | this.oauthPopup.popup.location = url 73 | if (window && window['cordova']) { 74 | return this.oauthPopup.open(this.providerConfig.redirectUri) 75 | } else { 76 | return this.oauthPopup.pooling(this.providerConfig.redirectUri) 77 | } 78 | } 79 | 80 | /** 81 | * Exchange token and token verifier for access token 82 | * @param {Object} oauth OAuth data containing token and token verifier 83 | * @param {Object} userData User data 84 | * @return {Promise} 85 | */ 86 | exchangeForToken(oauth, userData) { 87 | let payload = objectExtend({}, userData) 88 | payload = objectExtend(payload, oauth) 89 | let requestOptions = {} 90 | requestOptions.method = 'POST' 91 | requestOptions[this.options.requestDataKey] = payload 92 | requestOptions.withCredentials = this.options.withCredentials 93 | if (this.options.baseUrl) { 94 | requestOptions.url = joinUrl(this.options.baseUrl, this.providerConfig.url) 95 | } else { 96 | requestOptions.url = this.providerConfig.url 97 | } 98 | return this.$http(requestOptions) 99 | } 100 | 101 | buildQueryString(params) { 102 | const parsedParams = []; 103 | for (var key in params) { 104 | let value = params[key] 105 | parsedParams.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); 106 | } 107 | return parsedParams.join('&'); 108 | } 109 | } -------------------------------------------------------------------------------- /src/oauth/oauth2.js: -------------------------------------------------------------------------------- 1 | import OAuthPopup from './popup.js' 2 | import { camelCase, isFunction, isString, objectExtend, joinUrl } from '../utils.js' 3 | 4 | /** 5 | * Default provider configuration 6 | * @type {Object} 7 | */ 8 | const defaultProviderConfig = { 9 | name: null, 10 | url: null, 11 | clientId: null, 12 | authorizationEndpoint: null, 13 | redirectUri: null, 14 | scope: null, 15 | scopePrefix: null, 16 | scopeDelimiter: null, 17 | state: null, 18 | requiredUrlParams: null, 19 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 20 | responseType: 'code', 21 | responseParams: { 22 | code: 'code', 23 | clientId: 'clientId', 24 | redirectUri: 'redirectUri' 25 | }, 26 | oauthType: '2.0', 27 | popupOptions: {} 28 | } 29 | 30 | export default class OAuth2 { 31 | constructor($http, storage, providerConfig, options) { 32 | this.$http = $http 33 | this.storage = storage 34 | this.providerConfig = objectExtend({}, defaultProviderConfig) 35 | this.providerConfig = objectExtend(this.providerConfig, providerConfig) 36 | this.options = options 37 | } 38 | 39 | init(userData) { 40 | let stateName = this.providerConfig.name + '_state'; 41 | if (isFunction(this.providerConfig.state)) { 42 | this.storage.setItem(stateName, this.providerConfig.state()) 43 | } else if (isString(this.providerConfig.state)) { 44 | this.storage.setItem(stateName, this.providerConfig.state) 45 | } 46 | 47 | let url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?') 48 | 49 | this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions) 50 | 51 | return new Promise((resolve, reject) => { 52 | this.oauthPopup.open(this.providerConfig.redirectUri).then((response) => { 53 | if (this.providerConfig.responseType === 'code' || !this.providerConfig.url) { 54 | return resolve(response) 55 | } 56 | 57 | if (response.state && response.state !== this.storage.getItem(stateName)) { 58 | return reject(new Error('State parameter value does not match original OAuth request state value')) 59 | } 60 | 61 | resolve(this.exchangeForToken(response, userData)) 62 | }).catch((err) => { 63 | reject(err) 64 | }) 65 | }) 66 | } 67 | 68 | /** 69 | * Exchange temporary oauth data for access token 70 | * @author Sahat Yalkabov 71 | * @copyright Method taken from https://github.com/sahat/satellizer 72 | * 73 | * @param {[type]} oauth [description] 74 | * @param {[type]} userData [description] 75 | * @return {[type]} [description] 76 | */ 77 | exchangeForToken(oauth, userData) { 78 | let payload = objectExtend({}, userData) 79 | 80 | for (let key in defaultProviderConfig.responseParams) { 81 | let value = defaultProviderConfig[key] 82 | 83 | switch(key) { 84 | case 'code': 85 | payload[key] = oauth.code 86 | break 87 | case 'clientId': 88 | payload[key] = this.providerConfig.clientId 89 | break 90 | case 'redirectUri': 91 | payload[key] = this.providerConfig.redirectUri 92 | break 93 | default: 94 | payload[key] = oauth[key] 95 | } 96 | } 97 | 98 | if (oauth.state) { 99 | payload.state = oauth.state 100 | } 101 | 102 | let exchangeTokenUrl 103 | if (this.options.baseUrl) { 104 | exchangeTokenUrl = joinUrl(this.options.baseUrl, this.providerConfig.url) 105 | } else { 106 | exchangeTokenUrl = this.providerConfig.url 107 | } 108 | 109 | return this.$http.post(exchangeTokenUrl, payload, { 110 | withCredentials: this.options.withCredentials 111 | }) 112 | } 113 | 114 | /** 115 | * Stringify oauth params 116 | * @author Sahat Yalkabov 117 | * @copyright Method taken from https://github.com/sahat/satellizer 118 | * 119 | * @return {String} 120 | */ 121 | _stringifyRequestParams() { 122 | let keyValuePairs = [] 123 | let paramCategories = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams'] 124 | 125 | paramCategories.forEach((categoryName) => { 126 | if (!this.providerConfig[categoryName]) return 127 | if (!Array.isArray(this.providerConfig[categoryName])) return 128 | 129 | this.providerConfig[categoryName].forEach((paramName) => { 130 | let camelCaseParamName = camelCase(paramName) 131 | let paramValue = isFunction(this.providerConfig[paramName]) ? this.providerConfig[paramName]() : this.providerConfig[camelCaseParamName] 132 | 133 | if (paramName === 'redirect_uri' && !paramValue) return 134 | 135 | if (paramName === 'state') { 136 | let stateName = this.providerConfig.name + '_state'; 137 | paramValue = encodeURIComponent(this.storage.getItem(stateName)); 138 | } 139 | if (paramName === 'scope' && Array.isArray(paramValue)) { 140 | paramValue = paramValue.join(this.providerConfig.scopeDelimiter); 141 | if (this.providerConfig.scopePrefix) { 142 | paramValue = [this.providerConfig.scopePrefix, paramValue].join(this.providerConfig.scopeDelimiter); 143 | } 144 | } 145 | 146 | keyValuePairs.push([paramName, paramValue]) 147 | }) 148 | }) 149 | 150 | return keyValuePairs.map((param) => { 151 | return param.join('=') 152 | }).join('&') 153 | } 154 | } -------------------------------------------------------------------------------- /src/oauth/popup.js: -------------------------------------------------------------------------------- 1 | import Promise from '../promise.js' 2 | import { objectExtend, parseQueryString, getFullUrlPath, isUndefined } from '../utils.js' 3 | 4 | /** 5 | * OAuth2 popup management class 6 | * 7 | * @author Sahat Yalkabov 8 | * @copyright Class mostly taken from https://github.com/sahat/satellizer 9 | * and adjusted to fit vue-social-auth library 10 | */ 11 | export default class OAuthPopup { 12 | constructor(url, name, popupOptions) { 13 | this.popup = null 14 | this.url = url 15 | this.name = name 16 | this.popupOptions = popupOptions 17 | } 18 | 19 | open(redirectUri, skipPooling) { 20 | try { 21 | this.popup = window.open(this.url, this.name, this._stringifyOptions()) 22 | if (this.popup && this.popup.focus) { 23 | this.popup.focus() 24 | } 25 | 26 | if (skipPooling) { 27 | return Promise.resolve() 28 | } else { 29 | return this.pooling(redirectUri) 30 | } 31 | } catch(e) { 32 | return Promise.reject(new Error('OAuth popup error occurred')) 33 | } 34 | } 35 | 36 | pooling(redirectUri) { 37 | return new Promise((resolve, reject) => { 38 | const redirectUriParser = document.createElement('a') 39 | redirectUriParser.href = redirectUri 40 | const redirectUriPath = getFullUrlPath(redirectUriParser) 41 | 42 | let poolingInterval = setInterval(() => { 43 | if (!this.popup || this.popup.closed || this.popup.closed === undefined) { 44 | clearInterval(poolingInterval) 45 | poolingInterval = null 46 | reject(new Error('Auth popup window closed')) 47 | } 48 | 49 | try { 50 | const popupWindowPath = getFullUrlPath(this.popup.location) 51 | 52 | if (popupWindowPath === redirectUriPath) { 53 | if (this.popup.location.search || this.popup.location.hash) { 54 | const query = parseQueryString(this.popup.location.search.substring(1).replace(/\/$/, '')); 55 | const hash = parseQueryString(this.popup.location.hash.substring(1).replace(/[\/$]/, '')); 56 | let params = objectExtend({}, query); 57 | params = objectExtend(params, hash) 58 | 59 | if (params.error) { 60 | reject(new Error(params.error)); 61 | } else { 62 | resolve(params); 63 | } 64 | } else { 65 | reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')) 66 | } 67 | 68 | clearInterval(poolingInterval) 69 | poolingInterval = null 70 | this.popup.close() 71 | } 72 | } catch(e) { 73 | // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. 74 | } 75 | }, 250) 76 | }) 77 | } 78 | 79 | _stringifyOptions() { 80 | let options = [] 81 | for (var optionKey in this.popupOptions) { 82 | if (!isUndefined(this.popupOptions[optionKey])) { 83 | options.push(`${optionKey}=${this.popupOptions[optionKey]}`) 84 | } 85 | } 86 | return options.join(',') 87 | } 88 | } -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default configuration 3 | */ 4 | export default { 5 | baseUrl: null, 6 | tokenName: 'token', 7 | tokenPrefix: 'vueauth', 8 | tokenHeader: 'Authorization', 9 | tokenType: 'Bearer', 10 | loginUrl: '/auth/login', 11 | registerUrl: '/auth/register', 12 | logoutUrl: null, 13 | storageType: 'localStorage', 14 | storageNamespace: 'vue-social-auth', 15 | cookieStorage: { 16 | domain: window.location.hostname, 17 | path: '/', 18 | secure: false 19 | }, 20 | requestDataKey: 'data', 21 | responseDataKey: 'data', 22 | 23 | /** 24 | * Default request interceptor for Axios library 25 | * @context {VueSocialauth} 26 | */ 27 | bindRequestInterceptor: function ($auth) { 28 | 29 | const tokenHeader = $auth.options.tokenHeader; 30 | 31 | $auth.$http.interceptors.request.use((config) => { 32 | delete config.headers[tokenHeader] 33 | return config 34 | }) 35 | 36 | 37 | }, 38 | 39 | /** 40 | * Default response interceptor for Axios library 41 | * @contect {VueSocialauth} 42 | */ 43 | bindResponseInterceptor: function ($auth) { 44 | $auth.$http.interceptors.response.use((response) => { 45 | return response 46 | }) 47 | }, 48 | 49 | providers: { 50 | facebook: { 51 | name: 'facebook', 52 | url: '/auth/facebook', 53 | authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', 54 | redirectUri: window.location.origin + '/', 55 | requiredUrlParams: ['display', 'scope'], 56 | scope: ['email'], 57 | scopeDelimiter: ',', 58 | display: 'popup', 59 | oauthType: '2.0', 60 | popupOptions: { width: 580, height: 400 } 61 | }, 62 | 63 | google: { 64 | name: 'google', 65 | url: '/auth/google', 66 | authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', 67 | redirectUri: window.location.origin, 68 | requiredUrlParams: ['scope'], 69 | optionalUrlParams: ['display'], 70 | scope: ['profile', 'email'], 71 | scopePrefix: 'openid', 72 | scopeDelimiter: ' ', 73 | display: 'popup', 74 | oauthType: '2.0', 75 | popupOptions: { width: 452, height: 633 } 76 | }, 77 | 78 | github: { 79 | name: 'github', 80 | url: '/auth/github', 81 | authorizationEndpoint: 'https://github.com/login/oauth/authorize', 82 | redirectUri: window.location.origin, 83 | optionalUrlParams: ['scope'], 84 | scope: ['user:email'], 85 | scopeDelimiter: ' ', 86 | oauthType: '2.0', 87 | popupOptions: { width: 1020, height: 618 } 88 | }, 89 | 90 | instagram: { 91 | name: 'instagram', 92 | url: '/auth/instagram', 93 | authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', 94 | redirectUri: window.location.origin, 95 | requiredUrlParams: ['scope'], 96 | scope: ['basic'], 97 | scopeDelimiter: '+', 98 | oauthType: '2.0', 99 | popupOptions: { width: null, height: null } 100 | }, 101 | 102 | twitter: { 103 | name: 'twitter', 104 | url: '/auth/twitter', 105 | authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', 106 | redirectUri: window.location.origin, 107 | oauthType: '1.0', 108 | popupOptions: { width: 495, height: 645 } 109 | }, 110 | 111 | bitbucket: { 112 | name: 'bitbucket', 113 | url: '/auth/bitbucket', 114 | authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', 115 | redirectUri: window.location.origin + '/', 116 | optionalUrlParams: ['scope'], 117 | scope: ['email'], 118 | scopeDelimiter: ' ', 119 | oauthType: '2.0', 120 | popupOptions: { width: 1020, height: 618 } 121 | }, 122 | 123 | linkedin: { 124 | name: 'linkedin', 125 | url: '/auth/linkedin', 126 | authorizationEndpoint: 'https://www.linkedin.com/oauth/v2/authorization', 127 | redirectUri: window.location.origin, 128 | requiredUrlParams: ['state','scope'], 129 | scope: ['r_liteprofile','r_emailaddress'], 130 | scopeDelimiter: ' ', 131 | state: 'STATE', 132 | oauthType: '2.0', 133 | popupOptions: { width: 527, height: 582 } 134 | }, 135 | 136 | vkontakte: { 137 | name: 'vkontakte', 138 | url: '/auth/vkontakte', 139 | authorizationEndpoint: 'https://oauth.vk.com/authorize', 140 | redirectUri: window.location.origin + '/', 141 | requiredUrlParams: ['scope'], 142 | scope: ['email'], 143 | scopeDelimiter: ',', 144 | display: 'popup', 145 | oauthType: '2.0', 146 | popupOptions: { width: 580, height: 400 } 147 | }, 148 | 149 | live: { 150 | name: 'live', 151 | url: '/auth/live', 152 | authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', 153 | redirectUri: window.location.origin, 154 | requiredUrlParams: ['display', 'scope'], 155 | scope: ['wl.emails'], 156 | scopeDelimiter: ' ', 157 | display: 'popup', 158 | oauthType: '2.0', 159 | popupOptions: { width: 500, height: 560 } 160 | }, 161 | 162 | oauth1: { 163 | name: null, 164 | url: '/auth/oauth1', 165 | authorizationEndpoint: null, 166 | redirectUri: window.location.origin, 167 | oauthType: '1.0', 168 | popupOptions: null 169 | }, 170 | 171 | oauth2: { 172 | name: null, 173 | url: '/auth/oauth2', 174 | clientId: null, 175 | redirectUri: window.location.origin, 176 | authorizationEndpoint: null, 177 | defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], 178 | requiredUrlParams: null, 179 | optionalUrlParams: null, 180 | scope: null, 181 | scopePrefix: null, 182 | scopeDelimiter: null, 183 | state: null, 184 | oauthType: '2.0', 185 | popupOptions: null, 186 | responseType: 'code', 187 | responseParams: { 188 | code: 'code', 189 | clientId: 'clientId', 190 | redirectUri: 'redirectUri' 191 | } 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/promise.js: -------------------------------------------------------------------------------- 1 | // Store setTimeout reference so promise-polyfill will be unaffected by 2 | // other code modifying setTimeout (like sinon.useFakeTimers()) 3 | var setTimeoutFunc = setTimeout; 4 | 5 | function noop() {} 6 | 7 | // Polyfill for Function.prototype.bind 8 | function bind(fn, thisArg) { 9 | return function () { 10 | fn.apply(thisArg, arguments); 11 | }; 12 | } 13 | 14 | function Promise(fn) { 15 | if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); 16 | if (typeof fn !== 'function') throw new TypeError('not a function'); 17 | this._state = 0; 18 | this._handled = false; 19 | this._value = undefined; 20 | this._deferreds = []; 21 | 22 | doResolve(fn, this); 23 | } 24 | 25 | function handle(self, deferred) { 26 | while (self._state === 3) { 27 | self = self._value; 28 | } 29 | if (self._state === 0) { 30 | self._deferreds.push(deferred); 31 | return; 32 | } 33 | self._handled = true; 34 | Promise._immediateFn(function () { 35 | var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 36 | if (cb === null) { 37 | (self._state === 1 ? resolve : reject)(deferred.promise, self._value); 38 | return; 39 | } 40 | var ret; 41 | try { 42 | ret = cb(self._value); 43 | } catch (e) { 44 | reject(deferred.promise, e); 45 | return; 46 | } 47 | resolve(deferred.promise, ret); 48 | }); 49 | } 50 | 51 | function resolve(self, newValue) { 52 | try { 53 | // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 54 | if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.'); 55 | if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { 56 | var then = newValue.then; 57 | if (newValue instanceof Promise) { 58 | self._state = 3; 59 | self._value = newValue; 60 | finale(self); 61 | return; 62 | } else if (typeof then === 'function') { 63 | doResolve(bind(then, newValue), self); 64 | return; 65 | } 66 | } 67 | self._state = 1; 68 | self._value = newValue; 69 | finale(self); 70 | } catch (e) { 71 | reject(self, e); 72 | } 73 | } 74 | 75 | function reject(self, newValue) { 76 | self._state = 2; 77 | self._value = newValue; 78 | finale(self); 79 | } 80 | 81 | function finale(self) { 82 | if (self._state === 2 && self._deferreds.length === 0) { 83 | Promise._immediateFn(function() { 84 | if (!self._handled) { 85 | Promise._unhandledRejectionFn(self._value); 86 | } 87 | }); 88 | } 89 | 90 | for (var i = 0, len = self._deferreds.length; i < len; i++) { 91 | handle(self, self._deferreds[i]); 92 | } 93 | self._deferreds = null; 94 | } 95 | 96 | function Handler(onFulfilled, onRejected, promise) { 97 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 98 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 99 | this.promise = promise; 100 | } 101 | 102 | /** 103 | * Take a potentially misbehaving resolver function and make sure 104 | * onFulfilled and onRejected are only called once. 105 | * 106 | * Makes no guarantees about asynchrony. 107 | */ 108 | function doResolve(fn, self) { 109 | var done = false; 110 | try { 111 | fn(function (value) { 112 | if (done) return; 113 | done = true; 114 | resolve(self, value); 115 | }, function (reason) { 116 | if (done) return; 117 | done = true; 118 | reject(self, reason); 119 | }); 120 | } catch (ex) { 121 | if (done) return; 122 | done = true; 123 | reject(self, ex); 124 | } 125 | } 126 | 127 | Promise.prototype['catch'] = function (onRejected) { 128 | return this.then(null, onRejected); 129 | }; 130 | 131 | Promise.prototype.then = function (onFulfilled, onRejected) { 132 | var prom = new (this.constructor)(noop); 133 | 134 | handle(this, new Handler(onFulfilled, onRejected, prom)); 135 | return prom; 136 | }; 137 | 138 | Promise.all = function (arr) { 139 | var args = Array.prototype.slice.call(arr); 140 | 141 | return new Promise(function (resolve, reject) { 142 | if (args.length === 0) return resolve([]); 143 | var remaining = args.length; 144 | 145 | function res(i, val) { 146 | try { 147 | if (val && (typeof val === 'object' || typeof val === 'function')) { 148 | var then = val.then; 149 | if (typeof then === 'function') { 150 | then.call(val, function (val) { 151 | res(i, val); 152 | }, reject); 153 | return; 154 | } 155 | } 156 | args[i] = val; 157 | if (--remaining === 0) { 158 | resolve(args); 159 | } 160 | } catch (ex) { 161 | reject(ex); 162 | } 163 | } 164 | 165 | for (var i = 0; i < args.length; i++) { 166 | res(i, args[i]); 167 | } 168 | }); 169 | }; 170 | 171 | Promise.resolve = function (value) { 172 | if (value && typeof value === 'object' && value.constructor === Promise) { 173 | return value; 174 | } 175 | 176 | return new Promise(function (resolve) { 177 | resolve(value); 178 | }); 179 | }; 180 | 181 | Promise.reject = function (value) { 182 | return new Promise(function (resolve, reject) { 183 | reject(value); 184 | }); 185 | }; 186 | 187 | Promise.race = function (values) { 188 | return new Promise(function (resolve, reject) { 189 | for (var i = 0, len = values.length; i < len; i++) { 190 | values[i].then(resolve, reject); 191 | } 192 | }); 193 | }; 194 | 195 | // Use polyfill for setImmediate for performance gains 196 | Promise._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) || 197 | function (fn) { 198 | setTimeoutFunc(fn, 0); 199 | }; 200 | 201 | Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { 202 | if (typeof console !== 'undefined' && console) { 203 | console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console 204 | } 205 | }; 206 | 207 | /** 208 | * Set the immediate function to execute callbacks 209 | * @param fn {function} Function to execute 210 | * @deprecated 211 | */ 212 | Promise._setImmediateFn = function _setImmediateFn(fn) { 213 | Promise._immediateFn = fn; 214 | }; 215 | 216 | /** 217 | * Change the function to execute on unhandled rejection 218 | * @param {function} fn Function to execute on unhandled rejection 219 | * @deprecated 220 | */ 221 | Promise._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) { 222 | Promise._unhandledRejectionFn = fn; 223 | }; 224 | 225 | export default Promise; -------------------------------------------------------------------------------- /src/storage.js: -------------------------------------------------------------------------------- 1 | import CookieStorage from './storage/cookie-storage.js'; 2 | import LocalStorage from './storage/local-storage.js' 3 | import MemoryStorage from './storage/memory-storage.js' 4 | import SessionStorage from './storage/session-storage.js' 5 | 6 | export default function StorageFactory(options) { 7 | switch (options.storageType) { 8 | case 'localStorage': 9 | try { 10 | window.localStorage.setItem('testKey', 'test') 11 | window.localStorage.removeItem('testKey') 12 | return new LocalStorage(options.storageNamespace) 13 | } catch(e) {} 14 | 15 | case 'sessionStorage': 16 | try { 17 | window.sessionStorage.setItem('testKey', 'test') 18 | window.sessionStorage.removeItem('testKey') 19 | return new SessionStorage(options.storageNamespace) 20 | } catch (e) {} 21 | 22 | case 'cookieStorage': 23 | return new CookieStorage(options.cookieStorage); 24 | 25 | case 'memoryStorage': 26 | default: 27 | return new MemoryStorage(options.storageNamespace) 28 | break; 29 | } 30 | } -------------------------------------------------------------------------------- /src/storage/cookie-storage.js: -------------------------------------------------------------------------------- 1 | import { 2 | objectExtend, 3 | formatCookie, 4 | parseCookies 5 | } from '../utils.js'; 6 | 7 | class CookieStorage { 8 | constructor(defaultOptions) { 9 | this._defaultOptions = objectExtend({ 10 | domain: window.location.hostname, 11 | expires: null, 12 | path: '/', 13 | secure: false 14 | }, defaultOptions); 15 | } 16 | 17 | setItem(key, value) { 18 | const options = objectExtend({}, this._defaultOptions); 19 | const cookie = formatCookie(key, value, options); 20 | this._setCookie(cookie); 21 | } 22 | 23 | getItem(key) { 24 | const cookies = parseCookies(this._getCookie()); 25 | return cookies.hasOwnProperty(key) ? cookies[key] : null; 26 | } 27 | 28 | removeItem(key) { 29 | const value = ''; 30 | const defaultOptions = objectExtend({}, this._defaultOptions); 31 | const options = objectExtend(defaultOptions, { 32 | expires: new Date(0) 33 | }); 34 | const cookie = formatCookie(key, value, options); 35 | this._setCookie(cookie); 36 | } 37 | 38 | _getCookie() { 39 | return typeof document === 'undefined' 40 | ? '' : typeof document.cookie === 'undefined' 41 | ? '' : document.cookie; 42 | } 43 | 44 | _setCookie(cookie) { 45 | document.cookie = cookie; 46 | } 47 | } 48 | 49 | export default CookieStorage -------------------------------------------------------------------------------- /src/storage/local-storage.js: -------------------------------------------------------------------------------- 1 | class LocalStorage { 2 | constructor(namespace) { 3 | this.namespace = namespace || null 4 | } 5 | 6 | setItem(key, value) { 7 | window.localStorage.setItem(this._getStorageKey(key), value) 8 | } 9 | 10 | getItem(key) { 11 | return window.localStorage.getItem(this._getStorageKey(key)) 12 | } 13 | 14 | removeItem(key) { 15 | window.localStorage.removeItem(this._getStorageKey(key)) 16 | } 17 | 18 | _getStorageKey(key) { 19 | if (this.namespace) { 20 | return [this.namespace, key].join('.') 21 | } 22 | return key; 23 | } 24 | } 25 | 26 | export default LocalStorage -------------------------------------------------------------------------------- /src/storage/memory-storage.js: -------------------------------------------------------------------------------- 1 | class MemoryStorage { 2 | constructor(namespace) { 3 | this.namespace = namespace || null 4 | this._storage = {} 5 | } 6 | 7 | setItem(key, value) { 8 | this._storage[this._getStorageKey(key)] = value 9 | } 10 | 11 | getItem(key) { 12 | return this._storage[this._getStorageKey(key)] 13 | } 14 | 15 | removeItem(key) { 16 | delete this._storage[this._getStorageKey(key)] 17 | } 18 | 19 | _getStorageKey(key) { 20 | if (this.namespace) { 21 | return [this.namespace, key].join('.') 22 | } 23 | return key; 24 | } 25 | } 26 | 27 | export default MemoryStorage -------------------------------------------------------------------------------- /src/storage/session-storage.js: -------------------------------------------------------------------------------- 1 | class LocalStorage { 2 | constructor(namespace) { 3 | this.namespace = namespace || null 4 | } 5 | 6 | setItem(key, value) { 7 | window.sessionStorage.setItem(this._getStorageKey(key), value) 8 | } 9 | 10 | getItem(key) { 11 | return window.sessionStorage.getItem(this._getStorageKey(key)) 12 | } 13 | 14 | removeItem(key) { 15 | window.sessionStorage.removeItem(this._getStorageKey(key)) 16 | } 17 | 18 | _getStorageKey(key) { 19 | if (this.namespace) { 20 | return [this.namespace, key].join('.') 21 | } 22 | return key; 23 | } 24 | } 25 | 26 | export default LocalStorage -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | if (typeof Object.assign != 'function') { 2 | Object.assign = function(target, varArgs) { 3 | 'use strict'; 4 | if (target == null) { 5 | throw new TypeError('Cannot convert undefined or null to object'); 6 | } 7 | 8 | var to = Object(target); 9 | 10 | for (var index = 1; index < arguments.length; index++) { 11 | var nextSource = arguments[index]; 12 | 13 | if (nextSource != null) { // Skip over if undefined or null 14 | for (var nextKey in nextSource) { 15 | // Avoid bugs when hasOwnProperty is shadowed 16 | if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 17 | to[nextKey] = nextSource[nextKey]; 18 | } 19 | } 20 | } 21 | } 22 | return to; 23 | }; 24 | } 25 | 26 | export function camelCase(name) { 27 | return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { 28 | return offset ? letter.toUpperCase() : letter; 29 | }); 30 | } 31 | 32 | export function isUndefined(value) { 33 | return typeof value === 'undefined' 34 | } 35 | 36 | export function isDefined(value) { 37 | return typeof value !== 'undefined' 38 | } 39 | 40 | export function isObject(value) { 41 | return value !== null && typeof value === 'object' 42 | } 43 | 44 | export function isString(value) { 45 | return typeof value === 'string' 46 | } 47 | 48 | export function isNumber(value) { 49 | return typeof value === 'number' 50 | } 51 | 52 | export function isFunction(value) { 53 | return typeof value === 'function' 54 | } 55 | 56 | export function objectExtend(a, b) { 57 | 58 | // Don't touch 'null' or 'undefined' objects. 59 | if (a == null || b == null) { 60 | return a; 61 | } 62 | 63 | Object.keys(b).forEach(function (key) { 64 | if (Object.prototype.toString.call(b[key]) == '[object Object]') { 65 | if (Object.prototype.toString.call(a[key]) != '[object Object]') { 66 | a[key] = b[key]; 67 | } else { 68 | a[key] = objectExtend(a[key], b[key]); 69 | } 70 | } else { 71 | a[key] = b[key]; 72 | } 73 | }); 74 | 75 | return a; 76 | }; 77 | 78 | /** 79 | * Assemble url from two segments 80 | * 81 | * @author Sahat Yalkabov 82 | * @copyright Method taken from https://github.com/sahat/satellizer 83 | * 84 | * @param {String} baseUrl Base url 85 | * @param {String} url URI 86 | * @return {String} 87 | */ 88 | export function joinUrl(baseUrl, url) { 89 | if (/^(?:[a-z]+:)?\/\//i.test(url)) { 90 | return url; 91 | } 92 | let joined = [baseUrl, url].join('/'); 93 | let normalize = function (str) { 94 | return str 95 | .replace(/[\/]+/g, '/') 96 | .replace(/\/\?/g, '?') 97 | .replace(/\/\#/g, '#') 98 | .replace(/\:\//g, '://'); 99 | }; 100 | return normalize(joined); 101 | } 102 | 103 | /** 104 | * Get full path based on current location 105 | * 106 | * @author Sahat Yalkabov 107 | * @copyright Method taken from https://github.com/sahat/satellizer 108 | * 109 | * @param {Location} location 110 | * @return {String} 111 | */ 112 | export function getFullUrlPath(location) { 113 | const isHttps = location.protocol === 'https:'; 114 | return location.protocol + '//' + location.hostname + 115 | ':' + (location.port || (isHttps ? '443' : '80')) + 116 | (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); 117 | } 118 | 119 | /** 120 | * Parse query string variables 121 | * 122 | * @author Sahat Yalkabov 123 | * @copyright Method taken from https://github.com/sahat/satellizer 124 | * 125 | * @param {String} Query string 126 | * @return {String} 127 | */ 128 | export function parseQueryString(str) { 129 | let obj = {}; 130 | let key; 131 | let value; 132 | (str || '').split('&').forEach((keyValue) => { 133 | if (keyValue) { 134 | value = keyValue.split('='); 135 | key = decodeURIComponent(value[0]); 136 | obj[key] = (!!value[1]) ? decodeURIComponent(value[1]) : true; 137 | } 138 | }); 139 | return obj; 140 | } 141 | 142 | /** 143 | * Decode base64 string 144 | * @author Sahat Yalkabov 145 | * @copyright Method taken from https://github.com/sahat/satellizer 146 | * 147 | * @param {String} str base64 encoded string 148 | * @return {Object} 149 | */ 150 | export function decodeBase64(str) { 151 | let buffer; 152 | if (typeof module !== 'undefined' && module.exports) { 153 | try { 154 | buffer = require('buffer').Buffer; 155 | } catch (err) { 156 | // noop 157 | } 158 | } 159 | 160 | let fromCharCode = String.fromCharCode; 161 | 162 | let re_btou = new RegExp([ 163 | '[\xC0-\xDF][\x80-\xBF]', 164 | '[\xE0-\xEF][\x80-\xBF]{2}', 165 | '[\xF0-\xF7][\x80-\xBF]{3}' 166 | ].join('|'), 'g'); 167 | 168 | let cb_btou = function (cccc) { 169 | switch (cccc.length) { 170 | case 4: 171 | let cp = ((0x07 & cccc.charCodeAt(0)) << 18) 172 | | ((0x3f & cccc.charCodeAt(1)) << 12) 173 | | ((0x3f & cccc.charCodeAt(2)) << 6) 174 | | (0x3f & cccc.charCodeAt(3)); 175 | let offset = cp - 0x10000; 176 | return (fromCharCode((offset >>> 10) + 0xD800) 177 | + fromCharCode((offset & 0x3FF) + 0xDC00)); 178 | case 3: 179 | return fromCharCode( 180 | ((0x0f & cccc.charCodeAt(0)) << 12) 181 | | ((0x3f & cccc.charCodeAt(1)) << 6) 182 | | (0x3f & cccc.charCodeAt(2)) 183 | ); 184 | default: 185 | return fromCharCode( 186 | ((0x1f & cccc.charCodeAt(0)) << 6) 187 | | (0x3f & cccc.charCodeAt(1)) 188 | ); 189 | } 190 | }; 191 | 192 | let btou = function (b) { 193 | return b.replace(re_btou, cb_btou); 194 | }; 195 | 196 | let _decode = buffer ? function (a) { 197 | return (a.constructor === buffer.constructor 198 | ? a : new buffer(a, 'base64')).toString(); 199 | } 200 | : function (a) { 201 | return btou(atob(a)); 202 | }; 203 | 204 | return _decode( 205 | String(str).replace(/[-_]/g, function (m0) { 206 | return m0 === '-' ? '+' : '/'; 207 | }) 208 | .replace(/[^A-Za-z0-9\+\/]/g, '') 209 | ); 210 | } 211 | 212 | export function parseCookies(str) { 213 | if (str.length === 0) return {}; 214 | const parsed = {}; 215 | const pattern = new RegExp('\\s*;\\s*'); 216 | str.split(pattern).forEach((i) => { 217 | const [encodedKey, encodedValue] = i.split('='); 218 | const key = decodeURIComponent(encodedKey); 219 | const value = decodeURIComponent(encodedValue); 220 | parsed[key] = value; 221 | }); 222 | return parsed; 223 | }; 224 | 225 | export function formatOptions(options) { 226 | const { path, domain, expires, secure } = options; 227 | return [ 228 | typeof path === 'undefined' || path === null 229 | ? '' : ';path=' + path, 230 | typeof domain === 'undefined' || domain === null 231 | ? '' : ';domain=' + domain, 232 | typeof expires === 'undefined' || expires === null 233 | ? '' : ';expires=' + expires.toUTCString(), 234 | typeof secure === 'undefined' || secure === null || secure === false 235 | ? '' : ';secure' 236 | ].join(''); 237 | }; 238 | 239 | export function formatCookie(key, value, options) { 240 | return [ 241 | encodeURIComponent(key), 242 | '=', 243 | encodeURIComponent(value), 244 | formatOptions(options) 245 | ].join(''); 246 | }; 247 | --------------------------------------------------------------------------------