| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  | ;(function (exports) { | 
					
						
							|  |  |  |   'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var OAUTH3 = exports.OAUTH3; | 
					
						
							|  |  |  |   var OAUTH3_CORE = exports.OAUTH3_CORE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function getDefaultAppUrl() { | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |     console.warn('[deprecated] using window.location.{protocol, host, pathname} when opts.client_id should be used'); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |     return window.location.protocol | 
					
						
							|  |  |  |       + '//' + window.location.host | 
					
						
							|  |  |  |       + (window.location.pathname).replace(/\/?$/, '') | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var browser = exports.OAUTH3_BROWSER = { | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |     window: window | 
					
						
							|  |  |  |   , clientUri: function (location) { | 
					
						
							| 
									
										
										
										
											2017-02-10 21:34:00 -05:00
										 |  |  |       return OAUTH3_CORE.normalizeUri(location.host + location.pathname); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , discover: function (providerUri, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-08 04:18:15 -05:00
										 |  |  |       if (!providerUri) { | 
					
						
							|  |  |  |         throw new Error('oauth3.discover(providerUri, opts) received providerUri as ' + providerUri); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-11 00:19:17 -05:00
										 |  |  |       var directives = OAUTH3.hooks.getDirectives(providerUri); | 
					
						
							|  |  |  |       if (directives && directives.issuer) { | 
					
						
							|  |  |  |         return OAUTH3.PromiseA.resolve(directives); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return browser._discoverHelper(providerUri, opts).then(function (directives) { | 
					
						
							|  |  |  |         directives.issuer = directives.issuer || OAUTH3_CORE.normalizeUrl(providerUri); | 
					
						
							|  |  |  |         console.log('discoverHelper', directives); | 
					
						
							|  |  |  |         return OAUTH3.hooks.setDirectives(providerUri, directives); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , _discoverHelper: function (providerUri, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       opts = opts || {}; | 
					
						
							| 
									
										
										
										
											2017-02-11 00:19:17 -05:00
										 |  |  |       //opts.debug = true;
 | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       providerUri = OAUTH3_CORE.normalizeUrl(providerUri); | 
					
						
							| 
									
										
										
										
											2017-02-10 23:45:34 -05:00
										 |  |  |       if (window.location.hostname.match(providerUri)) { | 
					
						
							|  |  |  |         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.core.normalizeUrl(providerUri) + '/.well-known/oauth3/directives.json' | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!window.location.hostname.match(opts.client_id || opts.client_uri)) { | 
					
						
							|  |  |  |         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, window.location.hostname); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |       var discObj = OAUTH3_CORE.urls.discover(providerUri, { client_id: (opts.client_id || opts.client_uri || getDefaultAppUrl()), debug: opts.debug }); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |       // TODO ability to reuse iframe instead of closing
 | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       return browser.insertIframe(discObj.url, discObj.state, opts).then(function (params) { | 
					
						
							|  |  |  |         if (params.error) { | 
					
						
							|  |  |  |           return OAUTH3_CORE.formatError(providerUri, params.error); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         var directives = JSON.parse(atob(OAUTH3_CORE.utils.urlSafeBase64ToBase64(params.result || params.directives))); | 
					
						
							|  |  |  |         return directives; | 
					
						
							|  |  |  |       }, function (err) { | 
					
						
							|  |  |  |         return OAUTH3.PromiseA.reject(err); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 20:47:32 -07:00
										 |  |  |   , discoverAuthorizationDialog: function(providerUri, opts) { | 
					
						
							|  |  |  |       var discObj = OAUTH3.core.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
 | 
					
						
							|  |  |  |       var discWin = OAUTH3.openWindow(discObj.url, discObj.state, { reuseWindow: 'conquerer' }); | 
					
						
							|  |  |  |       return discWin.then(function (params) { | 
					
						
							|  |  |  |         console.log('discwin params'); | 
					
						
							|  |  |  |         console.log(params); | 
					
						
							|  |  |  |         // discWin.child
 | 
					
						
							|  |  |  |         // TODO params should have response_type indicating json, binary, etc
 | 
					
						
							|  |  |  |         var directives = JSON.parse(atob(OAUTH3.core.utils.urlSafeBase64ToBase64(params.result || params.directives))); | 
					
						
							|  |  |  |         console.log('directives'); | 
					
						
							|  |  |  |         console.log(directives); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Do some stuff
 | 
					
						
							|  |  |  |         var authObj = OAUTH3.core.implicitGrant( | 
					
						
							|  |  |  |           directives | 
					
						
							|  |  |  |         , { redirect_uri: opts.redirect_uri | 
					
						
							|  |  |  |           , debug: opts.debug | 
					
						
							|  |  |  |           , client_id: opts.client_id || opts.client_uri | 
					
						
							|  |  |  |           , client_uri: opts.client_uri || opts.client_id | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (params.debug) { | 
					
						
							|  |  |  |           window.alert("DEBUG MODE: Pausing so you can look at logs and whatnot :) Fire at will!"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new OAUTH3.PromiseA(function (resolve, reject) { | 
					
						
							|  |  |  |           // TODO check if authObj.url is relative or full
 | 
					
						
							|  |  |  |           discWin.child.location = OAUTH3.core.urls.resolve(providerUri, authObj.url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (params.debug) { | 
					
						
							|  |  |  |             discWin.child.focus(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           window['--oauth3-callback-' + authObj.state] = function (tokens) { | 
					
						
							|  |  |  |             if (tokens.error) { | 
					
						
							|  |  |  |               return reject(OAUTH3.core.formatError(tokens.error)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (params.debug || tokens.debug) { | 
					
						
							|  |  |  |               if (window.confirm("DEBUG MODE: okay to close oauth3 window?")) { | 
					
						
							|  |  |  |                 discWin.child.close(); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-02-10 20:53:12 -07:00
										 |  |  |             else { | 
					
						
							|  |  |  |               discWin.child.close(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-02-10 20:47:32 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             resolve(tokens); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-02-13 13:01:13 -05:00
										 |  |  |       }).then(function (tokens) { | 
					
						
							|  |  |  |         return OAUTH3.hooks.refreshSession( | 
					
						
							|  |  |  |           opts.session || { | 
					
						
							|  |  |  |             provider_uri: providerUri | 
					
						
							|  |  |  |           , client_id: opts.client_id | 
					
						
							|  |  |  |           , client_uri: opts.client_uri || opts.clientUri | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         , tokens | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2017-02-10 20:47:32 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |   , frameRequest: function (url, state, opts) { | 
					
						
							|  |  |  |       var promise; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |       if (!opts.windowType) { | 
					
						
							|  |  |  |         opts.windowType = 'popup'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ('background' === opts.windowType) { | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         promise = browser.insertIframe(url, state, opts); | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |       } else if ('popup' === opts.windowType) { | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         promise = browser.openWindow(url, state, opts); | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |       } else if ('inline' === opts.windowType) { | 
					
						
							|  |  |  |         // callback function will never execute and would need to redirect back to current page
 | 
					
						
							|  |  |  |         // rather than the callback.html
 | 
					
						
							|  |  |  |         url += '&original_url=' + browser.window.location.href; | 
					
						
							|  |  |  |         promise = browser.window.location = url; | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |         throw new Error("login framing method options.windowType not specified or not type yet implemented"); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return promise.then(function (params) { | 
					
						
							|  |  |  |         var err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (params.error || params.error_description) { | 
					
						
							|  |  |  |           err = new Error(params.error_description || "Unknown response error"); | 
					
						
							|  |  |  |           err.code = params.error || "E_UKNOWN_ERROR"; | 
					
						
							|  |  |  |           err.params = params; | 
					
						
							|  |  |  |           return OAUTH3.PromiseA.reject(err); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return params; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , insertIframe: function (url, state, opts) { | 
					
						
							|  |  |  |       opts = opts || {}; | 
					
						
							| 
									
										
										
										
											2017-02-10 21:56:23 -07:00
										 |  |  |       if (opts.debug) { | 
					
						
							|  |  |  |         opts.timeout = opts.timeout || 15 * 60 * 1000; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       var promise = new OAUTH3.PromiseA(function (resolve, reject) { | 
					
						
							|  |  |  |         var tok; | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |         var iframeDiv; | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         function cleanup() { | 
					
						
							|  |  |  |           delete window['--oauth3-callback-' + state]; | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |           iframeDiv.remove(); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |           clearTimeout(tok); | 
					
						
							|  |  |  |           tok = null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         window['--oauth3-callback-' + state] = function (params) { | 
					
						
							|  |  |  |           resolve(params); | 
					
						
							|  |  |  |           cleanup(); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tok = setTimeout(function () { | 
					
						
							|  |  |  |           var err = new Error("the iframe request did not complete within 15 seconds"); | 
					
						
							|  |  |  |           err.code = "E_TIMEOUT"; | 
					
						
							|  |  |  |           reject(err); | 
					
						
							|  |  |  |           cleanup(); | 
					
						
							| 
									
										
										
										
											2017-02-10 23:45:34 -05:00
										 |  |  |         }, opts.timeout || 15 * 1000); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // TODO hidden / non-hidden (via directive even)
 | 
					
						
							| 
									
										
										
										
											2017-02-09 17:13:40 -05:00
										 |  |  |         var framesrc = '<iframe class="js-oauth3-iframe" src="' + url + '" '; | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         if (opts.debug) { | 
					
						
							|  |  |  |           framesrc += ' width="800px" height="800px" style="opacity: 0.8;" frameborder="1"'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           framesrc += ' width="1px" height="1px" frameborder="0"'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         framesrc += '></iframe>'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |         iframeDiv = window.document.createElement('div'); | 
					
						
							|  |  |  |         iframeDiv.innerHTML = framesrc; | 
					
						
							|  |  |  |         window.document.body.appendChild(iframeDiv); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO periodically garbage collect expired handlers from window object
 | 
					
						
							|  |  |  |       return promise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , openWindow: function (url, state, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-10 21:56:23 -07:00
										 |  |  |       if (opts.debug) { | 
					
						
							|  |  |  |         opts.timeout = opts.timeout || 15 * 60 * 1000; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       var promise = new OAUTH3.PromiseA(function (resolve, reject) { | 
					
						
							|  |  |  |         var tok; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         function cleanup() { | 
					
						
							|  |  |  |           clearTimeout(tok); | 
					
						
							|  |  |  |           tok = null; | 
					
						
							| 
									
										
										
										
											2017-02-10 20:41:11 -07:00
										 |  |  |           delete window['--oauth3-callback-' + state]; | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |           // this is last in case the window self-closes synchronously
 | 
					
						
							|  |  |  |           // (should never happen, but that's a negotiable implementation detail)
 | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |           if (!opts.reuseWindow) { | 
					
						
							|  |  |  |             promise.child.close(); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         window['--oauth3-callback-' + state] = function (params) { | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |           console.log('YOLO!!'); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |           resolve(params); | 
					
						
							|  |  |  |           cleanup(); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tok = setTimeout(function () { | 
					
						
							|  |  |  |           var err = new Error("the windowed request did not complete within 3 minutes"); | 
					
						
							|  |  |  |           err.code = "E_TIMEOUT"; | 
					
						
							|  |  |  |           reject(err); | 
					
						
							|  |  |  |           cleanup(); | 
					
						
							|  |  |  |         }, opts.timeout || 3 * 60 * 1000); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |         setTimeout(function () { | 
					
						
							|  |  |  |           if (!promise.child) { | 
					
						
							|  |  |  |             reject("TODO: open the iframe first and discover oauth3 directives before popup"); | 
					
						
							|  |  |  |             cleanup(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }, 0); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 20:23:57 -07:00
										 |  |  |       // TODO allow size changes (via directive even)
 | 
					
						
							|  |  |  |       promise.child = window.open( | 
					
						
							|  |  |  |         url | 
					
						
							|  |  |  |       , 'oauth3-login-' + (opts.reuseWindow || state) | 
					
						
							|  |  |  |       , 'height=' + (opts.height || 720) + ',width=' + (opts.width || 620) | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       // TODO periodically garbage collect expired handlers from window object
 | 
					
						
							|  |  |  |       return promise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Logins
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |   , authn: { | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |       authorizationRedirect: function (providerUri, opts) { | 
					
						
							|  |  |  |         // TODO get own directives
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return OAUTH3.discover(providerUri, opts).then(function (directive) { | 
					
						
							| 
									
										
										
										
											2017-02-08 04:18:15 -05:00
										 |  |  |           var prequest = OAUTH3_CORE.urls.authorizationRedirect( | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |             directive | 
					
						
							|  |  |  |           , opts | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!prequest.state) { | 
					
						
							|  |  |  |             throw new Error("[Devolper Error] [authorization redirect] prequest.state is empty"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return browser.frameRequest(prequest.url, prequest.state, opts); | 
					
						
							| 
									
										
										
										
											2017-02-13 13:01:13 -05:00
										 |  |  |         }).then(function (tokens) { | 
					
						
							|  |  |  |           return OAUTH3.hooks.refreshSession( | 
					
						
							|  |  |  |             opts.session || { | 
					
						
							|  |  |  |               provider_uri: providerUri | 
					
						
							|  |  |  |             , client_id: opts.client_id | 
					
						
							|  |  |  |             , client_uri: opts.client_uri || opts.clientUri | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           , tokens | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , implicitGrant: function (providerUri, opts) { | 
					
						
							| 
									
										
										
										
											2017-02-13 13:01:13 -05:00
										 |  |  |         // TODO let broker=true change behavior to open discover inline with frameRequest
 | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         // TODO OAuth3 provider should use the redirect URI as the appId?
 | 
					
						
							|  |  |  |         return OAUTH3.discover(providerUri, opts).then(function (directive) { | 
					
						
							| 
									
										
										
										
											2017-02-08 04:18:15 -05:00
										 |  |  |           var prequest = OAUTH3_CORE.urls.implicitGrant( | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |             directive | 
					
						
							|  |  |  |             // TODO OAuth3 provider should referrer / referer / origin as the appId?
 | 
					
						
							|  |  |  |           , opts | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!prequest.state) { | 
					
						
							|  |  |  |             throw new Error("[Devolper Error] [implicit grant] prequest.state is empty"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return browser.frameRequest(prequest.url, prequest.state, opts); | 
					
						
							| 
									
										
										
										
											2017-02-13 13:01:13 -05:00
										 |  |  |         }).then(function (tokens) { | 
					
						
							|  |  |  |           return OAUTH3.hooks.refreshSession( | 
					
						
							|  |  |  |             opts.session || { | 
					
						
							|  |  |  |               provider_uri: providerUri | 
					
						
							|  |  |  |             , client_id: opts.client_id | 
					
						
							|  |  |  |             , client_uri: opts.client_uri || opts.clientUri | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           , tokens | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , logout: function (providerUri, opts) { | 
					
						
							|  |  |  |         opts = opts || {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return OAUTH3.discover(providerUri, opts).then(function (directive) { | 
					
						
							| 
									
										
										
										
											2017-02-08 04:18:15 -05:00
										 |  |  |           var prequest = OAUTH3_CORE.urls.logout( | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |             directive | 
					
						
							|  |  |  |           , opts | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |           // Oauth3.init({ logout: function () {} });
 | 
					
						
							|  |  |  |           //return Oauth3.logout();
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-10 23:45:34 -05:00
										 |  |  |           var redirectUri = opts.redirect_uri || opts.redirectUri | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |             || (window.location.protocol + '//' + (window.location.host + window.location.pathname) + 'oauth3.html') | 
					
						
							|  |  |  |             ; | 
					
						
							|  |  |  |           var params = { | 
					
						
							|  |  |  |             // logout=true for all logins/accounts
 | 
					
						
							|  |  |  |             // logout=app-scoped-login-id for a single login
 | 
					
						
							|  |  |  |             action: 'logout' | 
					
						
							|  |  |  |             // TODO specify specific accounts / logins to delete from session
 | 
					
						
							|  |  |  |           , accounts: true | 
					
						
							|  |  |  |           , logins: true | 
					
						
							|  |  |  |           , redirect_uri: redirectUri | 
					
						
							|  |  |  |           , state: prequest.state | 
					
						
							| 
									
										
										
										
											2017-02-10 23:45:34 -05:00
										 |  |  |           , debug: opts.debug | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |           }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (prequest.url === params.redirect_uri) { | 
					
						
							|  |  |  |             return OAUTH3.PromiseA.resolve(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           prequest.url += '#' + OAUTH3_CORE.querystringify(params); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return OAUTH3.insertIframe(prequest.url, prequest.state, opts); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |   , isIframe: function isIframe () { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         return window.self !== window.top; | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , parseUrl: function (url) { | 
					
						
							|  |  |  |       var parser = document.createElement('a'); | 
					
						
							|  |  |  |       parser.href = url; | 
					
						
							|  |  |  |       return parser; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , isRedirectHostSafe: function (referrerUrl, redirectUrl) { | 
					
						
							|  |  |  |       var src = browser.parseUrl(referrerUrl); | 
					
						
							|  |  |  |       var dst = browser.parseUrl(redirectUrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO how should we handle subdomains?
 | 
					
						
							|  |  |  |       // It should be safe for api.example.com to redirect to example.com
 | 
					
						
							|  |  |  |       // But it may not be safe for to example.com to redirect to aj.example.com
 | 
					
						
							|  |  |  |       // It is also probably not safe for sally.example.com to redirect to john.example.com
 | 
					
						
							|  |  |  |       // The client should have a list of allowed URLs to choose from and perhaps a wildcard will do
 | 
					
						
							|  |  |  |       //
 | 
					
						
							|  |  |  |       // api.example.com.evil.com SHOULD NOT match example.com
 | 
					
						
							| 
									
										
										
										
											2017-02-09 17:13:40 -05:00
										 |  |  |       return dst.hostname === src.hostname; | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |     } | 
					
						
							|  |  |  |   , checkRedirect: function (client, query) { | 
					
						
							|  |  |  |       console.warn("[security] URL path checking not yet implemented"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var clientUrl = OAUTH3.core.normalizeUrl(client.url); | 
					
						
							|  |  |  |       var redirectUrl = OAUTH3.core.normalizeUrl(query.redirect_uri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // General rule:
 | 
					
						
							|  |  |  |       // I can callback to a shorter domain (fewer subs) or a shorter path (on the same domain)
 | 
					
						
							|  |  |  |       // but not a longer (more subs) or different domain or a longer path (on the same domain)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // We can callback to an explicitly listed domain (TODO and path)
 | 
					
						
							|  |  |  |       if (browser.isRedirectHostSafe(clientUrl, redirectUrl)) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   /* | 
					
						
							|  |  |  |   , redirect: function (redirect) { | 
					
						
							|  |  |  |       if (parser.search) { | 
					
						
							|  |  |  |         parser.search += '&'; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         parser.search += '?'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       parser.search += 'error=E_NO_SESSION'; | 
					
						
							|  |  |  |       redirectUri = parser.href; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       window.location.href = redirectUri; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , hackFormSubmit: function (opts) { | 
					
						
							|  |  |  |       opts = opts || {}; | 
					
						
							|  |  |  |       scope.authorizationDecisionUri = DaplieApiConfig.providerUri + '/api/org.oauth3.provider/authorization_decision'; | 
					
						
							|  |  |  |       scope.updateScope(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var redirectUri = scope.appQuery.redirect_uri.replace(/^https?:\/\//i, 'https://'); | 
					
						
							|  |  |  |       var separator; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO check that we appropriately use '#' for implicit and '?' for code
 | 
					
						
							|  |  |  |       // (server-side) in an OAuth2 backwards-compatible way
 | 
					
						
							|  |  |  |       if ('token' === scope.appQuery.response_type) { | 
					
						
							|  |  |  |         separator = '#'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else if ('code' === scope.appQuery.response_type) { | 
					
						
							|  |  |  |         separator = '?'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							|  |  |  |         separator = '#'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (scope.pendingScope.length && !opts.allow) { | 
					
						
							|  |  |  |         redirectUri += separator + Oauth3.querystringify({ | 
					
						
							|  |  |  |           error: 'access_denied' | 
					
						
							|  |  |  |           , error_description: 'None of the permissions were accepted' | 
					
						
							|  |  |  |           , error_uri: 'https://oauth3.org/docs/errors#access_denied' | 
					
						
							|  |  |  |           , state: scope.appQuery.state | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |         window.location.href = redirectUri; | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO move to Oauth3? or not?
 | 
					
						
							|  |  |  |       // this could be implementation-specific,
 | 
					
						
							|  |  |  |       // but it may still be nice to provide it as de-facto
 | 
					
						
							|  |  |  |       var url = DaplieApiConfig.apiBaseUri + '/api/org.oauth3.provider/grants/:client_id/:account_id' | 
					
						
							|  |  |  |         .replace(/:client_id/g, scope.appQuery.client_id || scope.appQuery.client_uri) | 
					
						
							|  |  |  |         .replace(/:account_id/g, scope.selectedAccountId) | 
					
						
							|  |  |  |         ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var account = scope.sessionAccount; | 
					
						
							|  |  |  |       var session = { accessToken: account.token, refreshToken: account.refreshToken }; | 
					
						
							|  |  |  |       var preq = { | 
					
						
							|  |  |  |         url: url | 
					
						
							|  |  |  |       , method: 'POST' | 
					
						
							|  |  |  |       , data: { | 
					
						
							|  |  |  |           scope: updateAccepted() | 
					
						
							|  |  |  |         , response_type: scope.appQuery.response_type | 
					
						
							|  |  |  |         , referrer: document.referrer || document.referer || '' | 
					
						
							|  |  |  |         , referer: document.referrer || document.referer || '' | 
					
						
							|  |  |  |         , tenant_id: scope.appQuery.tenant_id | 
					
						
							|  |  |  |         , client_id: scope.appQuery.client_id | 
					
						
							|  |  |  |         , client_uri: scope.appQuery.client_uri | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , session: session | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       preq.clientId = preq.appId = DaplieApiConfig.appId || DaplieApiConfig.clientId; | 
					
						
							|  |  |  |       preq.clientUri = preq.appUri = DaplieApiConfig.appUri || DaplieApiConfig.clientUri; | 
					
						
							|  |  |  |       // TODO need a way to have middleware in Oauth3.request for TherapySession et al
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-09 17:13:40 -05:00
										 |  |  |       return Oauth3.request(preq).then(function (resp) { | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |         var err; | 
					
						
							|  |  |  |         var data = resp.data || {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (data.error) { | 
					
						
							|  |  |  |           err = new Error(data.error.message || data.errorDescription); | 
					
						
							|  |  |  |           err.message = data.error.message || data.errorDescription; | 
					
						
							|  |  |  |           err.code = resp.data.error.code || resp.data.error; | 
					
						
							|  |  |  |           err.uri = 'https://oauth3.org/docs/errors#' + (resp.data.error.code || resp.data.error); | 
					
						
							|  |  |  |           return $q.reject(err); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!(data.code || data.accessToken)) { | 
					
						
							|  |  |  |           err = new Error("No grant code"); | 
					
						
							|  |  |  |           return $q.reject(err); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return data; | 
					
						
							|  |  |  |       }).then(function (data) { | 
					
						
							|  |  |  |         redirectUri += separator + Oauth3.querystringify({ | 
					
						
							|  |  |  |           state: scope.appQuery.state | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         , code: data.code | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |         , access_token: data.access_token | 
					
						
							|  |  |  |         , expires_at: data.expires_at | 
					
						
							|  |  |  |         , expires_in: data.expires_in | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |         , scope: data.scope | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |         , refresh_token: data.refresh_token | 
					
						
							|  |  |  |         , refresh_expires_at: data.refresh_expires_at | 
					
						
							|  |  |  |         , refresh_expires_in: data.refresh_expires_in | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ('token' === scope.appQuery.response_type) { | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |           window.location.href = redirectUri; | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if ('code' === scope.appQuery.response_type) { | 
					
						
							|  |  |  |           scope.hackFormSubmitHelper(redirectUri); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           console.warn("Grant Code NOT IMPLEMENTED for '" + scope.appQuery.response_type + "'"); | 
					
						
							|  |  |  |           console.warn(redirectUri); | 
					
						
							|  |  |  |           throw new Error("Grant Code NOT IMPLEMENTED for '" + scope.appQuery.response_type + "'"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, function (err) { | 
					
						
							|  |  |  |         redirectUri += separator + Oauth3.querystringify({ | 
					
						
							|  |  |  |           error: err.code || 'server_error' | 
					
						
							|  |  |  |         , error_description: err.message || "Server Error: It's not your fault" | 
					
						
							|  |  |  |         , error_uri: err.uri || 'https://oauth3.org/docs/errors#server_error' | 
					
						
							|  |  |  |         , state: scope.appQuery.state | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         console.error('Grant Code Error NOT IMPLEMENTED'); | 
					
						
							|  |  |  |         console.error(err); | 
					
						
							|  |  |  |         console.error(redirectUri); | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |         //window.location.href = redirectUri;
 | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , hackFormSubmitHelper: function (uri) { | 
					
						
							|  |  |  |       // TODO de-jQuerify
 | 
					
						
							| 
									
										
										
										
											2017-02-09 21:51:22 -05:00
										 |  |  |       //window.location.href = redirectUri;
 | 
					
						
							| 
									
										
										
										
											2017-02-09 00:11:12 -05:00
										 |  |  |       //return;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // the only way to do a POST that redirects the current window
 | 
					
						
							|  |  |  |       window.jQuery('form.js-hack-hidden-form').attr('action', uri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // give time for the apply to take place
 | 
					
						
							|  |  |  |       window.setTimeout(function () { | 
					
						
							|  |  |  |         window.jQuery('form.js-hack-hidden-form').submit(); | 
					
						
							|  |  |  |       }, 50); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2017-02-13 14:35:48 -05:00
										 |  |  |   browser.requests = browser.authn; | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Object.keys(browser).forEach(function (key) { | 
					
						
							| 
									
										
										
										
											2017-02-08 04:18:15 -05:00
										 |  |  |     if ('requests' === key) { | 
					
						
							|  |  |  |       Object.keys(browser.requests).forEach(function (key) { | 
					
						
							|  |  |  |         OAUTH3.requests[key] = browser.requests[key]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-08 00:48:07 -05:00
										 |  |  |     OAUTH3[key] = browser[key]; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }('undefined' !== typeof exports ? exports : window)); |