diff --git a/oauth3.implicit.js b/oauth3.implicit.js index 449508a..6d2784f 100644 --- a/oauth3.implicit.js +++ b/oauth3.implicit.js @@ -233,10 +233,10 @@ debug: opts.debug || undefined , client_uri: opts.client_uri || opts.clientUri || undefined , client_id: opts.client_id || opts.client_uri || undefined + , state: state }; var result; - params.state = state; params.response_type = responseType; if (scope) { params.scope = OAUTH3.utils.scope.stringify(scope); @@ -315,6 +315,48 @@ , data: body }; } + , logout: function (directive, opts) { + // action=logout + + // Example Logout Request + // (generally for 1st or 3rd party server-side, mobile, and desktop apps) + // + // GET https://example.com/#/logout/ + // ?client_id=<> + // &access_token=<> + // &sub=<> + // + // Note that the use of # keeps certain parameters from traveling across + // the network at all (and we use https anyway) + // + opts = opts || {}; + var action = 'logout'; + var args = directive[action]; + var state = opts.state || OAUTH3.utils.randomState(); + var params = { + action: action + //, response_type: 'confirmation' + , client_id: opts.client_id || opts.client_uri + , client_uri: opts.client_uri + , state: state + , debug: opts.debug + }; + var uri = args.url; + var body; + + if ('GET' === args.method.toUpperCase()) { + uri += '?' + OAUTH3.utils.query.stringify(params); + } else { + body = params; + } + + return { + url: OAUTH3.utils.url.resolve(directive.issuer, uri) + , method: args.method + , state: state + , data: body + }; + } } , hooks: { directives: { @@ -467,70 +509,7 @@ }); } , _discoverHelper: function(providerUri, opts) { - return OAUTH3._discover(providerUri, opts); - } - , _discover: function(providerUri, opts) { - opts = opts || {}; - 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 - , state: opts._state || undefined - , 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 - opts._windowType = opts.windowType; - opts.windowType = opts.windowType || 'background'; - return OAUTH3._browser.frameRequest( - OAUTH3.utils.url.resolve(providerUri, discReq.url) - , discReq.state - // why not just pass opts whole? - , { windowType: opts.windowType - , reuseWindow: opts.broker && '-broker' - , debug: opts.debug - } - ).then(function (params) { - 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)); - } - - // TODO params should have response_type indicating json, binary, etc - var directives = JSON.parse(OAUTH3.utils.atob(OAUTH3.utils._urlSafeBase64ToBase64(params.result || params.directives))); - // caller will call OAUTH3.hooks.directives.set(providerUri, directives); - return directives; - }); + return OAUTH3._browser.discover(providerUri, opts); } , request: function (preq, opts) { function fetch() { @@ -639,6 +618,36 @@ }); }); } + , logout: function(providerUri, opts) { + return OAUTH3._logoutHelper(providerUri, opts); + } + , _logoutHelper: function(providerUri, opts) { + var logoutReq = OAUTH3.urls.logout( + providerUri + , { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location)) + , windowType: 'popup' // we'll figure out background later + , broker: opts.broker + //, state: opts._state + , debug: opts.debug + } + ); + + return OAUTH3._browser.frameRequest( + OAUTH3.utils.url.resolve(directives.issuer, authReq.url) + , logoutReq.state // state should recycle params + , { windowType: 'popup' + , 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)); + } + + return tokens; + }); + } // @@ -647,6 +656,69 @@ , _browser: { window: window // TODO we don't need to include this if we're using jQuery or angular + , discover: function(providerUri, opts) { + opts = opts || {}; + 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 + , state: opts._state || undefined + , 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 + opts._windowType = opts.windowType; + opts.windowType = opts.windowType || 'background'; + return OAUTH3._browser.frameRequest( + OAUTH3.utils.url.resolve(providerUri, discReq.url) + , discReq.state + // why not just pass opts whole? + , { windowType: opts.windowType + , reuseWindow: opts.broker && '-broker' + , debug: opts.debug + } + ).then(function (params) { + 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)); + } + + // TODO params should have response_type indicating json, binary, etc + var directives = JSON.parse(OAUTH3.utils.atob(OAUTH3.utils._urlSafeBase64ToBase64(params.result || params.directives))); + // caller will call OAUTH3.hooks.directives.set(providerUri, directives); + return directives; + }); + } , request: function (preq, _sys) { return new OAUTH3.PromiseA(function (resolve, reject) { var xhr;