forest-express
Version:
Official package for all Forest Express Lianas
165 lines (140 loc) • 4.83 kB
JavaScript
const AuthenticationService = require('../../src/services/authentication');
describe('authenticationService', () => {
function setup() {
const client = {
authorizationUrl: jest.fn(),
callback: jest.fn(),
};
const authorizationFinder = {
authenticate: jest.fn(),
};
const tokenService = {
createToken: jest.fn(),
};
const errorMessages = {
SERVER_TRANSACTION: {
INVALID_STATE_MISSING: 'Missing state',
INVALID_STATE_FORMAT: 'Invalid state format',
INVALID_STATE_RENDERING_ID: 'Missing renderingId',
},
};
const oidcClientManagerService = {
getClientForCallbackUrl: jest.fn().mockReturnValue(client),
};
const authenticationService = new AuthenticationService({
env: {
FOREST_URL: 'https://api.development.forestadmin.com',
},
authorizationFinder,
tokenService,
errorMessages,
oidcClientManagerService,
});
return {
authenticationService,
client,
authorizationFinder,
tokenService,
errorMessages,
oidcClientManagerService,
};
}
describe('startAuthentication', () => {
it('should correctly generate the authorizationUrl', async () => {
const {
client, authenticationService, oidcClientManagerService,
} = setup();
const generatedUrl = 'https://production.com/forest/authorization/cb?oidc=true';
client.authorizationUrl.mockReturnValue(generatedUrl);
const redirectUrl = 'https://production.com/forest/authentication/cb';
const result = await authenticationService.startAuthentication(
redirectUrl,
{ renderingId: 42 },
);
expect(result).toStrictEqual({ authorizationUrl: generatedUrl });
expect(client.authorizationUrl).toHaveBeenCalledWith({
scope: 'openid email profile',
state: JSON.stringify({ renderingId: 42 }),
});
expect(oidcClientManagerService.getClientForCallbackUrl).toHaveBeenCalledWith(redirectUrl);
});
});
describe('verifyCodeAndGenerateToken', () => {
it('should authenticate the user and create a token', async () => {
const {
authorizationFinder, client, tokenService, authenticationService,
oidcClientManagerService,
} = setup();
client.callback.mockResolvedValue({
access_token: 'THE-ACCESS-TOKEN',
});
const user = {
id: 666,
first_name: 'Alice',
last_name: 'Doe',
email: 'alice@forestadmin.com',
};
authorizationFinder.authenticate.mockResolvedValue(user);
tokenService.createToken.mockReturnValue('THE-TOKEN');
const options = {
envSecret: 'THE-ENV-SECRET',
authSecret: 'THE-AUTH-SECRET',
};
const result = await authenticationService.verifyCodeAndGenerateToken(
'https://agent-url.com/forest/authentication/callback',
{
state: '{"renderingId": 42}',
},
options,
);
expect(result).toBe('THE-TOKEN');
expect(oidcClientManagerService.getClientForCallbackUrl)
.toHaveBeenCalledWith('https://agent-url.com/forest/authentication/callback');
expect(client.callback).toHaveBeenCalledWith(
'https://agent-url.com/forest/authentication/callback',
{
state: '{"renderingId": 42}',
},
{
state: '{"renderingId": 42}',
},
);
expect(authorizationFinder.authenticate).toHaveBeenCalledWith(
42,
'THE-ENV-SECRET',
'THE-ACCESS-TOKEN',
);
expect(tokenService.createToken).toHaveBeenCalledWith(
user,
42,
options,
);
});
describe('with an invalid state', () => {
it('should throw an error if the state is missing', async () => {
const { authenticationService } = setup();
await expect(authenticationService.verifyCodeAndGenerateToken(
'https://foo',
{},
undefined,
)).rejects.toStrictEqual(new Error('Missing state'));
});
it('should throw an error if the state is not parsable', async () => {
const { authenticationService } = setup();
await expect(authenticationService.verifyCodeAndGenerateToken(
'https://foo',
{ state: '{' },
undefined,
)).rejects.toStrictEqual(new Error('Invalid state format'));
});
it('should throw an error if the state does not contain a renderingId', async () => {
const { authenticationService } = setup();
await expect(authenticationService.verifyCodeAndGenerateToken(
'https://foo',
{ state: '{}' },
undefined,
)).rejects.toStrictEqual(new Error('Missing renderingId'));
});
});
});
});