From 35f4708d9acf25e69e452d1777208deb4886aa05 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 24 Jan 2017 10:43:46 -0700 Subject: [PATCH 1/4] use appId || clientId --- oauth3.core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index 812f14d..912d244 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -166,7 +166,7 @@ var redirectUri = opts.redirectUri; var scope = opts.scope || directive.authn_scope; - var clientId = opts.appId; + var clientId = opts.appId || opts.clientId; var args = directive[type]; var uri = args.url; var state = Math.random().toString().replace(/^0\./, ''); @@ -272,7 +272,7 @@ } var scope = opts.scope || directive.authn_scope; - var clientId = opts.appId; + var clientId = opts.appId || opts.clientId; var clientAgreeTos = opts.clientAgreeTos; var clientUri = opts.clientUri; var args = directive[type]; From 6f86d6ea6c3242e678387b7e323b9f246ca96230 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 24 Jan 2017 13:10:16 -0700 Subject: [PATCH 2/4] add unsecured jwt --- oauth3.core.js | 66 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index 912d244..ae76dce 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -33,10 +33,10 @@ // 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); - } + // parse a query or a hash + if (-1 !== ['#', '?'].indexOf(search[0])) { + search = search.substring(1); + } var args = search.split('&'); var argsParsed = {}; @@ -48,15 +48,15 @@ if (-1 === arg.indexOf('=')) { - argsParsed[decodeURIComponent(arg).trim()] = true; + argsParsed[decodeURIComponent(arg).trim()] = true; } else { - kvp = arg.split('='); - key = decodeURIComponent(kvp[0]).trim(); - value = decodeURIComponent(kvp[1]).trim(); - argsParsed[key] = value; + kvp = arg.split('='); + key = decodeURIComponent(kvp[0]).trim(); + value = decodeURIComponent(kvp[1]).trim(); + argsParsed[key] = value; } } @@ -64,6 +64,54 @@ return argsParsed; }; + core.utils = { + urlSafeBase64ToBase64: function (b64) { + // URL-safe Base64 to Base64 + b64 = b64.replace(/-/g, '+').replace(/_/g, '/'); + b64 = (b64 + '===').slice(0, b64.length + (b64.length % 4)); + return b64; + } + , base64ToUrlSafeBase64: function (b64) { + // Base64 to URL-safe Base64 + b64 = b64.replace(/\+/g, '-').replace(/\//g, '_'); + b64 = b64.replace(/=+/g, ''); + return b64; + } + }; + 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 (b64) { + var atob = exports.atob || require('atob'); + return atob(core.utils.urlSafeBase64ToBase64(b64)); + }); + + return { + header: JSON.parse(jsons[0]) + , payload: JSON.parse(jsons[1]) + , signature: parts[2] + }; + } + // encode-only (no signature) + , encode: function (parts) { + parts.header = parts.header || { alg: 'none', typ: 'jwt' }; + parts.signature = parts.signature || ''; + var result = [ + core.utils.base64ToUrlSafeBase64(JSON.stringify(parts.header, null)) + , core.utils.base64ToUrlSafeBase64(JSON.stringify(parts.payload, null)) + , parts.signature + ].join('.'); + + return result; + } + }; + core.authorizationCode = function (/*directive, scope, redirectUri, clientId*/) { // // Example Authorization Code Request From 0e7d7ecaea3081923a7983ed4a92f368dd591f2a Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 24 Jan 2017 13:16:21 -0700 Subject: [PATCH 3/4] fix url-safe base64 jwt encoding --- oauth3.core.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index ae76dce..6a29acb 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -64,6 +64,8 @@ return argsParsed; }; + // 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 @@ -95,17 +97,19 @@ return { header: JSON.parse(jsons[0]) , payload: JSON.parse(jsons[1]) - , signature: parts[2] + , signature: parts[2] // should remain url-safe base64 }; } // 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(JSON.stringify(parts.header, null)) - , core.utils.base64ToUrlSafeBase64(JSON.stringify(parts.payload, null)) - , parts.signature + 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; From 578e2b7354bacfacd7fff9f56b4b9f22a73ee3f6 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 24 Jan 2017 16:46:56 -0500 Subject: [PATCH 4/4] auto-refresh on request --- oauth3.js | 104 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 17 deletions(-) diff --git a/oauth3.js b/oauth3.js index dfb304a..e8bd127 100644 --- a/oauth3.js +++ b/oauth3.js @@ -73,14 +73,95 @@ }); }; - oauth3.provideRequest = function (request, opts) { + oauth3._recaseRequest = function (recase, req) { + // convert JavaScript camelCase to oauth3/ruby snake_case + if (req.data && 'object' === typeof req.data) { + req.originalData = req.data; + req.data = recase.snakeCopy(req.data); + } + + return req; + }; + oauth3._recaseResponse = function (recase, resp) { + // convert oauth3/ruby snake_case to JavaScript camelCase + if (resp.data && 'object' === typeof resp.data) { + resp.originalData = resp.data; + resp.data = recase.camelCopy(resp.data); + } + return resp; + }; + oauth3._lintRequest = function (preq, opts) { + var providerUri; + var fresh; + + console.log('[os] request meta opts', opts); + + // check that the JWT is not expired + // TODO check that this request applies to the aud and azp + if (!(preq.session && preq.session.accessToken)) { + console.log('[os] no session/accessTokenData'); + return oauth3.PromiseA.resolve(preq); + } + + preq.headers = preq.headers || {}; + preq.headers.Authorization = 'Bearer ' + preq.session.accessToken; + + if (!preq.session._accessTokenData) { + console.log('[os] no _accessTokenData'); + preq.session._accessTokenData = core.jwt.decode(preq.session.accessToken).payload; + } + + if (!preq.url.match(preq.session._accessTokenData.aud)) { + console.log("[os] doesn't match audience", preq.session._accessTokenData.aud); + return oauth3.PromiseA.resolve(preq); + } + + fresh = (Date.now() / 1000) >= (parseInt(preq.session._accessTokenData.exp) || 0); + if (!fresh) { + console.log("[os] isn't fresh", preq.session._accessTokenData.exp); + return oauth3.PromiseA.resolve(preq); + } + + if (!preq.session.refreshToken) { + console.log("[os] cann't refresh", preq.session); + return oauth3.PromiseA.resolve(preq); + } + + opts.refreshToken = preq.session.refreshToken; + console.log('[oauth3.js] refreshToken attempt'); + + // TODO include directive? + providerUri = preq.session.providerUri || preq.session._accessTokenData.iss; + //opts. + return oauth3.refreshToken(providerUri, opts).then(function (res) { + console.log('[oauth3.js] refreshToken result:', res); + + if (!res.data.accessToken) { + return preq; + } + + // TODO fire session update event + res.data.providerUri = preq.session.providerUri; + preq.session = res.data; + preq.headers.Authorization = 'Bearer ' + preq.session.accessToken; + return preq; + }); + }; + + oauth3.provideRequest = function (rawRequest, opts) { opts = opts || {}; var Recase = exports.Recase || require('recase'); // TODO make insensitive to providing exceptions var recase = Recase.create({ exceptions: {} }); + function lintAndRequest(preq) { + return oauth3._lintRequest(preq, opts).then(function (preq) { + return rawRequest(preq); + }); + } + if (opts.rawCase) { - oauth3.request = request; + oauth3.request = lintAndRequest; return; } @@ -90,23 +171,12 @@ opts = opts || {}; if (opts.rawCase) { - return request(req); + return lintAndRequest(req, opts); } - // convert JavaScript camelCase to oauth3 snake_case - if (req.data && 'object' === typeof req.data) { - req.originalData = req.data; - req.data = recase.snakeCopy(req.data); - } - - //console.log('[F] [oauth3 req.url]', req.url); - return request(req).then(function (resp) { - // convert oauth3 snake_case to JavaScript camelCase - if (resp.data && 'object' === typeof resp.data) { - resp.originalData = resp.data; - resp.data = recase.camelCopy(resp.data); - } - return resp; + req = oauth3._recaseRequest(recase, req); + return lintAndRequest(req, opts).then(function (res) { + return oauth3._recaseResponse(recase, res); }); };