feat(api): use jwt_access_token (in development) (#53997)

Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
Oliver Eyton-Williams
2024-03-20 12:47:12 +01:00
committed by GitHub
parent bb2efe7618
commit aacfb281fb
25 changed files with 871 additions and 289 deletions

View File

@@ -55,6 +55,7 @@ assert.ok(process.env.JWT_SECRET);
assert.ok(process.env.STRIPE_SECRET_KEY);
assert.ok(process.env.SHOW_UPCOMING_CHANGES);
assert.ok(process.env.MONGOHQ_URL);
assert.ok(process.env.COOKIE_SECRET);
if (process.env.FREECODECAMP_NODE_ENV !== 'development') {
assert.ok(process.env.SES_ID);
@@ -66,6 +67,7 @@ if (process.env.FREECODECAMP_NODE_ENV !== 'development') {
);
assert.ok(process.env.SES_REGION);
assert.ok(process.env.COOKIE_DOMAIN);
assert.notEqual(process.env.COOKIE_SECRET, 'a_cookie_secret');
assert.ok(process.env.PORT);
assert.ok(process.env.SENTRY_DSN);
// The following values can exist in development, but production-like
@@ -125,6 +127,7 @@ export const SENTRY_DSN =
? ''
: process.env.SENTRY_DSN;
export const COOKIE_DOMAIN = process.env.COOKIE_DOMAIN || 'localhost';
export const COOKIE_SECRET = process.env.COOKIE_SECRET;
export const JWT_SECRET = process.env.JWT_SECRET;
export const SES_ID = process.env.SES_ID;
export const SES_SECRET = process.env.SES_SECRET;

7
api/src/utils/ids.ts Normal file
View File

@@ -0,0 +1,7 @@
import { customAlphabet } from 'nanoid';
// uppercase, lowercase letters and numbers
export const customNanoid = customAlphabet(
'1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
64
);

View File

@@ -1,7 +1,6 @@
import { FastifyRequest } from 'fastify';
import jwt from 'jsonwebtoken';
// import { allowedOrigins } from '../../config/allowed-origins';
import { availableLangs } from '../../../shared/config/i18n';
import { allowedOrigins } from './allowed-origins';

View File

@@ -0,0 +1,28 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createAccessToken } from './tokens';
describe('createAccessToken', () => {
it('creates an object with id, ttl, created and userId', () => {
const userId = 'abc';
const actual = createAccessToken(userId);
expect(actual).toStrictEqual({
id: expect.stringMatching(/[a-zA-Z0-9]{64}/),
ttl: 77760000000,
created: expect.stringMatching(
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/
),
userId
});
});
it('sets the ttl, defaulting to 77760000000 ms', () => {
const userId = 'abc';
const ttl = 123;
const actual = createAccessToken(userId, ttl);
expect(actual.ttl).toBe(ttl);
expect(createAccessToken(userId).ttl).toBe(77760000000);
});
});

39
api/src/utils/tokens.ts Normal file
View File

@@ -0,0 +1,39 @@
import jwt from 'jsonwebtoken';
import { customNanoid } from './ids';
import { JWT_SECRET } from './env';
/**
* Encode an id into a JWT (the naming suggests it's a user token, but it's the
* id of the UserToken document).
* @param userToken A token id to encode.
* @returns An encoded object with the userToken property.
*/
export function encodeUserToken(userToken: string): string {
return jwt.sign({ userToken }, JWT_SECRET);
}
export type AccessToken = {
userId: string;
id: string;
ttl: number;
created: string;
};
/**
* Creates an access token.
* @param userId The user ID as a string (yes, it's an ObjectID, but it will be serialized to a string anyway).
* @param ttl The time to live for the token in milliseconds (default: 77760000000).
* @returns The access token.
*/
export const createAccessToken = (
userId: string,
ttl?: number
): AccessToken => {
return {
userId,
id: customNanoid(),
ttl: ttl ?? 77760000000,
created: new Date().toISOString()
};
};

View File

@@ -1,13 +0,0 @@
import jwt from 'jsonwebtoken';
import { JWT_SECRET } from './env';
/**
* Encode an id into a JWT (the naming suggests it's a user token, but it's the
* id of the UserToken document).
* @param userToken A token id to encode.
* @returns An encoded object with the userToken property.
*/
export function encodeUserToken(userToken: string): string {
return jwt.sign({ userToken }, JWT_SECRET);
}