swaf/test/Authentication.test.ts

648 lines
21 KiB
TypeScript

import TestApp from "../src/TestApp";
import useApp from "./_app";
import Controller from "../src/Controller";
import supertest from "supertest";
import CsrfProtectionComponent from "../src/components/CsrfProtectionComponent";
import User from "../src/auth/models/User";
import UserNameComponent from "../src/auth/models/UserNameComponent";
import UserPasswordComponent from "../src/auth/password/UserPasswordComponent";
import {popEmail} from "./_mail_server";
import AuthComponent from "../src/auth/AuthComponent";
import {followMagicLinkFromMail, testLogout} from "./_authentication_common";
let app: TestApp;
useApp(async (addr, port) => {
return app = new class extends TestApp {
protected async init(): Promise<void> {
this.use(new class extends Controller {
public routes(): void {
this.get('/', (req, res) => {
res.render('home');
}, 'home');
this.get('/csrf', (req, res) => {
res.send(CsrfProtectionComponent.getCsrfToken(req.getSession()));
}, 'csrf');
this.get('/is-auth', async (req, res) => {
const proofs = await this.getApp().as(AuthComponent).getAuthGuard().getProofs(req);
if (proofs.length > 0) res.sendStatus(200);
else res.sendStatus(401);
}, 'is-auth');
}
}());
await super.init();
}
}(addr, port);
});
let agent: supertest.SuperTest<supertest.Test>;
beforeAll(() => {
agent = supertest(app.getExpressApp());
});
describe('Register with username and password (password)', () => {
let cookies: string[];
let csrf: string;
test('General case', async () => {
const res = await agent.get('/csrf').expect(200);
cookies = res.get('Set-Cookie');
csrf = res.text;
// Register user
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'password',
identifier: 'entrapta',
password: 'darla_is_cute',
password_confirmation: 'darla_is_cute',
terms: 'on',
})
.expect(302)
.expect('Location', '/');
// Verify saved user
const user = await User.select()
.where('name', 'entrapta')
.first();
expect(user).toBeDefined();
expect(user?.as(UserNameComponent).name).toStrictEqual('entrapta');
await expect(user?.as(UserPasswordComponent).verifyPassword('darla_is_cute')).resolves.toStrictEqual(true);
});
test('Can\'t register when logged in', async () => {
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'password',
identifier: 'entrapta2',
password: 'darla_is_cute',
password_confirmation: 'darla_is_cute',
terms: 'on',
})
.expect(302)
.expect('Location', '/csrf');
const user2 = await User.select()
.where('name', 'entrapta2')
.first();
expect(user2).toBeNull();
});
test('Logout', async () => {
await testLogout(agent, cookies, csrf);
});
test('Cannot register taken username', async () => {
// Check that there is no hordak in DB
expect(await User.select()
.where('name', 'hordak')
.count()).toStrictEqual(0);
const res1 = await agent.get('/csrf').expect(200);
const cookies = res1.get('Set-Cookie');
const csrf = res1.text;
// Register user
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'password',
identifier: 'hordak',
password: 'horde_prime_will_rise',
password_confirmation: 'horde_prime_will_rise',
terms: 'on',
})
.expect(302)
.expect('Location', '/');
await testLogout(agent, cookies, csrf);
// Verify saved user
expect(await User.select()
.where('name', 'hordak')
.count()).toStrictEqual(1);
// Attempt register same user
const res = await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'password',
identifier: 'hordak',
password: 'horde_prime_will_rise_unless',
password_confirmation: 'horde_prime_will_rise_unless',
terms: 'on',
})
.expect(400);
// username field should be translated from identifier
expect(res.body.messages?.identifier?.name).toStrictEqual('AlreadyExistsValidationError');
// Verify nothing changed
expect(await User.select()
.where('name', 'hordak')
.count()).toStrictEqual(1);
});
});
describe('Register with email (magic_link)', () => {
test('General case', async () => {
const res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'glimmer@example.org',
name: 'glimmer',
})
.expect(302)
.expect('Location', '/magic/lobby?redirect_uri=%2Fcsrf');
await followMagicLinkFromMail(agent, cookies);
await testLogout(agent, cookies, csrf);
// Verify saved user
const user = await User.select()
.with('mainEmail')
.where('name', 'glimmer')
.first();
expect(user).toBeDefined();
const email = user?.mainEmail.getOrFail();
expect(email).toBeDefined();
expect(email?.email).toStrictEqual('glimmer@example.org');
expect(user?.as(UserNameComponent).name).toStrictEqual('glimmer');
await expect(user?.as(UserPasswordComponent).verifyPassword('')).resolves.toStrictEqual(false);
});
test('Cannot register without specifying username', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
res = await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'no_user_name@example.org',
})
.expect(400);
expect(res.body.messages?.name?.name).toStrictEqual('UndefinedValueValidationError');
expect(await popEmail()).toBeNull();
});
test('Cannot register taken username', async () => {
const res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'angella@example.org',
name: 'angella',
})
.expect(302)
.expect('Location', '/magic/lobby?redirect_uri=%2Fcsrf');
await followMagicLinkFromMail(agent, cookies);
await testLogout(agent, cookies, csrf);
// Verify saved user
const user = await User.select()
.with('mainEmail')
.where('name', 'angella')
.first();
expect(user).toBeDefined();
// Attempt register with another mail but same username
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'angella_something_else@example.org',
name: 'angella',
})
.expect(400);
expect(await popEmail()).toBeNull();
});
test('Cannot register taken email', async () => {
const res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'bow@example.org',
name: 'bow',
})
.expect(302)
.expect('Location', '/magic/lobby?redirect_uri=%2Fcsrf');
await followMagicLinkFromMail(agent, cookies);
await testLogout(agent, cookies, csrf);
// Verify saved user
const user = await User.select()
.with('mainEmail')
.where('name', 'bow')
.first();
expect(user).toBeDefined();
// Attempt register with another mail but same username
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'bow@example.org',
name: 'bow2',
})
.expect(400);
expect(await popEmail()).toBeNull();
});
});
describe('Authenticate with username and password (password)', () => {
test('Force auth_method', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Bad password
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'entrapta',
password: 'darla_is_not_cute',
auth_method: 'password',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate
await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'entrapta',
password: 'darla_is_cute',
auth_method: 'password',
})
.expect(302)
.expect('Location', '/');
await testLogout(agent, cookies, csrf);
});
test('Automatic auth_method', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Bad password
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'entrapta',
password: 'darla_is_not_cute',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate
await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'entrapta',
password: 'darla_is_cute',
})
.expect(302)
.expect('Location', '/');
await testLogout(agent, cookies, csrf);
});
test('Non-existing username', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Authenticate
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'i_do_not_exist',
password: 'there_is_no_point',
auth_method: 'password',
})
.expect(400);
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
// Authenticate (automatic method)
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'i_do_not_exist',
password: 'there_is_no_point',
auth_method: 'password',
})
.expect(400);
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
});
test('No password user', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Authenticate
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'glimmer',
password: '',
auth_method: 'password',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate (automatic method)
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'glimmer',
password: '',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate without password
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'angella',
auth_method: 'password',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate without password (automatic method)
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'angella',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
});
});
describe('Authenticate with email (magic_link)', () => {
test('Force auth_method', async () => {
const res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Authenticate
await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'glimmer@example.org',
auth_method: 'magic_link',
})
.expect(302)
.expect('Location', '/magic/lobby?redirect_uri=%2Fcsrf');
await followMagicLinkFromMail(agent, cookies);
await testLogout(agent, cookies, csrf);
});
test('Automatic auth_method', async () => {
const res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Authenticate
await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'angella@example.org',
})
.expect(302)
.expect('Location', '/magic/lobby?redirect_uri=%2Fcsrf');
await followMagicLinkFromMail(agent, cookies);
await testLogout(agent, cookies, csrf);
});
test('Non-existing email (forced auth_method)', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Authenticate
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'i_do_not_exist@invalid.org',
auth_method: 'magic_link',
})
.expect(400);
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
});
test('Non-existing email (automatic auth_method)', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Authenticate
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'i_do_not_exist@invalid.org',
})
.expect(400);
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
});
});
describe('Authenticate with email and password (password)', () => {
test('Prepare user', async () => {
const res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
await agent.post('/auth/register')
.set('Cookie', cookies)
.send({
csrf: csrf,
auth_method: 'magic_link',
identifier: 'double-trouble@example.org',
name: 'double-trouble',
})
.expect(302)
.expect('Location', '/magic/lobby?redirect_uri=%2Fcsrf');
await followMagicLinkFromMail(agent, cookies);
await testLogout(agent, cookies, csrf);
// Verify saved user
const user = await User.select()
.with('mainEmail')
.where('name', 'double-trouble')
.first();
await user?.as(UserPasswordComponent).setPassword('trick-or-treat');
await user?.save();
expect(user).toBeDefined();
const email = user?.mainEmail.getOrFail();
expect(email).toBeDefined();
expect(email?.email).toStrictEqual('double-trouble@example.org');
expect(user?.as(UserNameComponent).name).toStrictEqual('double-trouble');
await expect(user?.as(UserPasswordComponent).verifyPassword('trick-or-treat')).resolves.toStrictEqual(true);
});
test('Force auth_method', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Bad password
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'double-trouble@example.org',
password: 'i_have_no_imagination',
auth_method: 'password',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate
await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'double-trouble@example.org',
password: 'trick-or-treat',
auth_method: 'password',
})
.expect(302)
.expect('Location', '/');
await testLogout(agent, cookies, csrf);
});
test('Automatic auth_method', async () => {
let res = await agent.get('/csrf').expect(200);
const cookies = res.get('Set-Cookie');
const csrf = res.text;
// Not authenticated
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
// Bad password
res = await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'double-trouble@example.org',
password: 'i_have_no_imagination',
})
.expect(400);
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
// Authenticate
await agent.post('/auth/login')
.set('Cookie', cookies)
.send({
csrf: csrf,
identifier: 'double-trouble@example.org',
password: 'trick-or-treat',
})
.expect(302)
.expect('Location', '/');
await testLogout(agent, cookies, csrf);
});
});