Skip to content

Commit

Permalink
feat(internet): add jwt method (#2936)
Browse files Browse the repository at this point in the history
  • Loading branch information
eLoyyyyy authored Oct 23, 2024
1 parent 48931a5 commit e3858f2
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/definitions/internet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ export type InternetDefinition = LocaleEntry<{
* List of some HTTP status codes.
*/
http_status_code: Record<HTTPStatusCodeType, number[]>;

jwt_algorithm: string[];
}>;
23 changes: 23 additions & 0 deletions src/internal/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,26 @@ export const toBase64: (input: string) => string =
return btoa(binaryString);
}
: (input) => Buffer.from(input).toString('base64');

/**
* This works the same as `Buffer.from(input).toString('base64url')`
* to work on both Node.js and browser environment.
*
* @internal
*
* @param input The string to encode to Base64 URL.
*
* @returns Base64 URL encoded string.
*
* @see https://datatracker.ietf.org/doc/html/rfc4648
*
* @example const encodedHeader = toBase64Url(JSON.stringify(header));
*/
export const toBase64Url: (input: string) => string =
typeof Buffer === 'undefined'
? (input) =>
toBase64(input)
.replaceAll('+', '-')
.replaceAll('/', '_')
.replaceAll(/=+$/g, '')
: (input) => Buffer.from(input).toString('base64url');
2 changes: 2 additions & 0 deletions src/locales/base/internet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import type { InternetDefinition } from '../../..';
import emoji from './emoji';
import http_status_code from './http_status_code';
import jwt_algorithm from './jwt_algorithm';

const internet: InternetDefinition = {
emoji,
http_status_code,
jwt_algorithm,
};

export default internet;
15 changes: 15 additions & 0 deletions src/locales/base/internet/jwt_algorithm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default [
'HS256',
'HS384',
'HS512',
'RS256',
'RS384',
'RS512',
'ES256',
'ES384',
'ES512',
'PS256',
'PS384',
'PS512',
'none',
];
103 changes: 103 additions & 0 deletions src/modules/internet/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FakerError } from '../../errors/faker-error';
import { toBase64Url } from '../../internal/base64';
import { deprecated } from '../../internal/deprecated';
import { ModuleBase } from '../../internal/module-base';
import { charMapping } from './char-mappings';
Expand Down Expand Up @@ -1019,4 +1020,106 @@ export class InternetModule extends ModuleBase {
this.faker.definitions.internet.emoji[emojiType]
);
}

/**
* Generates a random JWT (JSON Web Token) Algorithm.
*
* @see faker.internet.jwt(): For generating random JWT (JSON Web Token).
*
* @example
* faker.internet.jwtAlgorithm() // 'HS256'
* faker.internet.jwtAlgorithm() // 'RS512'
*
* @since 9.1.0
*/
jwtAlgorithm(): string {
return this.faker.helpers.arrayElement(
this.faker.definitions.internet.jwt_algorithm
);
}

/**
* Generates a random JWT (JSON Web Token).
*
* Please note that this method generates a random signature instead of a valid one.
*
* @param options The optional options object.
* @param options.header The Header to use for the token. Defaults to a random object with the following fields: `alg` and `typ`.
* @param options.payload The Payload to use for the token. Defaults to a random object with the following fields: `iat`, `exp`, `nbf`, `iss`, `sub`, `aud`, and `jti`.
* @param options.refDate The date to use as reference point for the newly generated date.
*
* @see https://datatracker.ietf.org/doc/html/rfc7519
* @see faker.internet.jwtAlgorithm(): For generating random JWT (JSON Web Token) Algorithm.
*
* @example
* faker.internet.jwt()
* faker.internet.jwt({ header: { alg: 'HS256' }}) // 'eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MTg2MTM3MTIsImV4cCI6MTcxODYzMzY3OSwibmJmIjoxNjk3MjYzNjMwLCJpc3MiOiJEb3lsZSBhbmQgU29ucyIsInN1YiI6IjYxYWRkYWFmLWY4MjktNDkzZS1iNTI1LTJjMGJkNjkzOTdjNyIsImF1ZCI6IjczNjcyMjVjLWIwMWMtNGE1My1hYzQyLTYwOWJkZmI1MzBiOCIsImp0aSI6IjU2Y2ZkZjAxLWRhMzMtNGUxNi04MzJiLTFlYTk3ZGY1MTQ2YSJ9.5iUgaCaFVPZ8d1QD0xMjoeJbmPVyUfKfoRQ6Njzm5MLp5F4UMh5REbPCrW70fAkr'
* faker.internet.jwt({ payload: { iss: 'Acme' }}) // 'eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBY21lIn0.syUt0GBukNac8Cn1AGKFq2SWAXWy1YIfl0uOYiwg6TZ3omAW0c7FGWY6bC7ZOFSt'
* faker.internet.jwt({ refDate: '2020-01-01T00:00:00.000Z' }) // 'eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Nzc4MDY4NDUsImV4cCI6MTU3Nzg0NjI4MCwibmJmIjoxNTgxNTQyMDYwLCJpc3MiOiJLcmVpZ2VyLCBBbHRlbndlcnRoIGFuZCBQYXVjZWsiLCJzdWIiOiI5NzVjMjMyOS02MDlhLTRjYTYtYjBkZi05ZmY4MGZiNDUwN2QiLCJhdWQiOiI0ODQxZWYwNi01OWYwLTQzMWEtYmFmZi0xMjkxZmRhZDdhNjgiLCJqdGkiOiJmNDBjZTJiYi00ZWYyLTQ1MjMtOGIxMy1kN2Q4NTA5N2M2ZTUifQ.cuClEZQ0CyPIMVS5uxrMwWXz0wcqFFdt0oNne3PMryyly0jghkxVurss2TapMC3C'
*
* @since 9.1.0
*/
jwt(
options: {
/**
* The header to use for the token. If present, it will replace any default values.
*
* @default
* {
* alg: faker.internet.jwtAlgorithm(),
* typ: 'JWT'
* }
*/
header?: Record<string, unknown>;
/**
* The payload to use for the token. If present, it will replace any default values.
*
* @default
* {
* iat: faker.date.recent(),
* exp: faker.date.soon(),
* nbf: faker.date.anytime(),
* iss: faker.company.name(),
* sub: faker.string.uuid(),
* aud: faker.string.uuid(),
* jti: faker.string.uuid()
* }
*/
payload?: Record<string, unknown>;
/**
* The date to use as reference point for the newly generated date.
*
* @default faker.defaultRefDate()
*/
refDate?: string | Date | number;
} = {}
): string {
const { refDate = this.faker.defaultRefDate() } = options;

const iatDefault = this.faker.date.recent({ refDate });

const {
header = {
alg: this.jwtAlgorithm(),
typ: 'JWT',
},
payload = {
iat: Math.round(iatDefault.valueOf() / 1000),
exp: Math.round(
this.faker.date.soon({ refDate: iatDefault }).valueOf() / 1000
),
nbf: Math.round(this.faker.date.anytime({ refDate }).valueOf() / 1000),
iss: this.faker.company.name(),
sub: this.faker.string.uuid(),
aud: this.faker.string.uuid(),
jti: this.faker.string.uuid(),
},
} = options;

const encodedHeader = toBase64Url(JSON.stringify(header));
const encodedPayload = toBase64Url(JSON.stringify(payload));
const signature = this.faker.string.alphanumeric(64);

return `${encodedHeader}.${encodedPayload}.${signature}`;
}
}
24 changes: 24 additions & 0 deletions test/modules/__snapshots__/internet.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ exports[`internet > 42 > ipv4 > with network 1`] = `"229.254.29.199"`;

exports[`internet > 42 > ipv6 1`] = `"8ead:331d:df0f:c444:6b96:d368:ab4b:d1d3"`;

exports[`internet > 42 > jwt > noArgs 1`] = `"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpYXQiOjE1Nzc3ODI3NjAsImV4cCI6MTU3Nzg0NjAwNCwibmJmIjoxNTg0MDU5Mzg4LCJpc3MiOiJDcmlzdCAtIEJlZXIiLCJzdWIiOiJkOWIwZmQzMi0yNDg2LTQ0OTItODQ1Ny1jMzg5MDkyMWZmYzQiLCJhdWQiOiJhNzE3MGU0YS00ODgyLTRmY2YtOGU5ZS0xMzA1NjRkNTQ4MmMiLCJqdGkiOiJmYzMwZGJiYy0xNTFkLTQ5NTEtODQ1Yi1hZTcxYmM4Yzc4NjAifQ.1DjvUfpKe4h9VODSNbTxOTj6eqOR0vpd7kWkwHmYXfuih2Bv3hUe8uZfFLeJmDDx"`;

exports[`internet > 42 > jwt > with custom header 1`] = `"eyJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE1Nzc3ODI3NjAsImV4cCI6MTU3Nzg2NDkwMiwibmJmIjoxNTkyNDY5MTIyLCJpc3MiOiJDcmlzdCBHcm91cCIsInN1YiI6IjBkOWIwZmQzLTIyNDgtNDY0OS05MjQ1LTdjMzg5MDkyMWZmYyIsImF1ZCI6IjFhNzE3MGU0LWE0ODgtNDJmYy1iZmU5LWUxMzA1NjRkNTQ4MiIsImp0aSI6IjFmYzMwZGJiLWMxNTEtNGQ5NS04MTQ1LWJhZTcxYmM4Yzc4NiJ9.61DjvUfpKe4h9VODSNbTxOTj6eqOR0vpd7kWkwHmYXfuih2Bv3hUe8uZfFLeJmDD"`;

exports[`internet > 42 > jwt > with custom payload 1`] = `"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJBY21lIn0.JB993RBH1YPdbbiwqiB8imsMcvA2Ba4WXOi6Gr7u2UgFjwxbYMWTBV5c2kogPmhx"`;

exports[`internet > 42 > jwtAlgorithm 1`] = `"RS384"`;

exports[`internet > 42 > mac > noArgs 1`] = `"5f:b9:22:0d:9b:0f"`;

exports[`internet > 42 > mac > with separator 1`] = `"5f:b9:22:0d:9b:0f"`;
Expand Down Expand Up @@ -210,6 +218,14 @@ exports[`internet > 1211 > ipv4 > with network 1`] = `"238.219.55.242"`;

exports[`internet > 1211 > ipv6 1`] = `"ed4f:efa7:fbae:c9dc:4c48:fa8e:bf46:fb7c"`;

exports[`internet > 1211 > jwt > noArgs 1`] = `"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Nzc4MzA2MjMsImV4cCI6MTU3Nzg1MDExMywibmJmIjoxNjA5MTk5Nzk2LCJpc3MiOiJQYXVjZWssIFJ1bm9sZnNkb3R0aXIgYW5kIEhlcm1hbm4iLCJzdWIiOiJiZGNhZDZkZC0zOTM1LTRmYzYtOGU4Zi0zNGI4NWQ2NDQyOGIiLCJhdWQiOiJiNzM2M2Q5Ny0wYjJiLTQ0YzgtYjczOS1kMWQ3Njc5YzhlZmQiLCJqdGkiOiIzYmQ1ZTA4Yy03MTQyLTQ0M2UtYWY2My05OTk5ZGFkY2RlODUifQ.rlPcW5f2VqUAXZlCKU8wGJfPV9H4qDj1eeVBExFATrtOy1ztr5Cp9avVZNMtsWSD"`;

exports[`internet > 1211 > jwt > with custom header 1`] = `"eyJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE1Nzc4MzA2MjMsImV4cCI6MTU3NzkwNzgxOSwibmJmIjoxNTYwNTI3OTk5LCJpc3MiOiJPc2luc2tpLCBQYXVjZWsgYW5kIFJ1bm9sZnNkb3R0aXIiLCJzdWIiOiI1YmRjYWQ2ZC1kMzkzLTQ1ZmMtYTZlOC1mMzRiODVkNjQ0MjgiLCJhdWQiOiIyYjczNjNkOS03MGIyLTRiNGMtYjg3My05ZDFkNzY3OWM4ZWYiLCJqdGkiOiJkM2JkNWUwOC1jNzE0LTQyNDMtOWVmNi0zOTk5OWRhZGNkZTgifQ.xrlPcW5f2VqUAXZlCKU8wGJfPV9H4qDj1eeVBExFATrtOy1ztr5Cp9avVZNMtsWS"`;

exports[`internet > 1211 > jwt > with custom payload 1`] = `"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBY21lIn0.dZFGLlHOLEPqRRdAcmZLoWxYdiHwlQngjayH8HufpcQzt2HbHhMvsdBS6QsntzOx"`;

exports[`internet > 1211 > jwtAlgorithm 1`] = `"none"`;

exports[`internet > 1211 > mac > noArgs 1`] = `"ee:3f:aa:c5:bd:ca"`;

exports[`internet > 1211 > mac > with separator 1`] = `"ee:3f:aa:c5:bd:ca"`;
Expand Down Expand Up @@ -346,6 +362,14 @@ exports[`internet > 1337 > ipv4 > with network 1`] = `"228.49.64.201"`;

exports[`internet > 1337 > ipv6 1`] = `"536a:7b5f:a28d:2f9b:b79c:a46e:a394:bc4f"`;

exports[`internet > 1337 > jwt > noArgs 1`] = `"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Nzc3NzMwMzksImV4cCI6MTU3Nzc5NzA3MCwibmJmIjoxNTc1MjcwODM1LCJpc3MiOiJMZWFubm9uIC0gR2lic29uIiwic3ViIjoiZmIxNmEyZjctY2M1Ni00OWMzLWI0YTctMjYzOGQyZjY4ODBiIiwiYXVkIjoiMjI1YjA1MGMtNWI3Zi00ZDk5LThmNDAtMWZmNzViMGNhM2FlIiwianRpIjoiZTViNDgyNzctNmM3Yi00YzVlLThiZTYtN2VhODNmOGMzNjY4In0.G1eJVCpQZioHm1lu2UIL52g7eGtWAbbkq4D3IE0LkMkzaQgKyTx14Xs9FCyUTgIu"`;

exports[`internet > 1337 > jwt > with custom header 1`] = `"eyJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE1Nzc3NzMwMzksImV4cCI6MTU3Nzc4Njc1MCwibmJmIjoxNTYzODQyNzk2LCJpc3MiOiJIYW1tZXMgTExDIiwic3ViIjoiNGZiMTZhMmYtN2NjNS00NjljLWEzNGEtNzI2MzhkMmY2ODgwIiwiYXVkIjoiZjIyNWIwNTAtYzViNy00ZmQ5LWI5ZjQtMDFmZjc1YjBjYTNhIiwianRpIjoiMmU1YjQ4MjctNzZjNy00YmM1LWFlYmUtNjdlYTgzZjhjMzY2In0.7G1eJVCpQZioHm1lu2UIL52g7eGtWAbbkq4D3IE0LkMkzaQgKyTx14Xs9FCyUTgI"`;

exports[`internet > 1337 > jwt > with custom payload 1`] = `"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBY21lIn0.hsjwgYJ7nC7YrMNmpALbhFubpcwPbXqvv0JZa7nG0m3MlHuYPBzZf05WYulI0LFb"`;

exports[`internet > 1337 > jwtAlgorithm 1`] = `"RS256"`;

exports[`internet > 1337 > mac > noArgs 1`] = `"42:47:58:4f:b1:6a"`;

exports[`internet > 1337 > mac > with separator 1`] = `"42:47:58:4f:b1:6a"`;
Expand Down
43 changes: 43 additions & 0 deletions test/modules/internet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { times } from './../support/times';

const NON_SEEDED_BASED_RUN = 5;

const refDate = '2020-01-01T00:00:00.000Z';

describe('internet', () => {
seededTests(faker, 'internet', (t) => {
t.itEach(
Expand All @@ -18,6 +20,7 @@ describe('internet', () => {
'domainWord',
'ip',
'ipv6',
'jwtAlgorithm',
'port',
'userAgent'
);
Expand Down Expand Up @@ -154,6 +157,12 @@ describe('internet', () => {
.it('with cidrBlock', { cidrBlock: '192.168.13.37/24' })
.it('with network', { network: IPv4Network.Multicast });
});

t.describe('jwt', (t) => {
t.it('noArgs', { refDate })
.it('with custom header', { header: { alg: 'ES256' }, refDate })
.it('with custom payload', { payload: { iss: 'Acme' }, refDate });
});
});

describe.each(times(NON_SEEDED_BASED_RUN).map(() => faker.seed()))(
Expand Down Expand Up @@ -971,6 +980,40 @@ describe('internet', () => {
expect(emoji.length).toBeGreaterThanOrEqual(1);
});
});

describe('jwt', () => {
it('should return a random jwt', () => {
const jwt = faker.internet.jwt();

expect(jwt).toBeTruthy();
expect(jwt).toBeTypeOf('string');
expect(jwt).toSatisfy(validator.isJWT);
});

it('should return the header and payload values from the token', () => {
const header = {
kid: faker.string.alphanumeric(),
};

const payload = {
nonce: faker.string.alphanumeric(),
};

const actual = faker.internet.jwt({ header, payload });

expect(actual).toBeTypeOf('string');
expect(actual).toSatisfy(validator.isJWT);

const parts = actual.split('.');

expect(
JSON.parse(Buffer.from(parts[0], 'base64url').toString('ascii'))
).toMatchObject(header);
expect(
JSON.parse(Buffer.from(parts[1], 'base64url').toString('ascii'))
).toMatchObject(payload);
});
});
}
);
});

0 comments on commit e3858f2

Please sign in to comment.