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(internet): add jwt method #2936

Merged
merged 40 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b755808
feat: initial implementation
eLoyyyyy May 30, 2024
6d42492
feat: snapshot
eLoyyyyy May 31, 2024
fa8627a
feat: definitions and docs
eLoyyyyy May 31, 2024
2df8a26
nbf and docs
eLoyyyyy Jun 4, 2024
8683762
lint fix
eLoyyyyy Jun 4, 2024
b21d86f
make nbf either ISO date string or date primitive
eLoyyyyy Jun 4, 2024
d20157a
remove nbf for now
eLoyyyyy Jun 4, 2024
faa0fed
changes:
eLoyyyyy Jun 5, 2024
e403441
lint fixes
eLoyyyyy Jun 5, 2024
60285d5
update snapshot
eLoyyyyy Jun 5, 2024
885c000
runthrough of the change requests so far
eLoyyyyy Jun 10, 2024
d99583d
test with custom header and payload
eLoyyyyy Jun 10, 2024
051635f
non standard header and payload
eLoyyyyy Jun 10, 2024
3d26b24
test lint fixes
eLoyyyyy Jun 10, 2024
4a5717b
follow suggestion on default
eLoyyyyy Jun 10, 2024
84eaeba
refactor: remove jwtAlgorithmType
eLoyyyyy Jun 10, 2024
8ce8a84
changes:
eLoyyyyy Jun 13, 2024
40f602f
toBase64Url instead of Buffer for browser environments
eLoyyyyy Jun 13, 2024
43fbe36
docs and lint errors for toBase64Url
eLoyyyyy Jun 17, 2024
9766ee1
default values should be copy pastable from jsdoc preview
eLoyyyyy Jun 17, 2024
ee9eef1
example output values
eLoyyyyy Jun 17, 2024
3b99aa4
case changes
eLoyyyyy Jun 17, 2024
5250576
add the none jwt algorithm
eLoyyyyy Jun 18, 2024
d258b33
changes:
eLoyyyyy Jun 18, 2024
d243a32
jsdoc suggestions
eLoyyyyy Jun 19, 2024
bef573a
move to internal folder
eLoyyyyy Jun 21, 2024
cf80788
fix missing import
eLoyyyyy Jun 22, 2024
d7403e2
fix: apply renamed toBase64 method
eLoyyyyy Oct 14, 2024
52e02f7
fix: merge error
eLoyyyyy Oct 16, 2024
fae3c7f
docs: change version
eLoyyyyy Oct 16, 2024
adbbe6d
feat: add new variant for toBase64 for url
eLoyyyyy Oct 16, 2024
48cfa47
fix: add back replacing specific characters with url-friendly ones
eLoyyyyy Oct 17, 2024
a66a6b3
Merge branch 'next' into internet-jwt
ST-DDT Oct 17, 2024
581e5dc
refactor: reuse toBase64
eLoyyyyy Oct 17, 2024
ad0aa87
docs: add note that jwt method generates random signature
eLoyyyyy Oct 17, 2024
1aead96
docs: formatting
eLoyyyyy Oct 17, 2024
27aaf5c
Merge branch 'next' into internet-jwt
ST-DDT Oct 17, 2024
7ee8d7b
Merge branch 'next' into internet-jwt
ST-DDT Oct 18, 2024
80619cf
Merge branch 'next' into internet-jwt
ST-DDT Oct 18, 2024
5c3f33d
Merge branch 'next' into internet-jwt
ST-DDT Oct 23, 2024
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
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[];
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
}>;
28 changes: 28 additions & 0 deletions src/internal/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,31 @@
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) => {
const utf8Bytes = new TextEncoder().encode(input);
const binaryString = Array.from(utf8Bytes, (byte) =>
String.fromCodePoint(byte)
).join('');
return btoa(binaryString)
.replaceAll('+', '-')
.replaceAll('/', '_')
.replaceAll(/=+$/g, '');
}

Check warning on line 51 in src/internal/base64.ts

View check run for this annotation

Codecov / codecov/patch

src/internal/base64.ts#L42-L51

Added lines #L42 - L51 were not covered by tests
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
: (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
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
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',
];
101 changes: 101 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,104 @@ 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 {
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
return this.faker.helpers.arrayElement(
this.faker.definitions.internet.jwt_algorithm
);
}

/**
* Generates a random JWT (JSON Web Token).
*
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
* @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',
* };
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
*/
header?: Record<string, unknown>;
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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(),
* };
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
*/
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',
},
xDivisionByZerox marked this conversation as resolved.
Show resolved Hide resolved
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);
});
});
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
}
);
});