Permalink
Browse files

close #40 by adding automatic authorization header parsing

  • Loading branch information...
jeremydaly committed May 15, 2018
1 parent a8d33ca commit a35e3d8a8cee786f345423a21498cd80a5c4c9fe
Showing with 151 additions and 4 deletions.
  1. +6 −3 lib/request.js
  2. +26 −0 lib/utils.js
  3. +55 −1 test/headers.js
  4. +64 −0 test/utils.js
@@ -7,7 +7,7 @@
*/

const QS = require('querystring') // Require the querystring library
const parseBody = require('./utils.js').parseBody
const UTILS = require('./utils.js') // Require utils library

class REQUEST {

@@ -47,11 +47,14 @@ class REQUEST {
.reduce(
(acc,cookie) => {
cookie = cookie.trim().split('=')
return Object.assign(acc,{ [cookie[0]] : parseBody(decodeURIComponent(cookie[1])) })
return Object.assign(acc,{ [cookie[0]] : UTILS.parseBody(decodeURIComponent(cookie[1])) })
},
{}
) : {}

// Attempt to parse the auth
this.auth = UTILS.parseAuth(this.headers.authorization)

// Set the requestContext
this.requestContext = app._event.requestContext

@@ -67,7 +70,7 @@ class REQUEST {
} else if (typeof this.body === 'object') {
this.body = this.body
} else {
this.body = parseBody(this.body)
this.body = UTILS.parseBody(this.body)
}

// Extract path from event (strip querystring just in case)
@@ -6,6 +6,8 @@
* @license MIT
*/

const QS = require('querystring') // Require the querystring library

const entityMap = {
"&": "&",
"<": "&lt;",
@@ -43,6 +45,30 @@ module.exports.parseBody = body => {



// Parses auth values into known formats
const parseAuthValue = (type,value) => {
switch (type) {
case 'Basic':
let creds = Buffer.from(value, 'base64').toString().split(':')
return { type, value, username: creds[0], password: creds[1] ? creds[1] : null }
case 'OAuth':
let params = QS.parse(value.replace(/",\s*/g,'&').replace(/"/g,'').trim())
return Object.assign({ type, value }, params)
default:
return { type, value }
}
}

module.exports.parseAuth = authStr => {
let auth = authStr && typeof authStr === 'string' ? authStr.split(' ') : []
return auth.length > 1 && ['Bearer','Basic','Digest','OAuth'].includes(auth[0]) ?
parseAuthValue(auth[0], auth.slice(1).join(' ').trim()) :
{ type: 'none', value: null }
}




const mimeMap = require('./mimemap.js') // MIME Map

module.exports.mimeLookup = (input,custom={}) => {
@@ -89,6 +89,12 @@ api.get('/corsOverride', function(req,res) {
}).json({})
})

api.get('/auth', function(req,res) {
res.json({
auth: req.auth
})
})


/******************************************************************************/
/*** BEGIN TESTS ***/
@@ -207,6 +213,54 @@ describe('Header Tests:', function() {
isBase64Encoded: false
})
}) // end it

}) // end CORS tests


describe('Authorization Tests:', function() {

it('Bearer (OAuth2/JWT)', async function() {
let _event = Object.assign({},event,{ path: '/auth', headers: { authorization: "Bearer XYZ" } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{"auth":{"type":"Bearer","value":"XYZ"}}', isBase64Encoded: false })
}) // end it

it('Digest', async function() {
let _event = Object.assign({},event,{ path: '/auth', headers: { authorization: "Digest XYZ" } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{"auth":{"type":"Digest","value":"XYZ"}}', isBase64Encoded: false })
}) // end it

it('Basic Auth', async function() {
let creds = new Buffer('test:testing').toString('base64')
let _event = Object.assign({},event,{ path: '/auth', headers: { authorization: "Basic " + creds } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{"auth":{"type":"Basic","value":"dGVzdDp0ZXN0aW5n","username":"test","password":"testing"}}', isBase64Encoded: false })
}) // end it

it('OAuth 1.0', async function() {
let _event = Object.assign({},event,{ path: '/auth', headers: { authorization: 'OAuth realm="Example", oauth_consumer_key="xyz", oauth_token="abc", oauth_version="1.0"' } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{\"auth\":{\"type\":\"OAuth\",\"value\":\"realm=\\\"Example\\\", oauth_consumer_key=\\\"xyz\\\", oauth_token=\\\"abc\\\", oauth_version=\\\"1.0\\\"\",\"realm\":\"Example\",\"oauth_consumer_key\":\"xyz\",\"oauth_token\":\"abc\",\"oauth_version\":\"1.0\"}}', isBase64Encoded: false })
}) // end it

it('Missing Authorization Header', async function() {
let _event = Object.assign({},event,{ path: '/auth', headers: { } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{"auth":{"type":"none","value":null}}', isBase64Encoded: false })
}) // end it

it('Invalid Schema', async function() {
let _event = Object.assign({},event,{ path: '/auth', headers: { authorization: "Test XYZ" } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{"auth":{"type":"none","value":null}}', isBase64Encoded: false })
}) // end it

it('Incomplete Header', async function() {
let _event = Object.assign({},event,{ path: '/auth', headers: { authorization: "Bearer" } })
let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
expect(result).to.deep.equal({ headers: { 'content-type': 'application/json' }, statusCode: 200, body: '{"auth":{"type":"none","value":null}}', isBase64Encoded: false })
}) // end it

}) // end Auth tests

}) // end HEADER tests
@@ -83,6 +83,70 @@ describe('Utility Function Tests:', function() {
}) // end encodeBody tests


describe('parseAuth:', function() {

it('None: undefined', function() {
let result = utils.parseAuth(undefined)
expect(result).to.deep.equal({ type: 'none', value: null })
}) // end it

it('None: empty', function() {
let result = utils.parseAuth('')
expect(result).to.deep.equal({ type: 'none', value: null })
}) // end it

it('Invalid schema', function() {
let result = utils.parseAuth('Test 12345')
expect(result).to.deep.equal({ type: 'none', value: null })
}) // end it

it('Missing value/token', function() {
let result = utils.parseAuth('Bearer')
expect(result).to.deep.equal({ type: 'none', value: null })
}) // end it

it('Bearer Token (OAuth2/JWT)', function() {
let result = utils.parseAuth('Bearer XYZ')
expect(result).to.deep.equal({ type: 'Bearer', value: 'XYZ' })
}) // end it

it('Digest', function() {
let result = utils.parseAuth('Digest XYZ')
expect(result).to.deep.equal({ type: 'Digest', value: 'XYZ' })
}) // end it

it('OAuth 1.0', function() {
let result = utils.parseAuth('OAuth realm="Example", oauth_consumer_key="xyz", oauth_token="abc", oauth_version="1.0"')
expect(result).to.deep.equal({
type: 'OAuth',
value: 'realm="Example", oauth_consumer_key="xyz", oauth_token="abc", oauth_version="1.0"',
realm: 'Example',
oauth_consumer_key: 'xyz',
oauth_token: 'abc',
oauth_version: '1.0'
})
}) // end it

it('Basic', function() {
let creds = new Buffer('test:testing').toString('base64')
let result = utils.parseAuth('Basic ' + creds)
expect(result).to.deep.equal({ type: 'Basic', value: creds, username: 'test', password: 'testing' })
}) // end it

it('Basic (no password)', function() {
let creds = new Buffer('test').toString('base64')
let result = utils.parseAuth('Basic ' + creds)
expect(result).to.deep.equal({ type: 'Basic', value: creds, username: 'test', password: null })
}) // end it

it('Invalid type', function() {
let result = utils.parseAuth(123)
expect(result).to.deep.equal({ type: 'none', value: null })
}) // end it

}) // end encodeBody tests


describe('mimeLookup:', function() {

it('.pdf', function() {

0 comments on commit a35e3d8

Please sign in to comment.