diff --git a/oauth3.core.js b/oauth3.core.js index 321c8fe..37d7c63 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -78,7 +78,7 @@ , uri: { normalize: function (uri) { if ('string' !== typeof uri) { - console.error((new Error('stack')).stack); + throw new Error("attempted to normalize non-string URI: "+JSON.stringify(uri)); } // tested with // example.com @@ -94,7 +94,7 @@ , url: { normalize: function (url) { if ('string' !== typeof url) { - console.error((new Error('stack')).stack); + throw new Error("attempted to normalize non-string URL: "+JSON.stringify(url)); } // tested with // example.com diff --git a/oauth3.issuer.js b/oauth3.issuer.js index cacd206..d45c1e1 100644 --- a/oauth3.issuer.js +++ b/oauth3.issuer.js @@ -102,8 +102,6 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) { // , "username": "<>", "password": "password" } // opts = opts || {}; - var type = 'access_token'; - var grantType = 'password'; if (!opts.password) { if (opts.otp) { @@ -112,16 +110,13 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) { } } - var scope = opts.scope || directive.authn_scope; - var clientAgreeTos = 'oauth3.org/tos/draft'; // opts.clientAgreeTos || opts.client_agree_tos; - var clientUri = opts.client_uri; - var args = directive[type]; + var args = directive.access_token; var otpCode = opts.otp || opts.otpCode || opts.otp_code || opts.otpToken || opts.otp_token || undefined; // TODO require user agent var params = { client_id: opts.client_id || opts.client_uri , client_uri: opts.client_uri - , grant_type: grantType + , grant_type: 'password' , username: opts.username , password: opts.password || otpCode || undefined , totp: opts.totp || opts.totpToken || opts.totp_token || undefined @@ -136,23 +131,21 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) { //, "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 (clientUri) { - params.clientAgreeTos = clientAgreeTos; - if (!clientAgreeTos) { + if (opts.client_uri) { + params.clientAgreeTos = 'oauth3.org/tos/draft'; // opts.clientAgreeTos || opts.client_agree_tos; + if (!params.clientAgreeTos) { throw new Error('Developer Error: missing clientAgreeTos uri'); } } + var scope = opts.scope || directive.authn_scope; if (scope) { params.scope = OAUTH3.scope.stringify(scope); } + var uri = args.url; + var body; if ('GET' === args.method.toUpperCase()) { uri += '?' + OAUTH3.query.stringify(params); } else { @@ -168,6 +161,10 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) { OAUTH3.urls.grants = function (directive, opts) { // directive = { issuer, authorization_decision } // opts = { response_type, scopes{ granted, requested, pending, accepted } } + var grantsDir = directive.grants; + if (!grantsDir) { + throw new Error("provider doesn't support grants"); + } if (!opts) { throw new Error("You must supply a directive and an options object."); @@ -183,15 +180,18 @@ OAUTH3.urls.grants = function (directive, opts) { } if (!opts.method) { console.warn("You should supply options.method as either 'GET', or 'POST'"); - opts.method = 'GET'; + opts.method = grantsDir.method || 'GET'; } if ('POST' === opts.method) { if ('string' !== typeof opts.scope) { throw new Error("You must supply options.scope as a comma-delimited string of scopes"); } + if ('string' !== typeof opts.sub) { + console.log("provide 'sub' to urls.grants to specify the PPID for the client"); + } } - var url = OAUTH3.url.resolve(directive.api, directive.grants.url) + var url = OAUTH3.url.resolve(directive.api, grantsDir.url) .replace(/(:azp|:client_id)/g, OAUTH3.uri.normalize(opts.client_id || opts.client_uri)) .replace(/(:sub|:account_id)/g, opts.session.token.sub || 'ISSUER:GRANT:TOKEN_SUB:UNDEFINED') ; @@ -200,13 +200,13 @@ OAUTH3.urls.grants = function (directive, opts) { , client_uri: opts.client_uri , referrer: opts.referrer , scope: opts.scope + , sub: opts.sub }; var body; if ('GET' === opts.method) { url += '?' + OAUTH3.query.stringify(data); - } - else { + } else { body = data; } @@ -217,6 +217,50 @@ OAUTH3.urls.grants = function (directive, opts) { , session: opts.session }; }; +OAUTH3.urls.clientToken = function (directive, opts) { + var tokenDir = directive.access_token; + if (!tokenDir) { + throw new Error("provider doesn't support getting access tokens"); + } + + if (!opts) { + throw new Error("You must supply a directive and an options object."); + } + if (!(opts.azp || 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.method) { + opts.method = tokenDir.method || 'POST'; + } + + var params = { + grant_type: 'issuer_token' + , client_id: opts.azp || opts.client_id + , azp: opts.azp || opts.client_id + , aud: opts.aud + , exp: opts.exp + , refresh_token: opts.refresh_token + , refresh_exp: opts.refresh_exp + }; + + var url = OAUTH3.url.resolve(directive.api, tokenDir.url); + var body; + if ('GET' === opts.method) { + url += '?' + OAUTH3.query.stringify(params); + } else { + body = params; + } + + return { + method: opts.method + , url: url + , data: body + , session: opts.session + }; +}; OAUTH3.authn = {}; OAUTH3.authn.loginMeta = function (directive, opts) { @@ -340,34 +384,51 @@ OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, s if (!OAUTH3.url.checkRedirect(clientParams.client_uri, clientParams)) { return; } + if ('token' !== clientParams.response_type) { + var message; + if ('code' === clientParams.response_type) { + message = "Authorization Code Redirect NOT IMPLEMENTED"; + } else { + message = "Authorization response type '"+clientParams.response_type+"' not supported"; + } + window.alert(message); + throw new Error(message); + } - scopes.new = scopes.new || []; - - if ('token' === clientParams.response_type) { - // get token and redirect client-side - return OAUTH3.authz.grants(providerUri, { - method: 'POST' + var prom; + if (scopes.new) { + prom = OAUTH3.authz.grants(providerUri, { + session: session + , method: 'POST' , client_id: clientParams.client_uri - , client_uri: clientParams.client_uri - , scope: scopes.granted.concat(scopes.new).join(',') - , response_type: clientParams.response_type , referrer: clientParams.referrer - , session: session - , debug: clientParams.debug - }).then(function (results) { - - // TODO limit refresh token to an expirable token - // TODO inform client not to persist token - OAUTH3.url.redirect(clientParams, scopes, results); + , scope: scopes.accepted.concat(scopes.new).join(',') }); + } else { + prom = OAUTH3.PromiseA.resolve(); } - else if ('code' === clientParams.response_type) { - // get token and redirect server-side - // (requires insecure form post as per spec) - //OAUTH3.requests.authorizationDecision(); - window.alert("Authorization Code Redirect NOT IMPLEMENTED"); - throw new Error("Authorization Code Redirect NOT IMPLEMENTED"); - } + + return prom.then(function () { + return OAUTH3.discover(providerUri, { client_id: providerUri, debug: clientParams.debug }); + }).then(function (directive) { + return OAUTH3.request(OAUTH3.urls.clientToken(directive, { + method: 'POST' + , session: session + , referrer: clientParams.referrer + , response_type: clientParams.response_type + , client_id: clientParams.client_uri + , azp: clientParams.client_uri + , aud: clientParams.aud + , exp: clientParams.exp + , refresh_token: clientParams.refresh_token + , refresh_exp: clientParams.refresh_exp + , debug: clientParams.debug + })); + }).then(function (results) { + // TODO limit refresh token to an expirable token + // TODO inform client not to persist token + OAUTH3.url.redirect(clientParams, scopes, results.originalData || results.data); + }); }; OAUTH3.requests = {};