8c174360b1b815790024c5c59640a57583c9edc7
[websub-hub] / test / src / authenticator.js
1 /* eslint-env mocha */
2 'use strict';
3
4 const assert = require('assert');
5 const sinon = require('sinon');
6 const Authenticator = require('../../src/authenticator');
7 const stubLogger = require('../stub-logger');
8 const stubDb = require('../stub-db');
9 const Errors = require('../../src/errors');
10 const Enum = require('../../src/enum');
11
12 const noExpectedException = 'did not receive expected exception';
13
14 describe('Authenticator', function () {
15 let authenticator, credential, ctx, identifier, password, options;
16 beforeEach(function () {
17 options = {
18 authenticator: {
19 basicRealm: 'realm',
20 secureAuthOnly: true,
21 },
22 };
23 authenticator = new Authenticator(stubLogger, stubDb, options);
24 identifier = 'username';
25 credential = '$argon2id$v=19$m=4096,t=3,p=1$1a6zRlX4BI4$sZGcQ72BTpDOlxUI/j3DmE1PMcu+Cs5liZ/D6kk79Ew';
26 ctx = {};
27 password = 'badPassword';
28 });
29 afterEach(function () {
30 sinon.restore();
31 });
32
33 describe('isValidBasic', function () {
34 it('succeeds', async function () {
35 sinon.stub(authenticator.db, 'authenticationGet').resolves({
36 identifier,
37 credential,
38 });
39 const authString = `${identifier}:${password}`;
40 const result = await authenticator.isValidBasic(authString, ctx);
41 assert.strictEqual(result, true);
42 assert.strictEqual(ctx.authenticationId, identifier);
43 });
44 it('fails', async function () {
45 sinon.stub(authenticator.db, 'authenticationGet').resolves({
46 identifier,
47 credential,
48 });
49 const authString = `${identifier}:wrongPassword}`;
50 const result = await authenticator.isValidBasic(authString, ctx);
51 assert.strictEqual(result, false);
52 assert.strictEqual(ctx.authenticationId, undefined);
53 });
54 it('covers no entry', async function() {
55 sinon.stub(authenticator.db, 'authenticationGet').resolves();
56 const authString = `${identifier}:wrongPassword}`;
57 const result = await authenticator.isValidBasic(authString, ctx);
58 assert.strictEqual(result, false);
59 assert.strictEqual(ctx.authenticationId, undefined);
60 });
61 it('covers unknown password hash', async function () {
62 sinon.stub(authenticator.db, 'authenticationGet').resolves({
63 identifier,
64 credential: '$other$kind_of_credential',
65 });
66 const authString = `${identifier}:wrongPassword}`;
67 const result = await authenticator.isValidBasic(authString, ctx);
68 assert.strictEqual(result, false);
69 assert.strictEqual(ctx.authenticationId, undefined);
70 });
71 }); // isValidBasic
72
73 describe('isValidAuthorization', function () {
74 it('handles basic', async function () {
75 const expected = true;
76 const authorizationHeader = 'basic Zm9vOmJhcg==';
77 sinon.stub(authenticator, 'isValidBasic').resolves(expected);
78 const result = await authenticator.isValidAuthorization(authorizationHeader, ctx);
79 assert.strictEqual(result, expected);
80 });
81 it('handles other', async function () {
82 const expected = false;
83 const authorizationHeader = 'bearer Zm9vOmJhcg==';
84 const result = await authenticator.isValidAuthorization(authorizationHeader, ctx);
85 assert.strictEqual(result, expected);
86 });
87 }); // isValidAuthorization
88
89 describe('requestBasic', function () {
90 it('covers', function () {
91 try {
92 const res = {
93 setHeader: () => {},
94 };
95 authenticator.requestBasic(res);
96 assert.fail(noExpectedException);
97 } catch (e) {
98 assert(e instanceof Errors.ResponseError);
99 assert.strictEqual(e.statusCode, Enum.ErrorResponse.Unauthorized.statusCode);
100 }
101 });
102 }); // requestBasic
103
104 describe('required', function () {
105 let req, res;
106 beforeEach(function () {
107 ctx.clientProtocol = 'https';
108 req = {
109 getHeader: sinon.stub(),
110 };
111 res = {
112 setHeader: sinon.stub(),
113 }
114 });
115 it('succeeds', async function() {
116 req.getHeader.returns('auth header');
117 sinon.stub(authenticator, 'isValidAuthorization').resolves(true);
118 const result = await authenticator.required(req, res, ctx);
119 assert.strictEqual(result, true);
120 });
121 it('rejects insecure connection', async function () {
122 ctx.clientProtocol = 'http';
123 try {
124 await authenticator.required(req, res, ctx);
125 assert.fail(noExpectedException);
126 } catch (e) {
127 assert(e instanceof Errors.ResponseError);
128 assert.strictEqual(e.statusCode, Enum.ErrorResponse.Forbidden.statusCode);
129 }
130 });
131 it('rejects invalid auth', async function () {
132 try {
133 req.getHeader.returns('auth header');
134 sinon.stub(authenticator, 'isValidAuthorization').resolves(false);
135 await authenticator.required(req, res, ctx);
136 assert.fail(noExpectedException);
137 } catch (e) {
138 assert(e instanceof Errors.ResponseError);
139 assert.strictEqual(e.statusCode, Enum.ErrorResponse.Unauthorized.statusCode);
140 }
141 });
142 }); // required
143 }); // Authenticator