auth-server-helper/test/spec/AuthHandler.ts

266 lines
7.9 KiB
TypeScript
Raw Normal View History

2021-01-03 14:51:07 +01:00
/*
* Copyright (C) Sapphirecode - All Rights Reserved
* This file is part of Auth-Server-Helper which is released under MIT.
* See file 'LICENSE' for full license details.
* Created by Timo Hocker <timo@scode.ovh>, January 2021
*/
2021-01-01 14:14:19 +01:00
import http, { IncomingMessage, ServerResponse } from 'http';
import { to_b64 } from '@sapphirecode/encoding-helper';
2020-12-30 19:39:49 +01:00
import auth from '../../lib/Authority';
2021-01-06 22:43:03 +01:00
import {
2021-01-07 15:43:54 +01:00
clock_finalize,
clock_setup,
2021-01-06 22:43:03 +01:00
get, modify_signature, Response
} from '../Helper';
2021-01-05 21:35:45 +01:00
import { create_auth_handler } from '../../lib/index';
2020-12-30 19:39:49 +01:00
const expires_seconds = 600;
const refresh_expires_seconds = 3600;
2021-01-01 14:14:19 +01:00
const part_expires_seconds = 60;
interface CheckHeaderResult {
at: string;
rt?: string;
data: Record<string, unknown>;
}
function check_headers (resp: Response): CheckHeaderResult {
const data = JSON.parse (resp.body as string);
const at = data.access_token;
const rt = data.refresh_token;
expect (resp.headers['cache-control'])
.toEqual ('no-store');
expect (resp.headers.pragma)
.toEqual ('no-cache');
return { data, at, rt };
}
function check_token (token: string, type: string):void {
const v = auth.verify (token);
expect (v.valid)
.toEqual (true);
expect (v.authorized)
.toEqual (type === 'access_token');
expect (v.type)
.toEqual (type);
expect (token)
.toMatch (/^[0-9a-z-._~+/]+$/ui);
}
2020-12-30 19:39:49 +01:00
// eslint-disable-next-line max-lines-per-function
2021-01-01 14:14:19 +01:00
describe ('auth handler', () => {
2020-12-30 19:39:49 +01:00
let server: http.Server|null = null;
2021-01-06 22:43:03 +01:00
// eslint-disable-next-line max-lines-per-function
2020-12-30 19:39:49 +01:00
beforeAll (() => {
2021-01-07 15:43:54 +01:00
clock_setup ();
2021-01-06 22:43:03 +01:00
2021-01-01 14:14:19 +01:00
const ah = create_auth_handler ((req) => {
if (!req.is_basic && !req.is_bearer) {
req.invalid ('unknown authorization type');
}
else if (req.is_bearer) {
req.deny ();
2021-01-01 14:14:19 +01:00
}
2021-01-03 14:51:07 +01:00
else if (req.user === 'foo' && req.password === 'bar') {
2021-01-01 14:14:19 +01:00
req.allow_access ({
access_token_expires_in: expires_seconds,
include_refresh_token: true,
refresh_token_expires_in: refresh_expires_seconds
});
}
2021-01-03 14:51:07 +01:00
else if (req.user === 'part' && req.password === 'bar') {
req.allow_part (part_expires_seconds, 'two_factor');
}
else {
req.deny ();
}
2021-01-01 14:14:19 +01:00
}, {
cookie_name: 'cookie_jar',
refresh: {
access_token_expires_in: expires_seconds,
refresh_token_expires_in: refresh_expires_seconds,
include_refresh_token: true
2021-01-03 14:51:07 +01:00
},
modules: {
two_factor (request) {
if (request.body === 'letmein') {
request.allow_access ({
access_token_expires_in: expires_seconds,
include_refresh_token: true,
refresh_token_expires_in: refresh_expires_seconds
});
}
else { request.deny (); }
}
2021-01-01 14:14:19 +01:00
}
});
server = http.createServer ((req: IncomingMessage, res: ServerResponse) => {
ah (req, res);
2020-12-30 19:39:49 +01:00
});
server.listen (3000);
});
2021-01-06 22:43:03 +01:00
afterAll (() => {
if (server === null)
throw new Error ('server is null');
server.close ();
2021-01-07 15:43:54 +01:00
clock_finalize ();
2021-01-06 22:43:03 +01:00
});
2021-01-01 14:14:19 +01:00
it ('auth test sequence', async () => {
// get initial access and refresh tokens
const resp1 = await get ({ authorization: 'Basic foo:bar' });
expect (resp1.statusCode)
.toEqual (200);
const res1 = check_headers (resp1);
expect (res1.data.token_type)
.toEqual ('bearer');
expect (resp1.headers['set-cookie'])
.toContain (`cookie_jar=${res1.at}`);
check_token (res1.at as string, 'access_token');
expect (res1.data.expires_in)
.toEqual (expires_seconds);
check_token (res1.rt as string, 'refresh_token');
expect (res1.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
// get refresh token
const resp2 = await get ({ authorization: `Bearer ${res1.rt}` });
expect (resp2.statusCode)
.toEqual (200);
const res2 = check_headers (resp2);
expect (res2.data.token_type)
.toEqual ('bearer');
expect (resp2.headers['set-cookie'])
.toContain (`cookie_jar=${res2.at}`);
check_token (res2.at as string, 'access_token');
expect (res2.data.expires_in)
.toEqual (expires_seconds);
expect (res2.at).not.toEqual (res1.at);
check_token (res2.rt as string, 'refresh_token');
expect (res2.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
expect (res2.rt).not.toEqual (res1.rt);
});
it ('should return the correct denial message', async () => {
const resp = await get ({ authorization: 'Basic bar:baz' });
2020-12-30 19:39:49 +01:00
expect (resp.statusCode)
2021-01-01 14:14:19 +01:00
.toEqual (401);
const res = check_headers (resp);
expect (res.data)
.toEqual ({ error: 'invalid_client' });
});
it ('should allow base64 login', async () => {
// get initial access and refresh tokens
const resp1 = await get ({ authorization: `Basic ${to_b64 ('foo:bar')}` });
expect (resp1.statusCode)
2020-12-30 19:39:49 +01:00
.toEqual (200);
2021-01-01 14:14:19 +01:00
const res1 = check_headers (resp1);
expect (res1.data.token_type)
2020-12-30 19:39:49 +01:00
.toEqual ('bearer');
2021-01-01 14:14:19 +01:00
expect (resp1.headers['set-cookie'])
.toContain (`cookie_jar=${res1.at}`);
check_token (res1.at as string, 'access_token');
expect (res1.data.expires_in)
2020-12-30 19:39:49 +01:00
.toEqual (expires_seconds);
2021-01-01 14:14:19 +01:00
check_token (res1.rt as string, 'refresh_token');
expect (res1.data.refresh_expires_in)
2020-12-30 19:39:49 +01:00
.toEqual (refresh_expires_seconds);
2021-01-01 14:14:19 +01:00
});
it ('should reject invalid requests', async () => {
const resp1 = await get ();
expect (resp1.statusCode)
.toEqual (400);
2021-01-01 14:14:19 +01:00
const res1 = check_headers (resp1);
expect (res1.data)
.toEqual ({
error: 'invalid_request',
error_description: 'missing authorization header'
});
2021-01-01 14:14:19 +01:00
const resp2a = await get ({ authorization: 'Basic foo:bar' });
const res2a = check_headers (resp2a);
const resp2b = await get (
{ authorization: `Bearer ${res2a.at}` }
);
expect (resp2b.statusCode)
.toEqual (400);
const res2 = check_headers (resp2b);
expect (res2.data)
.toEqual ({
error: 'invalid_request',
error_description: 'invalid bearer type'
});
});
it ('should reject an invalid token', async () => {
const resp1 = await get ({ authorization: 'Basic foo:bar' });
const res1 = check_headers (resp1);
const resp2 = await get (
{ authorization: `Bearer ${modify_signature (res1.at)}` }
);
expect (resp2.statusCode)
.toEqual (401);
const res2 = check_headers (resp2);
expect (res2.data)
.toEqual ({ error: 'invalid_client' });
});
2020-12-30 19:39:49 +01:00
2021-01-03 14:51:07 +01:00
it ('should process part token', async () => {
2021-01-01 14:14:19 +01:00
const resp1 = await get ({ authorization: 'Basic part:bar' });
expect (resp1.statusCode)
.toEqual (200);
const res1 = check_headers (resp1);
expect (res1.data.token_type)
.toEqual ('bearer');
expect (res1.data.expires_in)
.toEqual (part_expires_seconds);
check_token (res1.data.part_token as string, 'part_token');
const resp2 = await get (
2021-01-03 14:51:07 +01:00
{ authorization: `Bearer ${res1.data.part_token}` },
'letmein'
2021-01-01 14:14:19 +01:00
);
expect (resp2.statusCode)
.toEqual (200);
const res2 = check_headers (resp2);
expect (res2.data.token_type)
.toEqual ('bearer');
expect (resp2.headers['set-cookie'])
.toContain (`cookie_jar=${res2.at}`);
check_token (res2.at as string, 'access_token');
expect (res2.data.expires_in)
.toEqual (expires_seconds);
expect (res2.at).not.toEqual (res1.at);
check_token (res2.rt as string, 'refresh_token');
expect (res2.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
expect (res2.rt).not.toEqual (res1.rt);
2020-12-30 19:39:49 +01:00
});
it ('should handle any authorization type', async () => {
const resp = await get ({ authorization: 'Foo asdefg' });
expect (resp.statusCode)
.toEqual (400);
expect (JSON.parse (resp.body as string))
.toEqual ({
error: 'invalid_request',
error_description: 'unknown authorization type'
});
});
2020-12-30 19:39:49 +01:00
});