ac-node-hipchat
Version:
A common module plugin for building Atlassian Connect add-ons for HipChat
196 lines (175 loc) • 6.38 kB
JavaScript
var assert = require('assert');
var _ = require('lodash');
var jwt = require('jwt-simple');
var MemoryStore = require('ac-node').MemoryStore;
var Tenants = require('ac-node').Tenants;
var authenticator = require('..').authenticator;
var fixtures = require('./fixtures');
var tenant = fixtures.load('tenant.json');
describe('ac hipchat authenticator', function () {
var store;
var tenantStore;
var tenants;
beforeEach(function *() {
store = MemoryStore();
tenantStore = store.narrow('tenant');
tenants = Tenants(tenantStore);
yield tenantStore.set(tenant.id, tenant);
});
it('should create an authenticate fn from tenants and nodeEnv', function *() {
assert.ok(typeof authenticator('production', tenants) === 'function');
});
it('should fail to authenticate given neither a signedRequestParam nor an authorizationHeader', function *() {
var authenticate = authenticator('production', tenants);
try {
yield authenticate();
assert.fail();
} catch (err) {
assert.equal(err.message, 'Request signature required but not found');
}
});
it('should fail to authenticate given a malformed authorization header', function *() {
var authenticate = authenticator('production', tenants);
try {
yield authenticate(null, 'malformed');
assert.fail();
} catch (err) {
assert.equal(err.message, 'Authorization header was either incompatible or malformed');
}
});
it('should fail to authenticate given a claim that lacks an issuer', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims({
iss: null
});
var token = createToken(claims);
try {
yield authenticate(token);
assert.fail();
} catch (err) {
assert.equal(err.message, 'Request signature did not contain an issuer claim');
}
});
it('should fail to authenticate given a claim that lacks an issued-at timestamp', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims({
iat: null
});
var token = createToken(claims);
try {
yield authenticate(token);
assert.fail();
} catch (err) {
assert.equal(err.message, 'Request signature did not contain an issued-at timestamp');
}
});
it('should fail to authenticate given a claim issued by an unknown tenant', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims({
iss: 'unknown'
});
var token = createToken(claims);
try {
yield authenticate(token);
assert.fail();
} catch (err) {
assert.equal(err.message, 'Request signature contained unknown issuer: unknown');
}
});
it('should fail to authenticate given a claim with an invalid signature', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims();
var token = createToken(claims) + 'fail';
try {
yield authenticate(token);
assert.fail();
} catch (err) {
assert.ok(err.message.indexOf('Request signature verification failed: ') === 0);
}
});
it('should fail to authenticate given a claim with an expired expiry', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims({
iat: 0,
exp: 1
});
var token = createToken(claims);
try {
yield authenticate(token);
assert.fail();
} catch (err) {
assert.equal(err.message, 'Request signature expired');
}
});
it('should ignore expiry expiration when not running in production mode', function *() {
var authenticate = authenticator('development', tenants);
var claims = createClaims({
iat: 0,
exp: 1
});
var token = createToken(claims);
yield authenticate(token);
});
it('should fail to authenticate given a claim with an issued-at timestamp greater than authTokenTtl minutes ago', function *() {
var authenticate = authenticator('production', tenants, {
authTokenTtl: 1 // minutes
});
var claims = createClaims({
iat: Math.floor(Date.now() / 1000) - 62 // seconds
});
var token = createToken(claims);
try {
yield authenticate(token);
assert.fail();
} catch (err) {
assert.equal(err.message, 'Request signature expired');
}
});
it('should ignore authTokenTtl expiration when not running in production mode', function *() {
var authenticate = authenticator('development', tenants, {
authTokenTtl: 1 // minutes
});
var claims = createClaims({
iat: Math.floor(Date.now() / 1000) - 62 // seconds
});
var token = createToken(claims);
yield authenticate(token);
});
it('should successfully authenticate given a valid signed request param', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims();
var token = createToken(claims);
var authentication = yield authenticate(token);
assert.ok(authentication);
assert.equal(authentication.issuer, tenant);
assert.ok(authentication.issued > claims.iat);
assert.equal(authentication.userId, claims.prn);
assert.equal(authentication.expiry, authentication.issued + (15 * 60));
assert.deepEqual(authentication.context, claims.context);
jwt.decode(authentication.token, tenant.secret);
});
it('should successfully authenticate given a valid authorization header', function *() {
var authenticate = authenticator('production', tenants);
var claims = createClaims();
var token = createToken(claims);
var authentication = yield authenticate(null, 'JWT token=' + token);
assert.ok(authentication);
assert.equal(authentication.issuer, tenant);
assert.ok(authentication.issued > claims.iat);
assert.equal(authentication.userId, claims.prn);
assert.equal(authentication.expiry, authentication.issued + (15 * 60));
assert.deepEqual(authentication.context, claims.context);
jwt.decode(authentication.token, tenant.secret);
});
});
function createClaims(overrides) {
return _.extend({
iss: tenant.id,
iat: Math.floor(Date.now() / 1000) - 10,
prn: 1,
context: {tz: 'GMT'}
}, overrides);
}
function createToken(claims) {
return jwt.encode(claims, tenant.secret);
}