| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  | /* global Promise */ | 
					
						
							|  |  |  | ;(function (exports) { | 
					
						
							|  |  |  |   'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var OAUTH3 = exports.OAUTH3 = { | 
					
						
							|  |  |  |     utils: { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       clientUri: function (location) { | 
					
						
							|  |  |  |         return OAUTH3.utils.uri.normalize(location.host + location.pathname); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |     , _formatError: function (providerUri, params) { | 
					
						
							|  |  |  |         var err = new Error(params.error_description || params.error.message || "Unknown error with provider '" + providerUri + "'"); | 
					
						
							|  |  |  |         err.uri = params.error_uri || params.error.uri; | 
					
						
							|  |  |  |         err.code = params.error.code || params.error; | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |     , atob: function (base64) { | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |         return (exports.atob || require('atob'))(base64); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |     , _urlSafeBase64ToBase64: function (b64) { | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |         // URL-safe Base64 to Base64
 | 
					
						
							|  |  |  |         // https://en.wikipedia.org/wiki/Base64
 | 
					
						
							|  |  |  |         // https://gist.github.com/catwell/3046205
 | 
					
						
							|  |  |  |         var mod = b64.length % 4; | 
					
						
							|  |  |  |         if (2 === mod) { b64 += '=='; } | 
					
						
							|  |  |  |         if (3 === mod) { b64 += '='; } | 
					
						
							|  |  |  |         b64 = b64.replace(/-/g, '+').replace(/_/g, '/'); | 
					
						
							|  |  |  |         return b64; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , uri: { | 
					
						
							|  |  |  |         normalize: function (uri) { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |           if ('string' !== typeof uri) { | 
					
						
							|  |  |  |             console.error((new Error('stack')).stack); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           // tested with
 | 
					
						
							|  |  |  |           //   example.com
 | 
					
						
							|  |  |  |           //   example.com/
 | 
					
						
							|  |  |  |           //   http://example.com
 | 
					
						
							|  |  |  |           //   https://example.com/
 | 
					
						
							|  |  |  |           return uri | 
					
						
							|  |  |  |             .replace(/^(https?:\/\/)?/i, '') | 
					
						
							|  |  |  |             .replace(/\/?$/, '') | 
					
						
							|  |  |  |             ; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , url: { | 
					
						
							|  |  |  |         normalize: function (url) { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |           if ('string' !== typeof url) { | 
					
						
							|  |  |  |             console.error((new Error('stack')).stack); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           // tested with
 | 
					
						
							|  |  |  |           //   example.com
 | 
					
						
							|  |  |  |           //   example.com/
 | 
					
						
							|  |  |  |           //   http://example.com
 | 
					
						
							|  |  |  |           //   https://example.com/
 | 
					
						
							|  |  |  |           return url | 
					
						
							|  |  |  |             .replace(/^(https?:\/\/)?/i, 'https://') | 
					
						
							|  |  |  |             .replace(/\/?$/, '') | 
					
						
							|  |  |  |             ; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |       , resolve: function (base, next) { | 
					
						
							|  |  |  |           if (/^https:\/\//i.test(next)) { | 
					
						
							|  |  |  |             return next; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return this.normalize(base) + '/' + this._normalizePath(next); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , _normalizePath: function (path) { | 
					
						
							|  |  |  |           return path.replace(/^\//, '').replace(/\/$/, ''); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     , query: { | 
					
						
							|  |  |  |         stringify: function (params) { | 
					
						
							|  |  |  |           var qs = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           Object.keys(params).forEach(function (key) { | 
					
						
							|  |  |  |             // TODO nullify instead?
 | 
					
						
							|  |  |  |             if ('undefined' === typeof params[key]) { | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ('scope' === key) { | 
					
						
							|  |  |  |               params[key] = OAUTH3.utils.scope.stringify(params[key]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             qs.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return qs.join('&'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , scope: { | 
					
						
							|  |  |  |         stringify: function (scope) { | 
					
						
							|  |  |  |           if (Array.isArray(scope)) { | 
					
						
							|  |  |  |             scope = scope.join(' '); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return scope; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , randomState: function () { | 
					
						
							|  |  |  |         // TODO put in different file for browser vs node
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           return Array.prototype.slice.call( | 
					
						
							|  |  |  |             window.crypto.getRandomValues(new Uint8Array(16)) | 
					
						
							|  |  |  |           ).map(function (ch) { return (ch).toString(16); }).join(''); | 
					
						
							|  |  |  |         } catch(e) { | 
					
						
							|  |  |  |           return OAUTH3.utils._insecureRandomState(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , _insecureRandomState: function () { | 
					
						
							|  |  |  |         var i; | 
					
						
							|  |  |  |         var ch; | 
					
						
							|  |  |  |         var str; | 
					
						
							|  |  |  |         // TODO use fisher-yates on 0..255 and select [0] 16 times
 | 
					
						
							|  |  |  |         // [security] https://medium.com/@betable/tifu-by-using-math-random-f1c308c4fd9d#.5qx0bf95a
 | 
					
						
							|  |  |  |         // https://github.com/v8/v8/blob/b0e4dce6091a8777bda80d962df76525dc6c5ea9/src/js/math.js#L135-L144
 | 
					
						
							|  |  |  |         // Note: newer versions of v8 do not have this bug, but other engines may still
 | 
					
						
							|  |  |  |         console.warn('[security] crypto.getRandomValues() failed, falling back to Math.random()'); | 
					
						
							|  |  |  |         str = ''; | 
					
						
							|  |  |  |         for (i = 0; i < 32; i += 1) { | 
					
						
							|  |  |  |           ch = Math.round(Math.random() * 255).toString(16); | 
					
						
							|  |  |  |           if (ch.length < 2) { ch = '0' + ch; } | 
					
						
							|  |  |  |           str += ch; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return str; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |   , jwt: { | 
					
						
							|  |  |  |       // decode only (no verification)
 | 
					
						
							|  |  |  |       decode: function (str) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 'abc.qrs.xyz'
 | 
					
						
							|  |  |  |         // [ 'abc', 'qrs', 'xyz' ]
 | 
					
						
							|  |  |  |         // [ {}, {}, 'foo' ]
 | 
					
						
							|  |  |  |         // { header: {}, payload: {}, signature: }
 | 
					
						
							|  |  |  |         var parts = str.split(/\./g); | 
					
						
							|  |  |  |         var jsons = parts.slice(0, 2).map(function (urlsafe64) { | 
					
						
							|  |  |  |           var atob = exports.atob || require('atob'); | 
					
						
							|  |  |  |           var b64 = OAUTH3.utils._urlSafeBase64ToBase64(urlsafe64); | 
					
						
							|  |  |  |           return atob(b64); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           header: JSON.parse(jsons[0]) | 
					
						
							|  |  |  |         , payload: JSON.parse(jsons[1]) | 
					
						
							|  |  |  |         , signature: parts[2] // should remain url-safe base64
 | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-14 16:19:46 -07:00
										 |  |  |     , freshness: function (tokenMeta, staletime, _now) { | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |         staletime = staletime || (15 * 60); | 
					
						
							| 
									
										
										
										
											2017-02-14 16:19:46 -07:00
										 |  |  |         var now = _now || Date.now(); | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |         var fresh = ((parseInt(tokenMeta.exp, 10) || 0) - Math.round(now / 1000)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (fresh >= staletime) { | 
					
						
							|  |  |  |           return 'fresh'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (fresh <= 0) { | 
					
						
							|  |  |  |           return 'expired'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return 'stale'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |       // encode-only (no signature)
 | 
					
						
							|  |  |  |     , encode: function (parts) { | 
					
						
							|  |  |  |         parts.header = parts.header || { alg: 'none', typ: 'jwt' }; | 
					
						
							|  |  |  |         parts.signature = parts.signature || ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var btoa = exports.btoa || require('btoa'); | 
					
						
							|  |  |  |         var result = [ | 
					
						
							|  |  |  |           core.utils.base64ToUrlSafeBase64(btoa(JSON.stringify(parts.header, null))) | 
					
						
							|  |  |  |         , core.utils.base64ToUrlSafeBase64(btoa(JSON.stringify(parts.payload, null))) | 
					
						
							|  |  |  |         , parts.signature // should already be url-safe base64
 | 
					
						
							|  |  |  |         ].join('.'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |   , urls: { | 
					
						
							|  |  |  |       discover: function (providerUri, opts) { | 
					
						
							|  |  |  |         if (!providerUri) { | 
					
						
							|  |  |  |           throw new Error("cannot discover without providerUri"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!opts.client_id) { | 
					
						
							|  |  |  |           throw new Error("cannot discover without options.client_id"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         var clientId = OAUTH3.utils.url.normalize(opts.client_id || opts.client_uri); | 
					
						
							|  |  |  |         providerUri = OAUTH3.utils.url.normalize(providerUri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var params = { | 
					
						
							|  |  |  |           action: 'directives' | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |         , state: opts.state || OAUTH3.utils.randomState() | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |         , redirect_uri: clientId + (opts.client_callback_path || '/.well-known/oauth3/callback.html#/') | 
					
						
							|  |  |  |         , response_type: 'rpc' | 
					
						
							|  |  |  |         , _method: 'GET' | 
					
						
							|  |  |  |         , _pathname: '.well-known/oauth3/directives.json' | 
					
						
							|  |  |  |         , debug: opts.debug || undefined | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var result = { | 
					
						
							|  |  |  |           url: providerUri + '/.well-known/oauth3/#/?' + OAUTH3.utils.query.stringify(params) | 
					
						
							|  |  |  |         , state: params.state | 
					
						
							|  |  |  |         , method: 'GET' | 
					
						
							|  |  |  |         , query: params | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |         return result; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , implicitGrant: function (directive, opts) { | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // Example Implicit Grant Request
 | 
					
						
							|  |  |  |         // (for generating a browser-only session, not a session on your server)
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // GET https://example.com/api/org.oauth3.provider/authorization_dialog
 | 
					
						
							|  |  |  |         //  ?response_type=token
 | 
					
						
							|  |  |  |         //  &scope=`encodeURIComponent('profile.login profile.email')`
 | 
					
						
							|  |  |  |         //  &state=`cryptoutil.random().toString('hex')`
 | 
					
						
							|  |  |  |         //  &client_id=xxxxxxxxxxx
 | 
					
						
							|  |  |  |         //  &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // NOTE: `redirect_uri` itself may also contain URI-encoded components
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         opts = opts || {}; | 
					
						
							|  |  |  |         var type = 'authorization_dialog'; | 
					
						
							|  |  |  |         var responseType = 'token'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var scope = opts.scope || directive.authn_scope; | 
					
						
							|  |  |  |         var args = directive[type]; | 
					
						
							|  |  |  |         var uri = args.url; | 
					
						
							|  |  |  |         var state = opts.state || OAUTH3.utils.randomState(); | 
					
						
							|  |  |  |         var params = { | 
					
						
							|  |  |  |           debug: opts.debug || undefined | 
					
						
							|  |  |  |         , client_uri: opts.client_uri || opts.clientUri || undefined | 
					
						
							|  |  |  |         , client_id: opts.client_id || opts.client_uri || undefined | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         var result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         params.state = state; | 
					
						
							|  |  |  |         params.response_type = responseType; | 
					
						
							|  |  |  |         if (scope) { | 
					
						
							|  |  |  |           params.scope = OAUTH3.utils.scope.stringify(scope); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!opts.redirect_uri) { | 
					
						
							|  |  |  |           // TODO consider making this optional
 | 
					
						
							| 
									
										
										
										
											2017-02-14 15:06:38 -07:00
										 |  |  |           //console.warn("auto-generating redirect_uri from hard-coded callback.html"
 | 
					
						
							|  |  |  |           //  + " (should be configurable... but then redirect_uri could just be manually-generated)");
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |           opts.redirect_uri = OAUTH3.utils.url.resolve( | 
					
						
							|  |  |  |             OAUTH3.utils.url.normalize(params.client_uri) | 
					
						
							|  |  |  |           , '.well-known/oauth3/callback.html' | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         params.redirect_uri = opts.redirect_uri; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         uri += '?' + OAUTH3.utils.query.stringify(params); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result = { | 
					
						
							|  |  |  |           url: uri | 
					
						
							|  |  |  |         , state: state | 
					
						
							|  |  |  |         , method: args.method | 
					
						
							|  |  |  |         , query: params | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |         return result; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |     , refreshToken: function (directive, opts) { | 
					
						
							|  |  |  |         // grant_type=refresh_token
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Example Refresh Token Request
 | 
					
						
							|  |  |  |         // (generally for 1st or 3rd party server-side, mobile, and desktop apps)
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // POST https://example.com/api/oauth3/access_token
 | 
					
						
							|  |  |  |         //    { "grant_type": "refresh_token", "client_id": "<<id>>", "scope": "<<scope>>"
 | 
					
						
							|  |  |  |         //    , "username": "<<username>>", "password": "password" }
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         opts = opts || {}; | 
					
						
							|  |  |  |         var type = 'access_token'; | 
					
						
							|  |  |  |         var grantType = 'refresh_token'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var scope = opts.scope || directive.authn_scope; | 
					
						
							|  |  |  |         var clientSecret = opts.client_secret; | 
					
						
							|  |  |  |         var args = directive[type]; | 
					
						
							|  |  |  |         var params = { | 
					
						
							|  |  |  |           "grant_type": grantType | 
					
						
							|  |  |  |         , "refresh_token": opts.refresh_token || (opts.session && opts.session.refresh_token) | 
					
						
							|  |  |  |         , "response_type": 'token' | 
					
						
							|  |  |  |         , "client_id": opts.client_id || opts.client_uri | 
					
						
							|  |  |  |         , "client_uri": opts.client_uri | 
					
						
							|  |  |  |         //, "scope": undefined
 | 
					
						
							|  |  |  |         //, "client_secret": undefined
 | 
					
						
							|  |  |  |         , debug: opts.debug || undefined | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         var uri = args.url; | 
					
						
							|  |  |  |         var body; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (clientSecret) { | 
					
						
							|  |  |  |           // TODO not allowed in the browser
 | 
					
						
							|  |  |  |           console.warn("if this is a browser, you must not use client_secret"); | 
					
						
							|  |  |  |           params.client_secret = clientSecret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (scope) { | 
					
						
							|  |  |  |           params.scope = OAUTH3.utils.scope.stringify(scope); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ('GET' === args.method.toUpperCase()) { | 
					
						
							|  |  |  |           uri += '?' + OAUTH3.utils.query.stringify(params); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           body = params; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           url: uri | 
					
						
							|  |  |  |         , method: args.method | 
					
						
							|  |  |  |         , data: body | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   , hooks: { | 
					
						
							|  |  |  |       directives: { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |         _get: function (providerUri) { | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           providerUri = OAUTH3.utils.uri.normalize(providerUri); | 
					
						
							|  |  |  |           if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |           return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives._cache[providerUri] || this.get(providerUri)) | 
					
						
							|  |  |  |             .then(function (directives) { | 
					
						
							|  |  |  |               // or do .then(this._set) to keep DRY?
 | 
					
						
							|  |  |  |             OAUTH3.hooks.directives._cache[providerUri] = directives; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , _getCached: function (providerUri) { | 
					
						
							|  |  |  |           return OAUTH3.hooks.directives._cache[providerUri]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , get: function (providerUri) { | 
					
						
							|  |  |  |           console.warn('[Warn] You should implement: OAUTH3.hooks.directives.get = function (providerUri) { return directives; }'); | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           return JSON.parse(window.localStorage.getItem('directives-' + providerUri) || '{}'); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |       , _set: function (providerUri, directives) { | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           providerUri = OAUTH3.utils.uri.normalize(providerUri); | 
					
						
							|  |  |  |           if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; } | 
					
						
							|  |  |  |           OAUTH3.hooks.directives._cache[providerUri] = directives; | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |           return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives.set(providerUri, directives)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , set: function (providerUri, directives) { | 
					
						
							|  |  |  |           console.warn('[Warn] You should implement: OAUTH3.hooks.directives.set = function (providerUri, directives) { return directives; }'); | 
					
						
							|  |  |  |           window.localStorage.setItem('directives-' + providerUri, JSON.stringify(directives)); | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           return directives; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |     , session: { | 
					
						
							|  |  |  |         refresh: function (oldSession, newSession) { | 
					
						
							|  |  |  |           var providerUri = oldSession.provider_uri; | 
					
						
							|  |  |  |           var clientUri = oldSession.client_uri; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           Object.keys(oldSession).forEach(function (key) { | 
					
						
							|  |  |  |             oldSession[key] = undefined; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           Object.keys(newSession).forEach(function (key) { | 
					
						
							|  |  |  |             oldSession[key] = newSession[key]; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // info about the session of this API call
 | 
					
						
							|  |  |  |           oldSession.provider_uri = providerUri;  // aud
 | 
					
						
							|  |  |  |           oldSession.client_uri = clientUri;      // azp
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // info about the newly-discovered token
 | 
					
						
							|  |  |  |           oldSession.token = OAUTH3.jwt.decode(oldSession.access_token).payload; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           oldSession.token.sub = oldSession.token.sub || oldSession.token.acx.id; | 
					
						
							|  |  |  |           oldSession.token.client_uri = clientUri; | 
					
						
							|  |  |  |           oldSession.token.provider_uri = providerUri; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (oldSession.refresh_token) { | 
					
						
							|  |  |  |             oldSession.refresh = OAUTH3.jwt.decode(oldSession.refresh_token).payload; | 
					
						
							|  |  |  |             oldSession.refresh.sub = oldSession.refresh.sub || oldSession.refresh.acx.id; | 
					
						
							|  |  |  |             oldSession.refresh.provider_uri = providerUri; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // set for a set of audiences
 | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session.set(providerUri, oldSession)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , check: function (preq, opts) { | 
					
						
							|  |  |  |           if (!preq.session) { | 
					
						
							|  |  |  |             return OAUTH3.PromiseA.resolve(null); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           var freshness = OAUTH3.jwt.freshness(preq.session.token, opts.staletime); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           switch (freshness) { | 
					
						
							|  |  |  |             case 'stale': | 
					
						
							|  |  |  |               return OAUTH3.hooks.session.stale(preq.session); | 
					
						
							|  |  |  |             case 'expired': | 
					
						
							|  |  |  |               return OAUTH3.hooks.session.expired(preq.session).then(function (newSession) { | 
					
						
							|  |  |  |                 preq.session = newSession; | 
					
						
							|  |  |  |                 return newSession; | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             //case 'fresh':
 | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |               return OAUTH3.PromiseA.resolve(preq.session); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , stale: function (staleSession) { | 
					
						
							|  |  |  |           if (OAUTH3.hooks.session._stalePromise) { | 
					
						
							|  |  |  |             return OAUTH3.PromiseA.resolve(staleSession); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           OAUTH3.hooks.session._stalePromise = OAUTH3._refreshToken( | 
					
						
							|  |  |  |             staleSession.provider_uri | 
					
						
							|  |  |  |           , { client_uri: staleSession.client_uri | 
					
						
							|  |  |  |             , session: staleSession | 
					
						
							|  |  |  |             , debug: staleSession.debug | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ).then(function (newSession) { | 
					
						
							|  |  |  |             OAUTH3.hooks.session._stalePromise = null; | 
					
						
							|  |  |  |             return newSession; // oauth3.hooks.refreshSession(staleSession, newSession);
 | 
					
						
							|  |  |  |           }, function () { | 
					
						
							|  |  |  |             OAUTH3.hooks.session._stalePromise = null; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.resolve(staleSession); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , expired: function (expiredSession) { | 
					
						
							|  |  |  |           return OAUTH3._refreshToken( | 
					
						
							|  |  |  |             expiredSession.provider_uri | 
					
						
							|  |  |  |           , { client_uri: expiredSession.client_uri | 
					
						
							|  |  |  |             , session: expiredSession | 
					
						
							|  |  |  |             , debug: expiredSession.debug | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ).then(function (newSession) { | 
					
						
							|  |  |  |             return newSession; // oauth3.hooks.refreshSession(expiredSession, newSession);
 | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , set: function (providerUri, newSession) { | 
					
						
							|  |  |  |           if (!providerUri) { | 
					
						
							|  |  |  |             console.error(new Error('no providerUri').stack); | 
					
						
							|  |  |  |             throw new Error("providerUri is not set"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           providerUri = OAUTH3.utils.uri.normalize(providerUri); | 
					
						
							|  |  |  |           console.warn('[Warn] Please implement OAUTH3.hooks.session.set = function (providerUri, newSession) { return PromiseA<newSession>; }'); | 
					
						
							|  |  |  |           if (!OAUTH3.hooks.session._sessions) { OAUTH3.hooks.session._sessions = {}; } | 
					
						
							|  |  |  |           OAUTH3.hooks.session._sessions[providerUri] = newSession; | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.resolve(newSession); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , get: function (providerUri) { | 
					
						
							|  |  |  |           providerUri = OAUTH3.utils.uri.normalize(providerUri); | 
					
						
							|  |  |  |           if (!providerUri) { | 
					
						
							|  |  |  |             throw new Error("providerUri is not set"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           console.warn('[Warn] Please implement OAUTH3.hooks.session.get = function (providerUri) { return PromiseA<savedSession>; }'); | 
					
						
							|  |  |  |           if (!OAUTH3.hooks.session._sessions) { OAUTH3.hooks.session._sessions = {}; } | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session._sessions[providerUri]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   , discover: function (providerUri, opts) { | 
					
						
							|  |  |  |       if (!providerUri) { | 
					
						
							|  |  |  |         throw new Error('oauth3.discover(providerUri, opts) received providerUri as ' + providerUri); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives.get(providerUri)).then(function (directives) { | 
					
						
							|  |  |  |         if (directives && directives.issuer) { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |           return directives; | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |         return OAUTH3._discoverHelper(providerUri, opts).then(function (directives) { | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |           directives.issuer = directives.issuer || OAUTH3.utils.url.normalize(providerUri); | 
					
						
							|  |  |  |           // OAUTH3.PromiseA.resolve() is taken care of because this is wrapped
 | 
					
						
							|  |  |  |           return OAUTH3.hooks.directives.set(providerUri, directives); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |   , _discoverHelper: function(providerUri, opts) { | 
					
						
							|  |  |  |       return OAUTH3._discover(providerUri, opts); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |   , _discover: function(providerUri, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |       opts = opts || {}; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       providerUri = OAUTH3.utils.url.normalize(providerUri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (providerUri.match(OAUTH3._browser.window.location.hostname)) { | 
					
						
							|  |  |  |         console.warn("It looks like you're a provider checking for your own directive," | 
					
						
							|  |  |  |           + " so we we're just gonna use" | 
					
						
							|  |  |  |           + " OAUTH3.request({ method: 'GET', url: '.well-known/oauth3/directive.json' })"); | 
					
						
							|  |  |  |         return OAUTH3.request({ | 
					
						
							|  |  |  |           method: 'GET' | 
					
						
							|  |  |  |         , url: OAUTH3.utils.url.normalize(providerUri) + '/.well-known/oauth3/directives.json' | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!(opts.client_id || opts.client_uri).match(OAUTH3._browser.window.location.hostname)) { | 
					
						
							|  |  |  |         console.warn("It looks like your client_id doesn't match your current window..." | 
					
						
							|  |  |  |           + " this probably won't end well"); | 
					
						
							|  |  |  |         console.warn(opts.client_id || opts.client_uri, OAUTH3._browser.window.location.hostname); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var discReq = OAUTH3.urls.discover( | 
					
						
							|  |  |  |         providerUri | 
					
						
							|  |  |  |       , { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location)) | 
					
						
							|  |  |  |         , windowType: opts.broker && opts.windowType || 'background' | 
					
						
							|  |  |  |         , broker: opts.broker | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |         , state: opts._state || undefined | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         , debug: opts.debug | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       opts._state = discReq.state; | 
					
						
							|  |  |  |       //var discReq = OAUTH3.urls.discover(providerUri, opts);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // hmm... we're gonna need a broker for this since switching windows is distracting,
 | 
					
						
							|  |  |  |       // popups are obnoxious, iframes are sometimes blocked, and most servers don't implement CORS
 | 
					
						
							|  |  |  |       // eventually it should be the browser (and postMessage may be a viable option now), but whatever...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO allow postMessage from providerUri in addition to callback
 | 
					
						
							|  |  |  |       // TODO allow node to open a desktop browser window
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |       opts._windowType = opts.windowType; | 
					
						
							|  |  |  |       opts.windowType = opts.windowType || 'background'; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       return OAUTH3._browser.frameRequest( | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |         OAUTH3.utils.url.resolve(providerUri, discReq.url) | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       , discReq.state | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |         // why not just pass opts whole?
 | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       , { windowType: opts.windowType | 
					
						
							|  |  |  |         , reuseWindow: opts.broker && '-broker' | 
					
						
							|  |  |  |         , debug: opts.debug | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ).then(function (params) { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |         opts.windowType = opts._windowType; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // caller will call OAUTH3._browser.closeFrame(discReq.state, { debug: opts.debug || params.debug });
 | 
					
						
							|  |  |  |         if (params.error) { | 
					
						
							|  |  |  |           // TODO directives.issuer || directives.audience
 | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(providerUri, params)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         // TODO params should have response_type indicating json, binary, etc
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         var directives = JSON.parse(OAUTH3.utils.atob(OAUTH3.utils._urlSafeBase64ToBase64(params.result || params.directives))); | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |         // caller will call OAUTH3.hooks.directives.set(providerUri, directives);
 | 
					
						
							|  |  |  |         return directives; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |   , request: function (preq, opts) { | 
					
						
							|  |  |  |       function fetch() { | 
					
						
							|  |  |  |         if (preq.session) { | 
					
						
							|  |  |  |           // TODO check session.token.aud against preq.url to make sure they match
 | 
					
						
							|  |  |  |           console.warn("[security] session audience checking has not been implemented yet (it's up to you to check)"); | 
					
						
							|  |  |  |           preq.headers = preq.headers || {}; | 
					
						
							|  |  |  |           preq.headers.Authorization = 'Bearer ' + (preq.session.access_token || preq.session.accessToken); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return OAUTH3._requestHelper(preq, opts); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!preq.session) { | 
					
						
							|  |  |  |         return fetch(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return OAUTH3.hooks.session.check(preq, opts).then(fetch); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , _requestHelper: function (preq, opts) { | 
					
						
							|  |  |  |       return OAUTH3._browser.request(preq, opts); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , implicitGrant: function(providerUri, opts) { | 
					
						
							|  |  |  |       var promise; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (opts.broker) { | 
					
						
							|  |  |  |         // Discovery can happen in-flow because we know that this is
 | 
					
						
							|  |  |  |         // a valid oauth3 provider
 | 
					
						
							|  |  |  |         promise = OAUTH3._discoverThenImplicitGrant(providerUri, opts); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							|  |  |  |         // Discovery must take place before calling implicitGrant
 | 
					
						
							|  |  |  |         promise = OAUTH3._implicitGrant(OAUTH3.hooks.directives._getCached(providerUri), opts); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return promise.then(function (tokens) { | 
					
						
							|  |  |  |         // TODO abstract browser bits away
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           OAUTH3._browser.closeFrame(tokens.state || opts._state, opts); | 
					
						
							|  |  |  |         } catch(e) { | 
					
						
							|  |  |  |           console.warn("[implicitGrant] TODO abstract browser bits away"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         opts._state = undefined; | 
					
						
							|  |  |  |         return OAUTH3.hooks.session.refresh( | 
					
						
							|  |  |  |           opts.session || { | 
					
						
							|  |  |  |             provider_uri: providerUri | 
					
						
							|  |  |  |           , client_id: opts.client_id | 
					
						
							|  |  |  |           , client_uri: opts.client_uri || opts.clientUri | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         , tokens | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , _discoverThenImplicitGrant: function(providerUri, opts) { | 
					
						
							|  |  |  |       opts.windowType = opts.windowType || 'popup'; | 
					
						
							|  |  |  |       return OAUTH3.discover(providerUri, opts).then(function (directives) { | 
					
						
							|  |  |  |         return OAUTH3._implicitGrant(directives, opts).then(function (tokens) { | 
					
						
							|  |  |  |           return tokens; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |   , _implicitGrant: function(directives, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       // TODO this may need to be synchronous for browser security policy
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |       // Do some stuff
 | 
					
						
							|  |  |  |       var authReq = OAUTH3.urls.implicitGrant( | 
					
						
							|  |  |  |         directives | 
					
						
							|  |  |  |       , { redirect_uri: opts.redirect_uri | 
					
						
							|  |  |  |         , client_id: opts.client_id || opts.client_uri | 
					
						
							|  |  |  |         , client_uri: opts.client_uri || opts.client_id | 
					
						
							|  |  |  |         , state: opts._state || undefined | 
					
						
							|  |  |  |         , debug: opts.debug | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |       ); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |       if (opts.debug) { | 
					
						
							|  |  |  |         window.alert("DEBUG MODE: Pausing so you can look at logs and whatnot :) Fire at will!"); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |       return OAUTH3._browser.frameRequest( | 
					
						
							|  |  |  |         OAUTH3.utils.url.resolve(directives.issuer, authReq.url) | 
					
						
							|  |  |  |       , authReq.state // state should recycle params
 | 
					
						
							|  |  |  |       , { windowType: opts.windowType | 
					
						
							|  |  |  |         , reuseWindow: opts.broker && '-broker' | 
					
						
							|  |  |  |         , debug: opts.debug | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ).then(function (tokens) { | 
					
						
							|  |  |  |         if (tokens.error) { | 
					
						
							|  |  |  |           // TODO directives.audience
 | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(directives.issuer /*providerUri*/, tokens)); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |         return tokens; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-14 14:37:04 -07:00
										 |  |  |   , _refreshToken: function (providerUri, opts) { | 
					
						
							|  |  |  |       return OAUTH3.discover(providerUri, opts).then(function (directive) { | 
					
						
							|  |  |  |         var prequest = OAUTH3.urls.refreshToken(directive, opts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return OAUTH3.request(prequest).then(function (req) { | 
					
						
							|  |  |  |           var data = req.data; | 
					
						
							|  |  |  |           data.provider_uri = providerUri; | 
					
						
							|  |  |  |           if (data.error) { | 
					
						
							|  |  |  |             return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(providerUri, data)); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return OAUTH3.hooks.session.refresh(opts, data); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Let the Code Waste begin!!
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |   , _browser: { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |       window: window | 
					
						
							|  |  |  |       // TODO we don't need to include this if we're using jQuery or angular
 | 
					
						
							|  |  |  |     , request: function (preq, _sys) { | 
					
						
							|  |  |  |         return new OAUTH3.PromiseA(function (resolve, reject) { | 
					
						
							|  |  |  |           var xhr; | 
					
						
							|  |  |  |           try { | 
					
						
							|  |  |  |             xhr = new XMLHttpRequest(_sys); | 
					
						
							|  |  |  |           } catch(e) { | 
					
						
							|  |  |  |             xhr = new XMLHttpRequest(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           xhr.onreadystatechange = function () { | 
					
						
							|  |  |  |             var data; | 
					
						
							|  |  |  |             if (xhr.readyState !== XMLHttpRequest.DONE) { | 
					
						
							|  |  |  |               // nothing to do here
 | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (xhr.status !== 200) { | 
					
						
							|  |  |  |               reject(new Error('bad status code: ' + xhr.status)); | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |               data = JSON.parse(xhr.responseText); | 
					
						
							|  |  |  |             } catch(e) { | 
					
						
							|  |  |  |               data = xhr.responseText; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             resolve({ | 
					
						
							|  |  |  |               request: xhr | 
					
						
							|  |  |  |             , data: data | 
					
						
							|  |  |  |             , status: xhr.status | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |           xhr.open(preq.method, preq.url, true); | 
					
						
							|  |  |  |           var headers = preq.headers || {}; | 
					
						
							|  |  |  |           Object.keys(headers).forEach(function (key) { | 
					
						
							|  |  |  |             xhr.setRequestHeader(key, headers[key]); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           xhr.send(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , frameRequest: function (url, state, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         opts = opts || {}; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         var previousFrame = OAUTH3._browser._frames[state]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         var windowType = opts.windowType; | 
					
						
							|  |  |  |         if (!windowType) { | 
					
						
							|  |  |  |           windowType = 'popup'; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         var timeout = opts.timeout; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |           timeout = timeout || 3 * 60 * 1000; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           timeout = timeout || ('background' === windowType ? 15 * 1000 : 3 * 60 * 1000); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new OAUTH3.PromiseA(function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |           // TODO periodically garbage collect expired handlers from window object
 | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |           var tok; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           function cleanup() { | 
					
						
							|  |  |  |             delete window['--oauth3-callback-' + state]; | 
					
						
							|  |  |  |             clearTimeout(tok); | 
					
						
							|  |  |  |             tok = null; | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |             // the actual close is done later (by the caller) so that the window/frame
 | 
					
						
							|  |  |  |             // can be reused or self-closes synchronously itself / by parent
 | 
					
						
							|  |  |  |             // (probably won't ever happen, but that's a negotiable implementation detail)
 | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |           window['--oauth3-callback-' + state] = function (params) { | 
					
						
							|  |  |  |             resolve(params); | 
					
						
							|  |  |  |             cleanup(); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           tok = setTimeout(function () { | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |             var err = new Error( | 
					
						
							|  |  |  |               "the '" + windowType + "' request did not complete within " + Math.round(timeout / 1000) + "s" | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             err.code = "E_TIMEOUT"; | 
					
						
							|  |  |  |             reject(err); | 
					
						
							|  |  |  |             cleanup(); | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |           }, timeout); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |           setTimeout(function () { | 
					
						
							|  |  |  |             if (!OAUTH3._browser._frames[state]) { | 
					
						
							|  |  |  |               reject(new Error("TODO: open the iframe first and discover oauth3 directives before popup")); | 
					
						
							|  |  |  |               cleanup(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if ('background' === windowType) { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             if (previousFrame) { | 
					
						
							|  |  |  |               previousFrame.location = url; | 
					
						
							|  |  |  |               //promise = previousFrame.promise;
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |               OAUTH3._browser._frames[state] = OAUTH3._browser.iframe(url, state, opts); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |           } else if ('popup' === windowType) { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             if (previousFrame) { | 
					
						
							|  |  |  |               previousFrame.location = url; | 
					
						
							|  |  |  |               if (opts.debug) { | 
					
						
							|  |  |  |                 previousFrame.focus(); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |               OAUTH3._browser._frames[state] = OAUTH3._browser.frame(url, state, opts); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |           } else if ('inline' === windowType) { | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             // callback function will never execute and would need to redirect back to current page
 | 
					
						
							|  |  |  |             // rather than the callback.html
 | 
					
						
							|  |  |  |             url += '&original_url=' + OAUTH3._browser.window.location.href; | 
					
						
							|  |  |  |             OAUTH3._browser.window.location = url; | 
					
						
							|  |  |  |             //promise = OAUTH3.PromiseA.resolve({ url: url });
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             throw new Error("login framing method options.windowType=" | 
					
						
							|  |  |  |               + opts.windowType + " not type yet implemented"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         }).then(function (params) { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:30:35 -07:00
										 |  |  |           if (params.error) { | 
					
						
							|  |  |  |             // TODO directives.issuer || directives.audience
 | 
					
						
							|  |  |  |             return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError('https://oauth3.org', params)); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |           } | 
					
						
							|  |  |  |           return params; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , closeFrame: function (state, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-14 12:53:03 -07:00
										 |  |  |         opts = opts || {}; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         function close() { | 
					
						
							|  |  |  |           try { | 
					
						
							|  |  |  |             OAUTH3._browser._frames[state].close(); | 
					
						
							|  |  |  |           } catch(e) { | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |               OAUTH3._browser._frames[state].remove(); | 
					
						
							|  |  |  |             } catch(e) { | 
					
						
							| 
									
										
										
										
											2017-02-14 14:51:28 -07:00
										 |  |  |               console.error(new Error("Could not clase window/iframe. closeFrame may have been called twice.")); | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           delete OAUTH3._browser._frames[state]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (opts.debug) { | 
					
						
							|  |  |  |           if (window.confirm("DEBUG MODE: okay to close oauth3 window?")) { | 
					
						
							|  |  |  |             close(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           close(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , _frames: {} | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |     , iframe: function (url, state, opts) { | 
					
						
							|  |  |  |         var framesrc = '<iframe class="js-oauth3-iframe" src="' + url + '" '; | 
					
						
							|  |  |  |         if (opts.debug) { | 
					
						
							|  |  |  |           framesrc += ' width="' + (opts.height || 600) + 'px"' | 
					
						
							|  |  |  |             + ' height="' + (opts.width || 720) + 'px"' | 
					
						
							|  |  |  |             + ' style="opacity: 0.8;" frameborder="1"'; | 
					
						
							| 
									
										
										
										
											2017-02-13 17:53:54 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         else { | 
					
						
							|  |  |  |           framesrc += ' width="1px" height="1px" frameborder="0"'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         framesrc += '></iframe>'; | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         var frame = OAUTH3._browser.window.document.createElement('div'); | 
					
						
							|  |  |  |         frame.innerHTML = framesrc; | 
					
						
							|  |  |  |         OAUTH3._browser.window.document.body.appendChild(frame); | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         return frame; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , frame: function (url, state, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 10:37:00 -07:00
										 |  |  |         // TODO allow size changes (via directive even)
 | 
					
						
							|  |  |  |         return window.open( | 
					
						
							|  |  |  |           url | 
					
						
							|  |  |  |         , 'oauth3-login-' + (opts.reuseWindow || state) | 
					
						
							|  |  |  |         , 'height=' + (opts.height || 720) + ',width=' + (opts.width || 620) | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2017-02-13 15:22:06 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   if ('undefined' !== typeof Promise) { | 
					
						
							|  |  |  |     OAUTH3.PromiseA = Promise; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }('undefined' !== typeof exports ? exports : window)); |