108 lines
3.6 KiB
JavaScript
108 lines
3.6 KiB
JavaScript
require('dotenv').config();
|
|
const axios = require('axios');
|
|
const { getBusToken, createBusRequest } = require('../../utils/busAuth');
|
|
|
|
jest.mock('axios');
|
|
|
|
const BUS_URL = process.env.BUS_URL;
|
|
const BUS_JWT_SECRET = process.env.BUS_JWT_SECRET;
|
|
const BUS_ISSUER = process.env.BUS_ISSUER;
|
|
const BUS_SCOPE = process.env.MPI_SCOPE;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
/**
|
|
* Decodes a JWT without verifying the signature.
|
|
*/
|
|
function decodeJwt(jwt) {
|
|
const [header, payload] = jwt.split('.');
|
|
return {
|
|
header: JSON.parse(Buffer.from(header, 'base64').toString()),
|
|
payload: JSON.parse(Buffer.from(payload, 'base64').toString()),
|
|
};
|
|
}
|
|
|
|
describe('getBusToken', () => {
|
|
it('POSTs to /bus-auth/v2/auth on the given busURL', async () => {
|
|
axios.post.mockResolvedValue({ data: 'access-token' });
|
|
|
|
await getBusToken(BUS_URL, BUS_JWT_SECRET, BUS_ISSUER, BUS_SCOPE);
|
|
|
|
expect(axios.post).toHaveBeenCalledWith(
|
|
`${BUS_URL}/bus-auth/v2/auth`,
|
|
expect.any(Object)
|
|
);
|
|
});
|
|
|
|
it('sends the correct grantType, scope and clientAssertionType', async () => {
|
|
axios.post.mockResolvedValue({ data: 'access-token' });
|
|
|
|
await getBusToken(BUS_URL, BUS_JWT_SECRET, BUS_ISSUER, BUS_SCOPE);
|
|
|
|
const body = axios.post.mock.calls[0][1];
|
|
expect(body.grantType).toBe('client_credentials');
|
|
expect(body.scope).toBe(BUS_SCOPE);
|
|
expect(body.clientAssertionType).toBe(
|
|
'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
);
|
|
});
|
|
|
|
it('sends a HS256 JWT signed with the secret as clientAssertion', async () => {
|
|
axios.post.mockResolvedValue({ data: 'access-token' });
|
|
|
|
await getBusToken(BUS_URL, BUS_JWT_SECRET, BUS_ISSUER, BUS_SCOPE);
|
|
|
|
const { clientAssertion } = axios.post.mock.calls[0][1];
|
|
expect(clientAssertion.split('.')).toHaveLength(3);
|
|
|
|
const { header } = decodeJwt(clientAssertion);
|
|
expect(header.alg).toBe('HS256');
|
|
expect(header.typ).toBe('JWT');
|
|
});
|
|
|
|
it('JWT payload contains the correct issuer and expiration fields', async () => {
|
|
axios.post.mockResolvedValue({ data: 'access-token' });
|
|
|
|
const before = Math.floor(Date.now() / 1000);
|
|
await getBusToken(BUS_URL, BUS_JWT_SECRET, BUS_ISSUER, BUS_SCOPE);
|
|
const after = Math.floor(Date.now() / 1000);
|
|
|
|
const { clientAssertion } = axios.post.mock.calls[0][1];
|
|
const { payload } = decodeJwt(clientAssertion);
|
|
|
|
expect(payload.iss).toBe(BUS_ISSUER);
|
|
expect(payload.iat).toBeGreaterThanOrEqual(before);
|
|
expect(payload.iat).toBeLessThanOrEqual(after);
|
|
expect(payload.exp).toBeGreaterThan(payload.iat);
|
|
});
|
|
|
|
it('returns the accessToken field from the bus auth response', async () => {
|
|
const tokenResponse = { accessToken: 'abc123', tokenType: 'bearer', expiresIn: 180000 };
|
|
axios.post.mockResolvedValue({ data: tokenResponse });
|
|
|
|
const result = await getBusToken(BUS_URL, BUS_JWT_SECRET, BUS_ISSUER, BUS_SCOPE);
|
|
|
|
expect(result).toBe('abc123');
|
|
});
|
|
});
|
|
|
|
describe('createBusRequest', () => {
|
|
it('creates an axios instance with the correct baseURL and headers', () => {
|
|
const mockInstance = { get: jest.fn(), post: jest.fn() };
|
|
axios.create.mockReturnValue(mockInstance);
|
|
|
|
const token = 'my-token';
|
|
createBusRequest(BUS_URL, token);
|
|
|
|
expect(axios.create).toHaveBeenCalledWith({
|
|
baseURL: BUS_URL,
|
|
headers: {
|
|
'Content-Type': 'application/fhir+json',
|
|
'Authorization': `Bearer ${token}`,
|
|
},
|
|
});
|
|
});
|
|
});
|