From 1ca6f0a3245089b594e632131ba3041f3acd072d Mon Sep 17 00:00:00 2001 From: tigerbot Date: Wed, 26 Jul 2017 16:27:03 -0600 Subject: [PATCH] updated how grants are retrieved --- oauth3.core.js | 7 +- oauth3.issuer.js | 213 +++++++++++++++-------------------------------- 2 files changed, 73 insertions(+), 147 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index 91975e8..321c8fe 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -168,9 +168,12 @@ } } , scope: { - stringify: function (scope) { + parse: function (scope) { + return (scope||'').split(/[+, ]+/g); + } + , stringify: function (scope) { if (Array.isArray(scope)) { - scope = scope.join(' '); + scope = scope.join(','); } return scope; } diff --git a/oauth3.issuer.js b/oauth3.issuer.js index 257a196..cacd206 100644 --- a/oauth3.issuer.js +++ b/oauth3.issuer.js @@ -3,39 +3,6 @@ var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; -OAUTH3.query.parse = 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; -}; -OAUTH3.scope.parse = function (scope) { - return (scope||'').split(/[, ]/g); -}; OAUTH3.url.parse = function (url) { // TODO browser // Node should replace this @@ -58,8 +25,16 @@ OAUTH3.url._isRedirectHostSafe = function (referrerUrl, redirectUrl) { }; OAUTH3.url.checkRedirect = function (client, query) { console.warn("[security] URL path checking not yet implemented"); + if (!query) { + query = client; + client = query.client_uri; + } + client = client.url || client; - var clientUrl = OAUTH3.url.normalize(client.url); + // 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) + var clientUrl = OAUTH3.url.normalize(client); var redirectUrl = OAUTH3.url.normalize(query.redirect_uri); // General rule: @@ -72,6 +47,18 @@ OAUTH3.url.checkRedirect = function (client, query) { return true; } + var callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK?'+OAUTH3.query.stringify({ + 'redirect_uri': redirectUrl + , 'allowed_urls': clientUrl + , 'client_id': client + , 'referrer_uri': OAUTH3.uri.normalize(window.document.referrer) + }); + if (query.debug) { + console.log('Redirect Attack'); + console.log(query); + window.alert("DEBUG MODE checkRedirect error encountered. Check the console."); + } + location.href = callbackUrl; return false; }; OAUTH3.url.redirect = function (clientParams, grants, tokenOrError) { @@ -195,14 +182,12 @@ OAUTH3.urls.grants = function (directive, opts) { console.warn("You should supply options.referrer"); } if (!opts.method) { - console.warn("You must supply options.method as either 'GET', or 'POST'"); + console.warn("You should supply options.method as either 'GET', or 'POST'"); + opts.method = 'GET'; } 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'"); + throw new Error("You must supply options.scope as a comma-delimited string of scopes"); } } @@ -214,12 +199,10 @@ OAUTH3.urls.grants = function (directive, opts) { 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; + var body; if ('GET' === opts.method) { url += '?' + OAUTH3.query.stringify(data); } @@ -290,14 +273,13 @@ OAUTH3.authn.resourceOwnerPassword = function (directive, opts) { OAUTH3.authz = {}; OAUTH3.authz.scopes = function (providerUri, session, clientParams) { - // OAuth3.requests.grants(providerUri, {}); // return list of grants - // OAuth3.checkGrants(providerUri, {}); // var clientUri = OAUTH3.uri.normalize(clientParams.client_uri || OAUTH3._browser.window.document.referrer); - var scope = clientParams.scope || ''; - var clientObj = clientParams; - - if (!scope) { - scope = 'oauth3_authn'; + var scope = clientParams.scope || 'oauth3_authn'; + 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); } return OAUTH3.authz.grants(providerUri, { @@ -305,74 +287,30 @@ OAUTH3.authz.scopes = function (providerUri, session, clientParams) { , 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; - - // 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.url.checkRedirect(grantResults.client, clientObj)) { - callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK' - + '?redirect_uri=' + clientObj.redirect_uri - + '&allowed_urls=' + grantResults.client.url - + '&client_id=' + clientUri - + '&referrer_uri=' + OAUTH3.uri.normalize(window.document.referrer) - ; - if (clientParams.debug) { - console.log('grantResults Redirect Attack'); - console.log(grantResults); - console.log(clientObj); - window.alert("DEBUG MODE checkRedirect error encountered. Check the console."); - } - location.href = callbackUrl; - return; + }).then(function (results) { + return results.grants; + }, function (err) { + if (!/no .*grants .*found/i.test(err.message)) { + console.error(err); } - - 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).grants.filter(function (grant) { - if (clientUri === (grant.azp || grant.oauth_client_id || grant.oauthClientId)) { - return true; + return []; + }).then(function (granted) { + var requested = OAUTH3.scope.parse(scope); + var accepted = []; + var pending = []; + requested.forEach(function (scp) { + if (granted.indexOf(scp) < 0) { + pending.push(scp); + } else { + accepted.push(scp); } }); - 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) + requested: requested // all requested, now + , granted: granted // all granted, ever + , accepted: accepted // intersection of granted (ever) and requested (now) + , pending: pending // not yet accepted }; }); }; @@ -381,34 +319,27 @@ OAUTH3.authz.grants = function (providerUri, opts) { client_id: providerUri , debug: opts.debug }).then(function (directive) { + return OAUTH3.request(OAUTH3.urls.grants(directive, opts), opts); + }).then(function (grantsResult) { + var grants = grantsResult.originalData || grantsResult.data; + if (grants.error) { + return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, grants)); + } + if ('POST' === opts.method) { + return grants; + } - return OAUTH3.request(OAUTH3.urls.grants(directive, opts), opts).then(function (grantsResult) { - if ('POST' === opts.method) { - // TODO this is clientToken - return grantsResult.originalData || grantsResult.data; - } - - var grants = grantsResult.originalData || grantsResult.data; - // TODO - if (grants.error) { - return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, grants)); - } - - OAUTH3.hooks.grants.set(opts.client_id + '-client', grants.client); - grants.grants.forEach(function (grant) { - var clientId = grant.client_id || grant.oauth_client_id || grant.oauthClientId; - // TODO should save as an array - OAUTH3.hooks.grants.set(clientId, [ grant ]); - }); - - return { - client: OAUTH3.hooks.grants.get(opts.client_id + '-client') - , grants: OAUTH3.hooks.grants.get(opts.client_id) || [] - }; - }); + OAUTH3.hooks.grants.set(grants.sub+'/'+grants.azp, grants.scope); + return { + client: grants.azp + , grants: OAUTH3.scope.parse(grants.scope) + }; }); }; OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, scopes) { + if (!OAUTH3.url.checkRedirect(clientParams.client_uri, clientParams)) { + return; + } scopes.new = scopes.new || []; @@ -427,15 +358,6 @@ OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, s // TODO limit refresh token to an expirable token // TODO inform client not to persist token - /* - if (clientParams.dnsTxt) { - Object.keys(results).forEach(function (key) { - if (/refresh/.test(key)) { - results[key] = undefined; - } - }); - } - */ OAUTH3.url.redirect(clientParams, scopes, results); }); } @@ -447,6 +369,7 @@ OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, s throw new Error("Authorization Code Redirect NOT IMPLEMENTED"); } }; + OAUTH3.requests = {}; OAUTH3.requests.accounts = {}; OAUTH3.requests.accounts.update = function (directive, session, opts) {