Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: optionally require JWT token to access api, logs requests #39

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ To try it out, go to your favourite HLS/MPEG-DASH video player such as `https://
| `statusCode` | Replace the response for a specific segment request with a specified status code response |
| `timeout` | Force a timeout for the response of a specific segment request |
| `throttle` | Send back the segment at a specified speed of bytes per second |
| `token` | JWT for authentication if `JWT_SECRET` env is set |

### Environmental Variales

| VARIABLE | TYPE | DESCRIPTION |
| -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `JWT_SECRET` | string | Enables jwt authentication for all endpoints and logs requests from users, provide token with the `token` query parameter. Heartbeat path unaffected. |
| `LOAD_PARAMS_FROM_AWS_SSM` | boolean | Load manifest url params from AWS SSM, [see below](#load-manifest-url-params-from-aws-ssm-parameter-store-instead) |
| `AWS_REGION` | string | AWS region for SSM parameters, no effect if `LOAD_PARAMS_FROM_AWS_SSM` is false |
| `AWS_SSM_PARAM_KEY` | string | Key for AWS SSM params, no effect if `LOAD_PARAMS_FROM_AWS_SSM` is false |

### Stateful Mode

Expand Down
134 changes: 134 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"typecheck": "tsc --noEmit -p tsconfig.json",
"pretty": "prettier --check --ignore-unknown .",
"deploy:dev": "git tag $(git rev-parse --short HEAD)-dev && git push --tags",
"postversion": "git push && git push --tags"
"postversion": "git push && git push --tags",
"genJwt": "node dist/genJwt.js"
},
"dependencies": {
"@aws-sdk/client-ssm": "^3.306.0",
Expand All @@ -25,6 +26,7 @@
"fastify": "^3.29.5",
"fastify-cors": "^6.0.2",
"jest": "^27.5.1",
"jsonwebtoken": "^9.0.0",
"lodash": "^4.17.15",
"nock": "^13.2.4",
"node-cache": "^5.1.2",
Expand All @@ -40,6 +42,7 @@
"@types/aws-lambda": "^8.10.92",
"@types/clone": "^2.1.1",
"@types/jest": "^27.4.0",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "^17.0.18",
"@types/node-fetch": "^2.5.7",
"@types/stream-throttle": "^0.1.1",
Expand Down
10 changes: 10 additions & 0 deletions src/genJwt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import jwt from 'jsonwebtoken';

if (process.argv.length < 5)
console.error('Expected 3 arguments; <secret> <email> <company>');
else {
const secret = process.argv[2];
const email = process.argv[3];
const company = process.argv[4];
console.log(jwt.sign({ company, email }, secret));
}
4 changes: 3 additions & 1 deletion src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import segmentRoutes from './segments/routes';
import manifestRoutes from './manifests/routes';
import {
generateHeartbeatResponse,
addCustomVersionHeader
addCustomVersionHeader,
authenticateToken
} from './shared/utils';
import throttlingProxyRoutes from './segments/routes/throttlingProxy';

Expand All @@ -23,4 +24,5 @@ export function registerRoutes(app: FastifyInstance) {
app.register(manifestRoutes, opts);
app.register(throttlingProxyRoutes, opts);
addCustomVersionHeader(app);
authenticateToken(app);
}
38 changes: 38 additions & 0 deletions src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import {
} from './segments/constants';

describe('Chaos Stream Proxy server', () => {
const env = process.env;
let app = null;
beforeEach(() => {
jest.resetModules();
process.env = { ...env };
app = fastify();
registerRoutes(app);
});

afterEach(() => {
process.env = env;
});

it.each([HLS_PROXY_MASTER, HLS_PROXY_MEDIA, SEGMENTS_PROXY_SEGMENT])(
'route %p contains x-version header',
async (route) => {
Expand All @@ -24,4 +31,35 @@ describe('Chaos Stream Proxy server', () => {
);
}
);

it('requires token when running with env JWT_SECRET set, except for heartbeat path', async () => {
// Arrange
process.env.JWT_SECRET = 'somesecret';
const appInternal = fastify();
registerRoutes(appInternal);

// Act
const invalidResponse = await appInternal.inject(
'/api/v2/manifests/dash/proxy-master.mpd?token=invalid'
);

const validResponse = await appInternal.inject(
'/api/v2/manifests/dash/proxy-master.mpd?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW55IjoidGVzdGNvbXBhbnkiLCJlbWFpbCI6InRlc3RAZW1haWwuY29tIiwiaWF0IjoxNjg2MTUzMzU5fQ.wHnzxMdoPZlzdU0GDCzEwd5lnEmq-rX2Ew0yODxqlzg'
);

const allowHeartbeatAlways = await appInternal.inject('/');

// Assert
expect(invalidResponse.statusCode).toEqual(401);
expect(validResponse.statusCode).toEqual(400);
expect(allowHeartbeatAlways.statusCode).toEqual(200);
});

it('ignores token when running without env JWT_SECRET set', async () => {
process.env.JWT_SECRET = undefined;
const appInternal = fastify();
registerRoutes(appInternal);
const response = await appInternal.inject('/?token=invalid');
expect(response.statusCode).toEqual(200);
});
});
Loading
Loading