UNPKG

jwt-couchdb

Version:

JWT endpoint to authenticate users and create JSON Web Tokens out of the CouchDB's session API

153 lines (129 loc) 4.94 kB
import { spy, stub } from 'sinon'; import proxyquire from 'proxyquire'; import test from 'tape'; const authHeaderTest = stub(); authHeaderTest.withArgs('valid').returns(true); authHeaderTest.withArgs('valid token but wrong user').returns(true); authHeaderTest.returns(false); const userCtx = {'name': 'name', 'roles': ['role1']}; const userCtx2 = {'name': 'name', 'roles': ['role1', 'role2']}; const endpoint = 'endpoint'; const endpointSession = `${endpoint}/_session`; const endpointUser = `${endpoint}/_users/org.couchdb.user:${userCtx.name}`; const fetchAuth = stub(); fetchAuth.withArgs(endpointSession, 'valid').returns({userCtx}); fetchAuth.withArgs(endpointUser, 'valid').returns(userCtx2); fetchAuth.withArgs(endpointSession, 'valid token but wrong user').throws(); const url = 'url'; const urlRefreshRoles = 'url-refresh-roles'; const needsToRefreshRolesTest = stub(); needsToRefreshRolesTest.withArgs(urlRefreshRoles).returns(true); needsToRefreshRolesTest.returns(false); const options = 'options'; const secret = 'secret'; const token = 'token'; const tokenRefreshedRoles = 'token-refreshed-roles'; const sign = stub(); sign.withArgs(userCtx2, secret, options).returns(tokenRefreshedRoles); sign.returns(token); const createHandler = proxyquire.noCallThru()('../create-handler', { 'fetch-auth-node': fetchAuth, './has-valid-authorisation-header': { test: authHeaderTest }, './needs-to-refresh-roles': { test: needsToRefreshRolesTest }, jsonwebtoken: { sign } }).default; test('#createHandler', t => { t.equals(typeof createHandler(), 'function', 'returns a thunk'); t.end(); }); const methods = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE']; test(`#handler doesn't handle ${methods.join(', ')}`, async t => { const handler = createHandler(); const res = { end: spy(), writeHead: spy() }; try { await Promise.all(methods.map(method => handler({method, url}, res))); t.deepEquals(res.writeHead.args, methods.map(m => [404]), 'responds with 404'); t.equals(res.end.callCount, methods.length, 'ends the responses'); t.end(); } catch(e) { console.log(e); } }); test(`#handler fails with POST and an invalid auth header`, async t => { const handler = createHandler(); const res = { end: spy(), writeHead: spy() }; try { await handler({method: 'POST', headers: {authorization: 'invalid'}, url}, res); t.equals(res.writeHead.args[0][0], 404, 'responds with 404'); t.ok(res.end.called, 'ends the response'); t.end(); } catch(e) { console.log(e); } }); test('#handler handles POST with valid auth header returns token', async t => { const handler = createHandler({endpoint, options, secret}); const res = { end: spy(), writeHead: spy() }; try { await handler({method: 'POST', headers: {authorization: 'valid'}, url}, res); t.ok(fetchAuth.args[0], [endpointSession, 'valid'], 'authorises user'); t.deepEquals(sign.args[0], [userCtx, secret, options], 'creates a token'); t.deepEquals(res.writeHead.args[0], [200, {'Content-Type': 'application/json'}], 'responds with 200 and content-type application/json'); t.ok(res.end.args[0][0], `"${token}"`, 'ends the response with the token as a JSON string'); t.end(); } catch(e) { console.log(e); } }); test('#handler handles POST with valid auth header but invalid credentials ends up in 401', async t => { const handler = createHandler({endpoint, options, secret}); const res = { end: spy(), writeHead: spy() }; spy(console, 'error'); try { await handler({method: 'POST', headers: {authorization: 'valid token but wrong user'}, url}, res); t.ok(fetchAuth.args[1], [endpointSession, 'valid token but wrong user'], 'tries to authorise user'); t.ok(console.error.called, 'logs the error'); t.equals(res.writeHead.args[0][0], 401, 'responds with 401'); t.ok(res.end.called, 'ends the response'); console.error.restore(); t.end(); } catch(e) { console.log(e); } }); test('#handler handles POST with valid auth header that needs to refresh roles returns token with new roles', async t => { const handler = createHandler({endpoint, options, secret}); const res = { end: spy(), writeHead: spy() }; try { await handler({method: 'POST', headers: {authorization: 'valid'}, url: urlRefreshRoles}, res); t.ok(fetchAuth.args[2], [endpointSession, 'valid'], 'authorises user'); t.ok(fetchAuth.args[3], [endpointUser, 'valid'], 'fetches user'); t.deepEquals(sign.args[1], [userCtx2, secret, options], 'creates a token'); t.deepEquals(res.writeHead.args[0], [200, {'Content-Type': 'application/json'}], 'responds with 200 and content-type application/json'); t.ok(res.end.args[0][0], `"${tokenRefreshedRoles}"`, 'ends the response with the token as a JSON string'); t.end(); } catch(e) { console.log(e); } });