Joachim Van Herwegen c9ed90aeeb fix: Update OIDC provider dependency to v7
The biggest resulting change is that the consent page always appears
after logging in.
Some minor fixes to be closer to the spec are included
together with some minor structural refactors.
2022-02-21 10:04:57 +01:00

82 lines
3.6 KiB
TypeScript

import { LoginHandler } from '../../../../../../src/identity/interaction/email-password/handler/LoginHandler';
import type { AccountStore } from '../../../../../../src/identity/interaction/email-password/storage/AccountStore';
import type {
Interaction,
InteractionHandlerInput,
} from '../../../../../../src/identity/interaction/InteractionHandler';
import { FoundHttpError } from '../../../../../../src/util/errors/FoundHttpError';
import { createPostJsonOperation } from './Util';
describe('A LoginHandler', (): void => {
const webId = 'http://alice.test.com/card#me';
const email = 'alice@test.email';
let oidcInteraction: jest.Mocked<Interaction>;
let input: Required<InteractionHandlerInput>;
let accountStore: jest.Mocked<AccountStore>;
let handler: LoginHandler;
beforeEach(async(): Promise<void> => {
oidcInteraction = {
exp: 123456,
save: jest.fn(),
} as any;
input = { oidcInteraction } as any;
accountStore = {
authenticate: jest.fn().mockResolvedValue(webId),
getSettings: jest.fn().mockResolvedValue({ useIdp: true }),
} as any;
handler = new LoginHandler(accountStore);
});
it('errors if no oidcInteraction is defined on POST requests.', async(): Promise<void> => {
const error = expect.objectContaining({
statusCode: 400,
message: 'This action can only be performed as part of an OIDC authentication flow.',
errorCode: 'E0002',
});
await expect(handler.canHandle({ operation: createPostJsonOperation({}) })).rejects.toThrow(error);
await expect(handler.canHandle({ operation: createPostJsonOperation({}), oidcInteraction }))
.resolves.toBeUndefined();
});
it('errors on invalid emails.', async(): Promise<void> => {
input.operation = createPostJsonOperation({});
await expect(handler.handle(input)).rejects.toThrow('Email required');
input.operation = createPostJsonOperation({ email: [ 'a', 'b' ]});
await expect(handler.handle(input)).rejects.toThrow('Email required');
});
it('errors on invalid passwords.', async(): Promise<void> => {
input.operation = createPostJsonOperation({ email });
await expect(handler.handle(input)).rejects.toThrow('Password required');
input.operation = createPostJsonOperation({ email, password: [ 'a', 'b' ]});
await expect(handler.handle(input)).rejects.toThrow('Password required');
});
it('throws an error if there is a problem.', async(): Promise<void> => {
input.operation = createPostJsonOperation({ email, password: 'password!' });
accountStore.authenticate.mockRejectedValueOnce(new Error('auth failed!'));
await expect(handler.handle(input)).rejects.toThrow('auth failed!');
});
it('throws an error if the account does not have the correct settings.', async(): Promise<void> => {
input.operation = createPostJsonOperation({ email, password: 'password!' });
accountStore.getSettings.mockResolvedValueOnce({ useIdp: false });
await expect(handler.handle(input))
.rejects.toThrow('This server is not an identity provider for this account.');
});
it('returns the generated redirect URL.', async(): Promise<void> => {
input.operation = createPostJsonOperation({ email, password: 'password!' });
await expect(handler.handle(input)).rejects.toThrow(FoundHttpError);
expect(accountStore.authenticate).toHaveBeenCalledTimes(1);
expect(accountStore.authenticate).toHaveBeenLastCalledWith(email, 'password!');
expect(oidcInteraction.save).toHaveBeenCalledTimes(1);
expect(oidcInteraction.result).toEqual({ login: { accountId: webId, remember: false }});
});
});