diff --git a/oauth3.issuer.mock.js b/oauth3.issuer.mock.js index 5f4966a..8208476 100644 --- a/oauth3.issuer.mock.js +++ b/oauth3.issuer.mock.js @@ -3,28 +3,52 @@ var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; - OAUTH3._base64.btoa = function (b64) { - // http://stackoverflow.com/questions/9677985/uncaught-typeerror-illegal-invocation-in-chrome - return (exports.btoa || require('btoa'))(b64); + OAUTH3.utils.bufferToBinaryString = function (buf) { + return Array.prototype.map.call(new Uint8Array(buf), function(ch) { + return String.fromCharCode(ch); + }).join(''); }; - OAUTH3._base64.encodeUrlSafe = function (b64) { - // Base64 to URL-safe Base64 - b64 = b64.replace(/\+/g, '-').replace(/\//g, '_'); - b64 = b64.replace(/=+/g, ''); - return OAUTH3._base64.btoa(b64); + OAUTH3.utils.binaryStringToBuffer = function (str) { + var buf; + + if ('undefined' !== typeof Uint8Array) { + buf = new Uint8Array(str.length); + } else { + buf = []; + } + + Array.prototype.forEach.call(str, function (ch, ind) { + buf[ind] = ch.charCodeAt(0); + }); + return buf; }; - OAUTH3.jwt.encode = function (parts) { - parts.header = parts.header || { alg: 'none', typ: 'jwt' }; - parts.signature = parts.signature || ''; + OAUTH3.crypto = {}; + OAUTH3.crypto._generateKey = function () { + return window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify']) + .catch(function (err) { + console.error('failed to generate ECDSA key', err); + return OAUTH3.PromiseA.reject(err); + }); + }; - var result = [ - OAUTH3._base64.encodeUrlSafe(JSON.stringify(parts.header, null)) - , OAUTH3._base64.encodeUrlSafe(JSON.stringify(parts.payload, null)) - , parts.signature // should already be url-safe base64 - ].join('.'); + OAUTH3.crypto._signPayload = function (payload) { + return OAUTH3.crypto._generateKey().then(function (key) { + var header = {type: 'jwt', alg: 'ES256'}; + var input = [ + OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null)) + , OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null)) + ].join('.'); - return result; + return window.crypto.subtle.sign( + {name: 'ECDSA', hash: {name: 'SHA-256'}} + , key.privateKey + , OAUTH3.utils.binaryStringToBuffer(input) + ).then(function (signature) { + var base64Sig = OAUTH3._base64.encodeUrlSafe(OAUTH3.utils.bufferToBinaryString(signature)); + return OAUTH3.PromiseA.resolve(input + '.' + base64Sig); + }); + }); }; OAUTH3.authn.resourceOwnerPassword = OAUTH3.authz.resourceOwnerPassword = function (directive, opts) { @@ -82,24 +106,21 @@ }; OAUTH3._mockToken = function (providerUri, opts) { - var accessToken = OAUTH3.jwt.encode({ - header: { alg: 'none' } - , payload: { exp: Math.round(Date.now() / 1000) + 900, sub: 'fakeUserId', scp: opts.scope } - , signature: "fakeSig" + var payload = { exp: Math.round(Date.now() / 1000) + 900, sub: 'fakeUserId', scp: opts.scope }; + return OAUTH3.crypto._signPayload(payload).then(function (accessToken) { + return OAUTH3.hooks.session.refresh( + opts.session || { + provider_uri: providerUri + , client_id: opts.client_id + , client_uri: opts.client_uri || opts.clientUri + } + , { access_token: accessToken + , refresh_token: accessToken + , expires_in: "900" + , scope: opts.scope + } + ); }); - - return OAUTH3.hooks.session.refresh( - opts.session || { - provider_uri: providerUri - , client_id: opts.client_id - , client_uri: opts.client_uri || opts.clientUri - } - , { access_token: accessToken - , refresh_token: accessToken - , expires_in: "900" - , scope: opts.scope - } - ); }; }('undefined' !== typeof exports ? exports : window));