From 5875e4bb92a1968bfd08b1cae91168f143eb6dac Mon Sep 17 00:00:00 2001 From: Dan Backes Date: Mon, 7 Jan 2019 16:07:07 -0600 Subject: [PATCH 1/4] Strip Bearer From Token - Some libraries use the "Bearer" prefix when doing token authentication. If we encounter a token with a "Bearer" prefix then just strip it off and proceed as normal. --- index.js | 8 ++++++-- package.json | 2 +- test/index.js | 38 ++++++++++++++++++++++++++++++++++---- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 61f68f6..dcfa3bd 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const jwks = require('jwks-rsa') const jwt = require('jsonwebtoken') const { - applyTo: thrush, curryN, dissoc, partialRight, prop + applyTo: thrush, curryN, dissoc, partialRight, pipe, prop } = require('ramda') const { promisify, rename, tapP } = require('@articulate/funky') @@ -26,6 +26,9 @@ const chooseKey = key => const decode = partialRight(jwt.decode, [{ complete: true }]) +const stripBearer = token => + token ? token.replace(/^[B|b]earer /, '') : null + const enforce = token => token || Promise.reject(new Error('null token not allowed')) @@ -50,11 +53,12 @@ const factory = opts => { .then(cacheClient(iss)) .then(thrush(kid)) - const verify = curryN(2, partialRight(promisify(jwt.verify), [ verifyOpts ])) + const verify = pipe(stripBearer, curryN(2, partialRight(promisify(jwt.verify), [ verifyOpts ]))) const authentic = token => Promise.resolve(token) .then(tapP(enforce)) + .then(stripBearer) .then(decode) .then(tapP(checkIss)) .then(getSigningKey) diff --git a/package.json b/package.json index b1e5d02..c67d46e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@articulate/authentic", - "version": "0.1.2", + "version": "0.1.3", "description": "Proper validation of JWT's against JWK's", "main": "index.js", "repository": "git@github.com:articulate/authentic.git", diff --git a/test/index.js b/test/index.js index 608ef37..4510b6b 100644 --- a/test/index.js +++ b/test/index.js @@ -3,10 +3,12 @@ const jwt = require('jsonwebtoken') const nock = require('nock') const property = require('prop-factory') -const bad = require('./fixtures/bad-iss') -const keys = require('./fixtures/keys') -const oidc = require('./fixtures/oidc') -const token = require('./fixtures/token') +const bad = require('./fixtures/bad-iss') +const keys = require('./fixtures/keys') +const oidc = require('./fixtures/oidc') +const token = require('./fixtures/token') +const capitalBearerToken = 'Bearer ' + token +const lowerBearerToken = 'bearer ' + token const { issuer } = oidc @@ -47,6 +49,34 @@ describe('authentic', () => { ) }) + describe('with a valid jwt that starts with Bearer', () => { + beforeEach(() => + authentic(capitalBearerToken).then(res) + ) + + it('validates the jwt against the jwks', () => + expect(res().sub).to.equal('00udjyjssbt2S1QVr0h7') + ) + + it('caches the jwks client', () => + expect(res().sub).to.equal('00udjyjssbt2S1QVr0h7') + ) + }) + + describe('with a valid jwt that starts with bearer', () => { + beforeEach(() => + authentic(lowerBearerToken).then(res) + ) + + it('validates the jwt against the jwks', () => + expect(res().sub).to.equal('00udjyjssbt2S1QVr0h7') + ) + + it('caches the jwks client', () => + expect(res().sub).to.equal('00udjyjssbt2S1QVr0h7') + ) + }) + describe('with an invalid jwt', () => { beforeEach(() => authentic('invalid').catch(res) From 4e0b69a7e06441f17efefa5b2cd32533c8bcdf8e Mon Sep 17 00:00:00 2001 From: Dan Backes Date: Tue, 8 Jan 2019 12:53:03 -0600 Subject: [PATCH 2/4] PR Update --- index.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index dcfa3bd..4e7803c 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const jwks = require('jwks-rsa') const jwt = require('jsonwebtoken') const { - applyTo: thrush, curryN, dissoc, partialRight, pipe, prop + applyTo: thrush, curryN, dissoc, partialRight, prop, replace } = require('ramda') const { promisify, rename, tapP } = require('@articulate/funky') @@ -26,12 +26,12 @@ const chooseKey = key => const decode = partialRight(jwt.decode, [{ complete: true }]) -const stripBearer = token => - token ? token.replace(/^[B|b]earer /, '') : null - const enforce = token => token || Promise.reject(new Error('null token not allowed')) +const stripBearer = + replace(/^Bearer /i, '') + const unauthorized = err => Promise.reject(Boom.wrap(err, 401)) @@ -53,20 +53,22 @@ const factory = opts => { .then(cacheClient(iss)) .then(thrush(kid)) - const verify = pipe(stripBearer, curryN(2, partialRight(promisify(jwt.verify), [ verifyOpts ]))) + const verify = curryN(2, partialRight(promisify(jwt.verify), [ verifyOpts ])) const authentic = token => Promise.resolve(token) - .then(tapP(enforce)) - .then(stripBearer) .then(decode) .then(tapP(checkIss)) .then(getSigningKey) .then(chooseKey) .then(verify(token)) - .catch(unauthorized) - return authentic + return token => + Promise.resolve(token) + .then(tapP(enforce)) + .then(stripBearer) + .then(authentic) + .catch(unauthorized) } module.exports = factory From 1ef5739ba8d305f644e729d18141f3ac28811081 Mon Sep 17 00:00:00 2001 From: Dan Backes Date: Tue, 8 Jan 2019 12:54:34 -0600 Subject: [PATCH 3/4] set version back --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c67d46e..b1e5d02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@articulate/authentic", - "version": "0.1.3", + "version": "0.1.2", "description": "Proper validation of JWT's against JWK's", "main": "index.js", "repository": "git@github.com:articulate/authentic.git", From ff1cc6f0d426219e7918d8a616481f4086d37ee9 Mon Sep 17 00:00:00 2001 From: Dan Backes Date: Tue, 8 Jan 2019 14:37:56 -0600 Subject: [PATCH 4/4] PR Update --- index.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 4e7803c..d9064d8 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const jwks = require('jwks-rsa') const jwt = require('jsonwebtoken') const { - applyTo: thrush, curryN, dissoc, partialRight, prop, replace + applyTo: thrush, composeP, curryN, dissoc, partialRight, prop, replace } = require('ramda') const { promisify, rename, tapP } = require('@articulate/funky') @@ -27,7 +27,7 @@ const chooseKey = key => const decode = partialRight(jwt.decode, [{ complete: true }]) const enforce = token => - token || Promise.reject(new Error('null token not allowed')) + token || Promise.reject(Boom.unauthorized('null token not allowed')) const stripBearer = replace(/^Bearer /i, '') @@ -62,13 +62,9 @@ const factory = opts => { .then(getSigningKey) .then(chooseKey) .then(verify(token)) - - return token => - Promise.resolve(token) - .then(tapP(enforce)) - .then(stripBearer) - .then(authentic) .catch(unauthorized) + + return composeP(authentic, stripBearer, tapP(enforce)) } module.exports = factory