Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const httpValidator = new HttpValidator({
)
}
],
tokenMandatory: true // Optional (default: true)
issuer: 'eyevinn',
audience: ['one', 'two'] // Optional
});
Expand Down
63 changes: 62 additions & 1 deletion src/validators/http.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRequest } from 'node-mocks-http';
import { HttpValidator } from './http';
import { HttpValidator, NoTokenFoundError } from './http';
import { CAT } from '..';

describe('HTTP Request CAT Validator', () => {
Expand Down Expand Up @@ -127,4 +127,65 @@ describe('HTTP Request CAT Validator', () => {
const result = await httpValidator.validateHttpRequest(request);
expect(result.status).toBe(200);
});

test('can handle when CTA access token header is an array', async () => {
const request = createRequest({
method: 'GET',
headers: {
'CTA-Common-Access-Token': [
'2D3RhEOhAQWhBFBha2FtYWlfa2V5X2hzMjU2U6MEGnUCOrsGGmfXRKwFGmfXRKxYIOM6yRx830uqAamWFv1amFYRa5vaV2z5lIQTqFEvFh8z'
]
}
});
const httpValidator = new HttpValidator({
keys: [
{
kid: 'Symmetric256',
key: Buffer.from(
'403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388',
'hex'
)
}
],
issuer: 'eyevinn'
});
const result = await httpValidator.validateHttpRequest(request);
expect(result.status).toBe(200);
});

test('returns ok when CTA common access token is optional', async () => {
const request = createRequest({
method: 'GET'
});
const httpValidator = new HttpValidator({
keys: [
{
kid: 'Symmetric256',
key: Buffer.from(
'403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388',
'hex'
)
}
],
issuer: 'eyevinn'
});
await expect(httpValidator.validateHttpRequest(request)).rejects.toThrow(
NoTokenFoundError
);
const httpValidatorOptional = new HttpValidator({
keys: [
{
kid: 'Symmetric256',
key: Buffer.from(
'403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388',
'hex'
)
}
],
issuer: 'eyevinn',
tokenMandatory: false
});
const result = await httpValidatorOptional.validateHttpRequest(request);
expect(result.status).toBe(200);
});
});
35 changes: 32 additions & 3 deletions src/validators/http.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { IncomingMessage } from 'node:http';
import { CAT } from '..';
import {
InvalidAudienceError,
InvalidIssuerError,
KeyNotFoundError,
TokenExpiredError
} from '../errors';

interface HttpValidatorKey {
kid: string;
key: Buffer;
}

export interface HttpValidatorOptions {
tokenMandatory?: boolean;
keys: HttpValidatorKey[];
issuer: string;
audience?: string[];
Expand All @@ -17,6 +24,12 @@ export interface HttpResponse {
message?: string;
}

export class NoTokenFoundError extends Error {
constructor() {
super('No CTA token could be found');
}
}

/**
* Handle request and validate CTA Common Access Token
*
Expand Down Expand Up @@ -49,6 +62,7 @@ export class HttpValidator {
this.keys[k.kid] = k.key;
});
this.opts = opts;
this.opts.tokenMandatory = opts.tokenMandatory ?? true;
}

public async validateHttpRequest(
Expand All @@ -60,17 +74,32 @@ export class HttpValidator {

// Check for token in headers first
if (request.headers['cta-common-access-token']) {
const token = request.headers['cta-common-access-token'] as string;
const token = Array.isArray(request.headers['cta-common-access-token'])
? request.headers['cta-common-access-token'][0]
: request.headers['cta-common-access-token'];
try {
await validator.validate(token, 'mac', {
issuer: this.opts.issuer,
audience: this.opts.audience
});
return { status: 200 };
} catch (err) {
return { status: 401, message: (err as Error).message };
if (
err instanceof InvalidIssuerError ||
err instanceof InvalidAudienceError ||
err instanceof KeyNotFoundError ||
err instanceof TokenExpiredError
) {
return { status: 401, message: (err as Error).message };
} else {
console.log(`Internal error`, err);
return { status: 500, message: (err as Error).message };
}
}
}
throw new Error('No CTA token could be found');
if (this.opts.tokenMandatory) {
throw new NoTokenFoundError();
}
return { status: 200 };
}
}
Loading