diff --git a/bump-versions.sh b/bump-versions.sh
deleted file mode 100644
index e7875b5..0000000
--- a/bump-versions.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-git push --tags
-
-git checkout v1.0
-git push
-
-git checkout v1
-git merge v1.0
-git push
-
-git checkout master
-git merge v1
-git push
-
-git checkout v1.0
diff --git a/dns.examples.js b/examples/dns@oauth3.org-demo.js
similarity index 100%
rename from dns.examples.js
rename to examples/dns@oauth3.org-demo.js
diff --git a/prefactor/oauth3.browser.js b/prefactor/oauth3.browser.js
deleted file mode 100644
index 7805971..0000000
--- a/prefactor/oauth3.browser.js
+++ /dev/null
@@ -1,560 +0,0 @@
-;(function (exports) {
- 'use strict';
-
- var OAUTH3 = exports.OAUTH3;
- var OAUTH3_CORE = exports.OAUTH3_CORE;
-
- function getDefaultAppUrl() {
- console.warn('[deprecated] using window.location.{protocol, host, pathname} when opts.client_id should be used');
- return window.location.protocol
- + '//' + window.location.host
- + (window.location.pathname).replace(/\/?$/, '')
- ;
- }
-
- var browser = exports.OAUTH3_BROWSER = {
- window: window
- , clientUri: function (location) {
- return OAUTH3_CORE.normalizeUri(location.host + location.pathname);
- }
- , discover: function (providerUri, opts) {
- if (!providerUri) {
- throw new Error('oauth3.discover(providerUri, opts) received providerUri as ' + providerUri);
- }
- 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) {
- opts = opts || {};
- //opts.debug = true;
- providerUri = OAUTH3_CORE.normalizeUrl(providerUri);
- 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);
- }
- var discObj = OAUTH3_CORE.urls.discover(providerUri, { client_id: (opts.client_id || opts.client_uri || getDefaultAppUrl()), debug: opts.debug });
-
- // TODO ability to reuse iframe instead of closing
- 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);
- });
- }
-
- , 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();
- }
- }
- else {
- discWin.child.close();
- }
-
- resolve(tokens);
- };
- });
- }).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
- );
- });
- }
-
- , frameRequest: function (url, state, opts) {
- var promise;
-
- if (!opts.windowType) {
- opts.windowType = 'popup';
- }
-
- if ('background' === opts.windowType) {
- promise = browser.insertIframe(url, state, opts);
- } else if ('popup' === opts.windowType) {
- promise = browser.openWindow(url, state, opts);
- } 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;
- } else {
- throw new Error("login framing method options.windowType not specified or not type yet implemented");
- }
-
- 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 || {};
- if (opts.debug) {
- opts.timeout = opts.timeout || 15 * 60 * 1000;
- }
- var promise = new OAUTH3.PromiseA(function (resolve, reject) {
- var tok;
- var iframeDiv;
-
- function cleanup() {
- delete window['--oauth3-callback-' + state];
- iframeDiv.remove();
- 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();
- }, opts.timeout || 15 * 1000);
-
- // TODO hidden / non-hidden (via directive even)
- var framesrc = '';
-
- iframeDiv = window.document.createElement('div');
- iframeDiv.innerHTML = framesrc;
- window.document.body.appendChild(iframeDiv);
- });
-
- // TODO periodically garbage collect expired handlers from window object
- return promise;
- }
-
- , openWindow: function (url, state, opts) {
- if (opts.debug) {
- opts.timeout = opts.timeout || 15 * 60 * 1000;
- }
- var promise = new OAUTH3.PromiseA(function (resolve, reject) {
- var tok;
-
- function cleanup() {
- clearTimeout(tok);
- tok = null;
- delete window['--oauth3-callback-' + state];
- // this is last in case the window self-closes synchronously
- // (should never happen, but that's a negotiable implementation detail)
- if (!opts.reuseWindow) {
- promise.child.close();
- }
- }
-
- window['--oauth3-callback-' + state] = function (params) {
- console.log('YOLO!!');
- 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);
-
- setTimeout(function () {
- if (!promise.child) {
- reject("TODO: open the iframe first and discover oauth3 directives before popup");
- cleanup();
- }
- }, 0);
- });
-
- // 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)
- );
- // TODO periodically garbage collect expired handlers from window object
- return promise;
- }
-
- //
- // Logins
- //
- , authn: {
- authorizationRedirect: function (providerUri, opts) {
- // TODO get own directives
-
- return OAUTH3.discover(providerUri, opts).then(function (directive) {
- var prequest = OAUTH3_CORE.urls.authorizationRedirect(
- directive
- , opts
- );
-
- if (!prequest.state) {
- throw new Error("[Devolper Error] [authorization redirect] prequest.state is empty");
- }
-
- return browser.frameRequest(prequest.url, prequest.state, opts);
- }).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
- );
- });
- }
- , implicitGrant: function (providerUri, opts) {
- // TODO let broker=true change behavior to open discover inline with frameRequest
- // TODO OAuth3 provider should use the redirect URI as the appId?
- return OAUTH3.discover(providerUri, opts).then(function (directive) {
- var prequest = OAUTH3_CORE.urls.implicitGrant(
- 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);
- }).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
- );
- });
- }
- , logout: function (providerUri, opts) {
- opts = opts || {};
-
- return OAUTH3.discover(providerUri, opts).then(function (directive) {
- var prequest = OAUTH3_CORE.urls.logout(
- directive
- , opts
- );
- // Oauth3.init({ logout: function () {} });
- //return Oauth3.logout();
-
- var redirectUri = opts.redirect_uri || opts.redirectUri
- || (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
- , debug: opts.debug
- };
-
- if (prequest.url === params.redirect_uri) {
- return OAUTH3.PromiseA.resolve();
- }
-
- prequest.url += '#' + OAUTH3_CORE.querystringify(params);
-
- return OAUTH3.insertIframe(prequest.url, prequest.state, opts);
- });
- }
- }
- , 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
- return dst.hostname === src.hostname;
- }
- , 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
- });
- window.location.href = redirectUri;
- 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
-
- return Oauth3.request(preq).then(function (resp) {
- 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
-
- , access_token: data.access_token
- , expires_at: data.expires_at
- , expires_in: data.expires_in
- , scope: data.scope
-
- , refresh_token: data.refresh_token
- , refresh_expires_at: data.refresh_expires_at
- , refresh_expires_in: data.refresh_expires_in
- });
-
- if ('token' === scope.appQuery.response_type) {
- window.location.href = redirectUri;
- 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);
- //window.location.href = redirectUri;
- });
- }
-
- , hackFormSubmitHelper: function (uri) {
- // TODO de-jQuerify
- //window.location.href = redirectUri;
- //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);
- }
- };
- browser.requests = browser.authn;
-
- Object.keys(browser).forEach(function (key) {
- if ('requests' === key) {
- Object.keys(browser.requests).forEach(function (key) {
- OAUTH3.requests[key] = browser.requests[key];
- });
- return;
- }
- OAUTH3[key] = browser[key];
- });
-
-}('undefined' !== typeof exports ? exports : window));
diff --git a/prefactor/oauth3.cache.js b/prefactor/oauth3.cache.js
deleted file mode 100644
index 6b5074a..0000000
--- a/prefactor/oauth3.cache.js
+++ /dev/null
@@ -1,42 +0,0 @@
- oauth3.discover = function (providerUri, opts) {
- opts = opts || {};
-
- console.log('DEBUG oauth3.discover', providerUri);
- console.log(opts);
- if (opts.directives) {
- return oauth3.PromiseA.resolve(opts.directives);
- }
-
- var promise;
- var promise2;
- var directives;
- var updatedAt;
- var fresh;
-
- providerUri = oauth3.normalizeUrl(providerUri);
- try {
- directives = JSON.parse(localStorage.getItem('oauth3.' + providerUri + '.directives'));
- console.log('DEBUG oauth3.discover cache', directives);
- updatedAt = localStorage.getItem('oauth3.' + providerUri + '.directives.updated_at');
- console.log('DEBUG oauth3.discover updatedAt', updatedAt);
- updatedAt = new Date(updatedAt).valueOf();
- console.log('DEBUG oauth3.discover updatedAt', updatedAt);
- } catch(e) {
- // ignore
- }
-
- fresh = (Date.now() - updatedAt) < (24 * 60 * 60 * 1000);
-
- if (directives) {
- promise = oauth3.PromiseA.resolve(directives);
-
- if (fresh) {
- //console.log('[local] [fresh directives]', directives);
- return promise;
- }
- }
-
- promise2 = oauth3._discoverHelper(providerUri, opts);
-
- return promise || promise2;
- };
diff --git a/prefactor/oauth3.core.js b/prefactor/oauth3.core.js
deleted file mode 100644
index a280465..0000000
--- a/prefactor/oauth3.core.js
+++ /dev/null
@@ -1,473 +0,0 @@
-;(function (exports) {
- 'use strict';
-
- // NOTE: we assume that directive.provider_uri exists
-
- var core = {};
- core.urls = core;
-
- function getDefaultAppApiBase() {
- console.warn('[deprecated] using window.location.host when opts.appApiBase should be used');
- return 'https://' + window.location.host;
- }
-
- core.parsescope = function (scope) {
- return (scope||'').split(/[+, ]/g);
- };
- core.stringifyscope = function (scope) {
- if (Array.isArray(scope)) {
- scope = scope.join(' ');
- }
- return scope;
- };
-
- core.querystringify = function (params) {
- var qs = [];
-
- Object.keys(params).forEach(function (key) {
- // TODO nullify instead?
- if ('undefined' === typeof params[key]) {
- return;
- }
-
- if ('scope' === key) {
- params[key] = core.stringifyscope(params[key]);
- }
-
- qs.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
- });
-
- return qs.join('&');
- };
-
- // Modified from http://stackoverflow.com/a/7826782
- core.queryparse = function (search) {
- // parse a query or a hash
- if (-1 !== ['#', '?'].indexOf(search[0])) {
- search = search.substring(1);
- }
- // Solve for case of search within hash
- // example: #/authorization_dialog/?state=...&redirect_uri=...
- var queryIndex = search.indexOf('?');
- if (-1 !== queryIndex) {
- search = search.substr(queryIndex + 1);
- }
-
- var args = search.split('&');
- var argsParsed = {};
- var i, arg, kvp, key, value;
-
- for (i = 0; i < args.length; i += 1) {
-
- arg = args[i];
-
- if (-1 === arg.indexOf('=')) {
-
- argsParsed[decodeURIComponent(arg).trim()] = true;
-
- }
- else {
-
- kvp = arg.split('=');
- key = decodeURIComponent(kvp[0]).trim();
- value = decodeURIComponent(kvp[1]).trim();
- argsParsed[key] = value;
-
- }
- }
-
- return argsParsed;
- };
-
- core.formatError = function (providerUri, params) {
- var err = new Error(params.error_description || params.error.message || "Unknown error when discoving provider '" + providerUri + "'");
- err.uri = params.error_uri || params.error.uri;
- err.code = params.error.code || params.error;
- return err;
- };
- core.normalizePath = function (path) {
- return path.replace(/^\//, '').replace(/\/$/, '');
- };
- core.normalizeUri = function (providerUri) {
- // tested with
- // example.com
- // example.com/
- // http://example.com
- // https://example.com/
- return providerUri
- .replace(/^(https?:\/\/)?/i, '')
- .replace(/\/?$/, '')
- ;
- };
- core.normalizeUrl = function (providerUri) {
- // tested with
- // example.com
- // example.com/
- // http://example.com
- // https://example.com/
- return providerUri
- .replace(/^(https?:\/\/)?/i, 'https://')
- .replace(/\/?$/, '')
- ;
- };
-
- // these might not really belong in core... not sure
- // there should be node.js- and browser-specific versions probably
- core.utils = {
- urlSafeBase64ToBase64: function (b64) {
- // 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;
- }
- , base64ToUrlSafeBase64: function (b64) {
- // Base64 to URL-safe Base64
- b64 = b64.replace(/\+/g, '-').replace(/\//g, '_');
- b64 = b64.replace(/=+/g, '');
- return b64;
- }
- , randomState: function () {
- var i;
- var ch;
- var str;
-
- // 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) {
- // 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;
- }
- }
- };
- core.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 = core.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
- };
- }
- , getFreshness: function (tokenMeta, staletime, now) {
- staletime = staletime || (15 * 60);
- now = now || Date.now();
- 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;
- }
- };
-
- core.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 = core.normalizeUrl(opts.client_id || opts.client_uri);
- providerUri = core.normalizeUrl(providerUri);
-
- var params = {
- action: 'directives'
- , state: core.utils.randomState()
- , 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/#/?' + core.querystringify(params)
- , state: params.state
- , method: 'GET'
- , query: params
- };
-
- return result;
- };
- core.urls.authorizationCode = function (/*directive, scope, redirectUri, clientId*/) {
- //
- // Example Authorization Code Request
- // (not for use in the browser)
- //
- // GET https://example.com/api/org.oauth3.provider/authorization_dialog
- // ?response_type=code
- // &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
- //
- // NOTE: This probably shouldn't be done in the browser because the server
- // needs to initiate the state. If it is done in a browser, the browser
- // should probably request 'state' from the server beforehand
- //
-
- throw new Error("not implemented");
- };
-
- core.urls.authorizationRedirect = function (directive, opts) {
- //console.log('[authorizationRedirect]');
- //
- // Example Authorization Redirect - from Browser to Consumer API
- // (for generating a session securely on your own server)
- //
- // i.e. GET https://<>.com/api/org.oauth3.consumer/authorization_redirect/<>.com
- //
- // GET https://myapp.com/api/org.oauth3.consumer/authorization_redirect/`encodeURIComponent('example.com')`
- // &scope=`encodeURIComponent('profile.login profile.email')`
- //
- // (optional)
- // &state=`cryptoutil.random().toString('hex')`
- // &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
- //
- // NOTE: This is not a request sent to the provider, but rather a request sent to the
- // consumer (your own API) which then sets some state and redirects.
- // This will initiate the `authorization_code` request on your server
- //
- opts = opts || {};
-
- var scope = opts.scope || directive.authn_scope;
- var providerUri = directive.provider_uri;
- var params = {
- state: core.utils.randomState()
- , debug: opts.debug || undefined
- };
- var slimProviderUri = encodeURIComponent(providerUri.replace(/^(https?|spdy):\/\//, ''));
- var authorizationRedirect = opts.authorizationRedirect;
-
- if (scope) {
- params.scope = scope;
- }
- if (opts.redirectUri) {
- // this is really only for debugging
- params.redirect_uri = opts.redirectUri;
- }
- // Note: the type check is necessary because we allow 'true'
- // as an automatic mechanism when it isn't necessary to specify
- if ('string' !== typeof authorizationRedirect) {
- // TODO oauth3.json for self?
- authorizationRedirect = (opts.appApiBase || getDefaultAppApiBase())
- + '/api/org.oauth3.consumer/authorization_redirect/:provider_uri';
- }
- authorizationRedirect = authorizationRedirect
- .replace(/!(provider_uri)/, slimProviderUri)
- .replace(/:provider_uri/, slimProviderUri)
- .replace(/#{provider_uri}/, slimProviderUri)
- .replace(/{{provider_uri}}/, slimProviderUri)
- ;
-
- return {
- url: authorizationRedirect + '?' + core.querystringify(params)
- , method: 'GET'
- , state: params.state // this becomes browser_state
- , params: params // includes scope, final redirect_uri?
- };
- };
-
- core.urls.implicitGrant = function (directive, opts) {
- //console.log('[implicitGrant]');
- //
- // 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 redirectUri = opts.redirect_uri;
- var scope = opts.scope || directive.authn_scope;
- var args = directive[type];
- var uri = args.url;
- var state = core.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 = core.stringifyscope(scope);
- }
- if (!redirectUri) {
- // TODO consider making this optional
- console.error('missing redirect_uri');
- }
- params.redirect_uri = redirectUri;
-
- uri += '?' + core.querystringify(params);
-
- result = {
- url: uri
- , state: state
- , method: args.method
- , query: params
- };
-
- return result;
- };
-
- core.urls.resolve = function (base, next) {
- if (/^https:\/\//i.test(next)) {
- return next;
- }
- return core.normalizeUrl(base) + '/' + core.normalizePath(next);
- };
-
- core.urls.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": "<>", "scope": "<>"
- // , "username": "<>", "password": "password" }
- //
- opts = opts || {};
- var type = 'access_token';
- var grantType = 'refresh_token';
-
- var scope = opts.scope || directive.authn_scope;
- var clientSecret = opts.appSecret || opts.clientSecret;
- var args = directive[type];
- var params = {
- "grant_type": grantType
- , "refresh_token": opts.refresh_token || opts.refreshToken || (opts.session && opts.session.refresh_token)
- , "response_type": 'token'
- , "client_id": opts.appId || opts.app_id || opts.client_id || opts.clientId || opts.client_id || opts.clientId
- , "client_uri": opts.client_uri || opts.clientUri
- //, "scope": undefined
- //, "client_secret": undefined
- , debug: opts.debug || undefined
- };
- var uri = args.url;
- var body;
-
- // TODO not allowed in the browser
- if (clientSecret) {
- params.client_secret = clientSecret;
- }
-
- if (scope) {
- params.scope = core.stringifyscope(scope);
- }
-
- if ('GET' === args.method.toUpperCase()) {
- uri += '?' + core.querystringify(params);
- } else {
- body = params;
- }
-
- return {
- url: uri
- , method: args.method
- , data: body
- };
- };
-
- core.urls.logout = function (directive, opts) {
- opts = opts || {};
- var type = 'logout';
- var clientId = opts.appId || opts.clientId || opts.client_id;
- var args = directive[type];
- var params = {
- client_id: opts.clientUri || opts.client_uri
- , debug: opts.debug || undefined
- };
- var uri = args.url;
- var body;
-
- if (opts.clientUri) {
- params.client_uri = opts.clientUri;
- }
-
- if (clientId) {
- params.client_id = clientId;
- }
-
- args.method = (args.method || 'GET').toUpperCase();
- if ('GET' === args.method) {
- uri += '?' + core.querystringify(params);
- } else {
- body = params;
- }
-
- return {
- url: uri
- , method: args.method || 'GET'
- , data: body
- };
- };
-
- exports.OAUTH3 = exports.OAUTH3 || { core: core };
- exports.OAUTH3_CORE = core.OAUTH3_CORE = core;
-
- if ('undefined' !== typeof module) {
- module.exports = core;
- }
-}('undefined' !== typeof exports ? exports : window));
diff --git a/prefactor/oauth3.core.provider.js b/prefactor/oauth3.core.provider.js
deleted file mode 100644
index 4626d37..0000000
--- a/prefactor/oauth3.core.provider.js
+++ /dev/null
@@ -1,302 +0,0 @@
-;(function (exports) {
- 'use strict';
-
- var core = window.OAUTH3_CORE;
-
- // Provider-Only
- core.urls.loginCode = function (directive, opts) {
- //
- // Example Resource Owner Password Request
- // (generally for 1st party and direct-partner mobile apps, and webapps)
- //
- // POST https://api.example.com/api/org.oauth3.provider/otp
- // { "request_otp": true, "client_id": "<>", "scope": "<>"
- // , "username": "<>" }
- //
- opts = opts || {};
- var clientId = opts.appId || opts.clientId;
-
- var args = directive.credential_otp;
- if (!directive.credential_otp) {
- console.log('[debug] loginCode directive:');
- console.log(directive);
- }
- var params = {
- "username": opts.id || opts.username
- , "request_otp": true // opts.requestOtp || undefined
- //, "jwt": opts.jwt // TODO sign a proof
- , debug: opts.debug || undefined
- };
- var uri = args.url;
- var body;
- if (opts.clientUri) {
- params.client_uri = opts.clientUri;
- }
- if (opts.clientAgreeTos) {
- params.client_agree_tos = opts.clientAgreeTos;
- }
- if (clientId) {
- params.client_id = clientId;
- }
- if ('GET' === args.method.toUpperCase()) {
- uri += '?' + core.querystringify(params);
- } else {
- body = params;
- }
-
- return {
- url: uri
- , method: args.method
- , data: body
- };
- };
-
- core.urls.resourceOwnerPassword = function (directive, opts) {
- //
- // Example Resource Owner Password Request
- // (generally for 1st party and direct-partner mobile apps, and webapps)
- //
- // POST https://example.com/api/org.oauth3.provider/access_token
- // { "grant_type": "password", "client_id": "<>", "scope": "<>"
- // , "username": "<>", "password": "password" }
- //
- opts = opts || {};
- var type = 'access_token';
- var grantType = 'password';
-
- if (!opts.password) {
- if (opts.otp) {
- // for backwards compat
- opts.password = opts.otp; // 'otp:' + opts.otpUuid + ':' + opts.otp;
- }
- }
-
- var scope = opts.scope || directive.authn_scope;
- var clientId = opts.appId || opts.clientId || opts.client_id;
- var clientAgreeTos = opts.clientAgreeTos || opts.client_agree_tos;
- var clientUri = opts.clientUri || opts.client_uri || opts.clientUrl || opts.client_url;
- var args = directive[type];
- var otpCode = opts.otp || opts.otpCode || opts.otp_code || opts.otpToken || opts.otp_token || undefined;
- var params = {
- "grant_type": grantType
- , "username": opts.username
- , "password": opts.password || otpCode || undefined
- , "totp": opts.totp || opts.totpToken || opts.totp_token || undefined
- , "otp": otpCode
- , "password_type": otpCode && 'otp'
- , "otp_code": otpCode
- , "otp_uuid": opts.otpUuid || opts.otp_uuid || undefined
- , "user_agent": opts.userAgent || opts.useragent || opts.user_agent || undefined // AJ's Macbook
- , "jwk": (opts.rememberDevice || opts.remember_device) && opts.jwk || undefined
- //, "public_key": opts.rememberDevice && opts.publicKey || undefined
- //, "public_key_type": opts.rememberDevice && opts.publicKeyType || undefined // RSA/ECDSA
- //, "jwt": opts.jwt // TODO sign a proof with a previously loaded public_key
- , debug: opts.debug || undefined
- };
- var uri = args.url;
- var body;
- if (opts.totp) {
- params.totp = opts.totp;
- }
-
- if (clientId) {
- params.clientId = clientId;
- }
- if (clientUri) {
- params.clientUri = clientUri;
- params.clientAgreeTos = clientAgreeTos;
- if (!clientAgreeTos) {
- throw new Error('Developer Error: missing clientAgreeTos uri');
- }
- }
-
- if (scope) {
- params.scope = core.stringifyscope(scope);
- }
-
- if ('GET' === args.method.toUpperCase()) {
- uri += '?' + core.querystringify(params);
- } else {
- body = params;
- }
-
- return {
- url: uri
- , method: args.method
- , data: body
- };
- };
-
-
- core.urls.grants = function (directive, opts) {
- // directive = { issuer, authorization_decision }
- // opts = { response_type, scopes{ granted, requested, pending, accepted } }
-
- if (!opts) {
- throw new Error("You must supply a directive and an options object.");
- }
- if (!opts.client_id) {
- throw new Error("You must supply options.client_id.");
- }
- if (!opts.session) {
- throw new Error("You must supply options.session.");
- }
- if (!opts.referrer) {
- console.warn("You should supply options.referrer");
- }
- if (!opts.method) {
- console.warn("You must supply options.method as either 'GET', or 'POST'");
- }
- if ('POST' === opts.method) {
- if ('string' !== typeof opts.scope) {
- console.warn("You should supply options.scope as a space-delimited string of scopes");
- }
- if (-1 === ['token', 'code'].indexOf(opts.response_type)) {
- throw new Error("You must supply options.response_type as 'token' or 'code'");
- }
- }
-
- var url = core.urls.resolve(directive.issuer, directive.grants.url)
- .replace(/(:azp|:client_id)/g, core.normalizeUri(opts.client_id || opts.client_uri))
- .replace(/(:sub|:account_id)/g, opts.session.token.sub)
- ;
- var data = {
- client_id: opts.client_id
- , client_uri: opts.client_uri
- , referrer: opts.referrer
- , response_type: opts.response_type
- , scope: opts.scope
- , tenant_id: opts.tenant_id
- };
- var body;
-
- if ('GET' === opts.method) {
- url += '?' + core.querystringify(data);
- }
- else {
- body = data;
- }
-
- return {
- method: opts.method
- , url: url
- , data: body
- , session: opts.session
- };
- };
- core.urls.authorizationDecision = function (directive, opts) {
- var url = core.urls.resolve(directive.issuer, directive.authorization_decision.url);
- if (!opts) {
- throw new Error("You must supply a directive and an options object");
- }
- console.info(url);
- throw new Error("NOT IMPLEMENTED authorization_decision");
- };
- core.authz = core.authz || {};
- core.authz.scopes = function (session, clientParams) {
- // OAuth3.requests.grants(providerUri, {}); // return list of grants
- // OAuth3.checkGrants(providerUri, {}); //
- var clientUri = OAUTH3.core.normalizeUri(clientParams.client_uri || window.document.referrer);
- var scope = clientParams.scope || '';
- var clientObj = clientParams;
-
- if (!scope) {
- scope = 'oauth3_authn';
- }
-
- //$('.js-user-avatar').attr('src', userAvatar);
-
- /*
- console.log('grants options');
- console.log(loc.hash);
- console.log(loc.search);
- console.log(clientObj);
- console.log(session.token);
- console.log(window.document.referrer);
- */
-
- return OAUTH3.requests.grants(CONFIG.host, {
- method: 'GET'
- , client_id: clientUri
- , client_uri: clientUri
- , session: session
- }).then(function (grantResults) {
- var grants;
- var grantedScopes;
- var grantedScopesMap;
- var pendingScopes;
- var acceptedScopes;
- var scopes = scope.split(/[+, ]/g);
- var callbackUrl;
-
- console.log('previous grants:');
- console.log(grantResults);
-
- if (grantResults.data.error) {
- window.alert('grantResults: ' + grantResults.data.error_description || grantResults.data.error.message);
- return;
- }
-
- // it doesn't matter who the referrer is as long as the destination
- // is an authorized destination for the client in question
- // (though it may not hurt to pass the referrer's info on to the client)
- if (!OAUTH3.checkRedirect(grantResults.data.client, clientObj)) {
- callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK'
- + '?redirect_uri=' + clientObj.redirect_uri
- + '&allowed_urls=' + grantResults.data.client.url
- + '&client_id=' + clientUri
- + '&referrer_uri=' + OAUTH3.core.normalizeUri(window.document.referrer)
- ;
- location.href = callbackUrl;
- return;
- }
-
- if ('oauth3_authn' === scope) {
- // implicit ppid grant is automatic
- console.warn('[security] fix scope checking on backend so that we can do automatic grants');
- // TODO check user preference if implicit ppid grant is allowed
- //return generateToken(session, clientObj);
- }
-
- grants = (grantResults.originalData||grantResults.data).grants.filter(function (grant) {
- if (clientUri === (grant.azp || grant.oauth_client_id || grant.oauthClientId)) {
- return true;
- }
- });
-
- grantedScopesMap = {};
- acceptedScopes = [];
- pendingScopes = scopes.filter(function (requestedScope) {
- return grants.every(function (grant) {
- if (!grant.scope) {
- grant.scope = 'oauth3_authn';
- }
- var gscopes = grant.scope.split(/[+, ]/g);
- gscopes.forEach(function (s) { grantedScopesMap[s] = true; });
- if (-1 !== gscopes.indexOf(requestedScope)) {
- // already accepted in the past
- acceptedScopes.push(requestedScope);
- }
- else {
- // true, is pending
- return true;
- }
- });
- });
- grantedScopes = Object.keys(grantedScopesMap);
-
- return {
- pending: pendingScopes // not yet accepted
- , granted: grantedScopes // all granted, ever
- , requested: scopes // all requested, now
- , accepted: acceptedScopes // granted (ever) and requested (now)
- };
- });
- };
-
- exports.OAUTH3_CORE_PROVIDER = core;
-
- if ('undefined' !== typeof module) {
- module.exports = core;
- }
-}('undefined' !== typeof exports ? exports : window));
diff --git a/prefactor/oauth3.jquery.js b/prefactor/oauth3.jquery.js
deleted file mode 100644
index 5c230dc..0000000
--- a/prefactor/oauth3.jquery.js
+++ /dev/null
@@ -1,109 +0,0 @@
-(function () {
- 'use strict';
-
- // I did try to shim jQuery's deferred, but it's just too clunky.
- // Here I use es6-promise which lacks asynchrity, but it's the smallest Promise implementation.
- // Only Opera Mini and MSIE (even on 11) will use this shim, so no biggie;
-
- var oauth3 = window.OAUTH3;
- var count = 0;
-
- function inject() {
- count += 1;
-
- if (count >= 100) {
- throw new Error("you forgot to include rsvp.js, methinks");
- }
-
- /*
- *
- [window.Promise, window.ES6Promise, window.RSVP.Promise].forEach(function (PromiseA) {
- var x = 1; new PromiseA(function (resolve, reject) { console.log('x', 1 === x); resolve(); }); x = 2; void null;
- var y = 1; PromiseA.resolve().then(function () { console.log('y', 2 === x); }); y = 2; void null;
- });
- */
- var PromiseA = /*(window.RSVP && window.RSVP.Promise) || window.ES6Promise || */window.Promise;
- if ('undefined' !== typeof PromiseA) {
- oauth3.providePromise(PromiseA).then(function () {
- // ignore
- window.jqOauth3 = oauth3;
- }, function (err) {
- console.error(err);
- console.error("Bad Promise Implementation!");
- });
- return;
- }
-
-
- // because MSIE can't tell when a script is loaded
- setTimeout(inject, 100);
- }
-
- if ('undefined' === typeof Promise) {
- // support Opera Mini and MSIE 11+ (which doesn't support