auth-vir 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type AnyDuration } from 'date-vir';
3
+ import { CreateJwtParams, type ParseJwtParams } from './jwt.js';
4
+ import { UserJwtData } from './user-jwt.js';
5
+ /**
6
+ * Parameters for {@link generateCookie}.
7
+ *
8
+ * @category Internal
9
+ */
10
+ export type CookieParams = {
11
+ /**
12
+ * The origin of the host (backend) service that cookies will be included in all requests to.
13
+ * This should be restricted to just your host (backend) origin for security purposes.
14
+ *
15
+ * @example 'https://www.example.com'
16
+ */
17
+ hostOrigin: string;
18
+ /**
19
+ * The max duration of this cookie. Or, in other words, the max user session duration before
20
+ * they're logged out.
21
+ */
22
+ cookieDuration: AnyDuration;
23
+ /**
24
+ * All JWT parameters required for generating the encrypted JWT that will be embedded in the
25
+ * Cookie. Note that all JWT keys contained herein should never shared with any frontend,
26
+ * client, etc.
27
+ */
28
+ jwtParams: Readonly<CreateJwtParams>;
29
+ } & PartialWithUndefined<{
30
+ /**
31
+ * Is set to `true` (which should only be done in development environments), the cookie will be
32
+ * allowed in insecure requests (non HTTPS requests).
33
+ *
34
+ * @default false
35
+ */
36
+ isDev: boolean;
37
+ }>;
38
+ /**
39
+ * Generate a secure cookie that stores the user JWT data. Used in host (backend) code.
40
+ *
41
+ * @category Internal
42
+ */
43
+ export declare function generateCookie(userJwtData: Readonly<UserJwtData>, cookieConfig: Readonly<CookieParams>): Promise<string>;
44
+ /**
45
+ * Extract an auth cookie from a cookie string. Used in host (backend) code.
46
+ *
47
+ * @category Internal
48
+ * @returns The extracted auth Cookie JWT data or `undefined` if no valid auth JWT data was found.
49
+ */
50
+ export declare function extractCookieJwt(rawCookie: string, jwtParams: Readonly<ParseJwtParams>): Promise<undefined | UserJwtData>;
package/dist/cookie.js ADDED
@@ -0,0 +1,38 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { safeMatch } from '@augment-vir/common';
3
+ import { convertDuration } from 'date-vir';
4
+ import { parseUrl } from 'url-vir';
5
+ import { createUserJwt, parseUserJwt } from './user-jwt.js';
6
+ /**
7
+ * Generate a secure cookie that stores the user JWT data. Used in host (backend) code.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export async function generateCookie(userJwtData, cookieConfig) {
12
+ return [
13
+ `auth=${await createUserJwt(userJwtData, cookieConfig.jwtParams)}`,
14
+ `Domain=${parseUrl(cookieConfig.hostOrigin).hostname}`,
15
+ 'HttpOnly',
16
+ 'Path=/',
17
+ 'SameSite=Strict',
18
+ `MAX-AGE=${convertDuration(cookieConfig.cookieDuration, { seconds: true }).seconds}`,
19
+ cookieConfig.isDev ? '' : 'Secure',
20
+ ]
21
+ .filter(check.isTruthy)
22
+ .join('; ');
23
+ }
24
+ /**
25
+ * Extract an auth cookie from a cookie string. Used in host (backend) code.
26
+ *
27
+ * @category Internal
28
+ * @returns The extracted auth Cookie JWT data or `undefined` if no valid auth JWT data was found.
29
+ */
30
+ export async function extractCookieJwt(rawCookie, jwtParams) {
31
+ const [auth] = safeMatch(rawCookie, /auth=[^;]+(?:;|$)/);
32
+ if (!auth) {
33
+ return undefined;
34
+ }
35
+ const rawJwt = auth.replace('auth=', '').replace(';', '');
36
+ const jwt = await parseUserJwt(rawJwt, jwtParams);
37
+ return jwt;
38
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Name of the header attached to responses that stores the CSRF token value.
3
+ *
4
+ * @category Internal
5
+ */
6
+ export declare const csrfTokenHeaderName = "csrf-token";
7
+ /**
8
+ * Generates a random, cryptographically secure CSRF token.
9
+ *
10
+ * @category Internal
11
+ */
12
+ export declare function generateCsrfToken(): string;
@@ -0,0 +1,15 @@
1
+ import { randomString } from '@augment-vir/common';
2
+ /**
3
+ * Name of the header attached to responses that stores the CSRF token value.
4
+ *
5
+ * @category Internal
6
+ */
7
+ export const csrfTokenHeaderName = 'csrf-token';
8
+ /**
9
+ * Generates a random, cryptographically secure CSRF token.
10
+ *
11
+ * @category Internal
12
+ */
13
+ export function generateCsrfToken() {
14
+ return randomString(256);
15
+ }
package/dist/hash.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Hashes a password using the bcrypt algorithm so passwords don't need to be stored in plain text.
3
+ * The output of this function is safe to store in a database for future credential comparisons.
4
+ *
5
+ * @category Auth : Host
6
+ * @returns `undefined` if the password is too long (and would be truncated by the bcrypt hashing
7
+ * algorithm). Otherwise, the hashed output.
8
+ * @see https://wikipedia.org/wiki/Bcrypt
9
+ */
10
+ export declare function hashPassword(password: string): Promise<undefined | string>;
11
+ /**
12
+ * Checks if the given string will be truncated when passed through {@link hashPassword}. Passwords
13
+ * longer than this should not be accepted.
14
+ *
15
+ * @category Internal
16
+ */
17
+ export declare function willHashTruncate(input: string): boolean;
18
+ /**
19
+ * A utility that provides more accurate string byte size than doing `string.length`.
20
+ *
21
+ * @category Internal
22
+ */
23
+ export declare function getByteLength(input: string): number;
24
+ /**
25
+ * Checks if the given password is a match by comparing it to its previously computed and stored
26
+ * hash.
27
+ *
28
+ * @category Auth : Host
29
+ */
30
+ export declare function doesPasswordMatchHash({ password, hash, }: {
31
+ /** The password entered by the user in their login attempt. */
32
+ password: string;
33
+ /** The stored password hash for that user. */
34
+ hash: string;
35
+ }): Promise<boolean>;
package/dist/hash.js ADDED
@@ -0,0 +1,51 @@
1
+ import { bcrypt, bcryptVerify } from 'hash-wasm';
2
+ /**
3
+ * Hashes a password using the bcrypt algorithm so passwords don't need to be stored in plain text.
4
+ * The output of this function is safe to store in a database for future credential comparisons.
5
+ *
6
+ * @category Auth : Host
7
+ * @returns `undefined` if the password is too long (and would be truncated by the bcrypt hashing
8
+ * algorithm). Otherwise, the hashed output.
9
+ * @see https://wikipedia.org/wiki/Bcrypt
10
+ */
11
+ export async function hashPassword(password) {
12
+ if (willHashTruncate(password)) {
13
+ return undefined;
14
+ }
15
+ const salt = new Uint8Array(16);
16
+ globalThis.crypto.getRandomValues(salt);
17
+ return await bcrypt({
18
+ costFactor: 10,
19
+ password: password.normalize(),
20
+ salt,
21
+ });
22
+ }
23
+ /**
24
+ * Checks if the given string will be truncated when passed through {@link hashPassword}. Passwords
25
+ * longer than this should not be accepted.
26
+ *
27
+ * @category Internal
28
+ */
29
+ export function willHashTruncate(input) {
30
+ return getByteLength(input) > 72;
31
+ }
32
+ /**
33
+ * A utility that provides more accurate string byte size than doing `string.length`.
34
+ *
35
+ * @category Internal
36
+ */
37
+ export function getByteLength(input) {
38
+ return new Blob([input]).size;
39
+ }
40
+ /**
41
+ * Checks if the given password is a match by comparing it to its previously computed and stored
42
+ * hash.
43
+ *
44
+ * @category Auth : Host
45
+ */
46
+ export async function doesPasswordMatchHash({ password, hash, }) {
47
+ return await bcryptVerify({
48
+ hash,
49
+ password,
50
+ });
51
+ }
@@ -0,0 +1,8 @@
1
+ export * from './auth.js';
2
+ export * from './cookie.js';
3
+ export * from './csrf-token.js';
4
+ export * from './hash.js';
5
+ export * from './jwt-keys.js';
6
+ export * from './jwt.js';
7
+ export * from './mock-local-storage.js';
8
+ export * from './user-jwt.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from './auth.js';
2
+ export * from './cookie.js';
3
+ export * from './csrf-token.js';
4
+ export * from './hash.js';
5
+ export * from './jwt-keys.js';
6
+ export * from './jwt.js';
7
+ export * from './mock-local-storage.js';
8
+ export * from './user-jwt.js';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The keys required to sign and encrypt the JWT in their raw form for storage in a secure secrets
3
+ * database (such as AWS Secrets Manager) for later parsing by {@link parseJwtKeys}.
4
+ *
5
+ * These keys should be kept secret and never shared with any frontend, client, etc.
6
+ *
7
+ * @category Internal
8
+ */
9
+ export type RawJwtKeys = Readonly<{
10
+ encryptionKey: string;
11
+ signingKey: string;
12
+ }>;
13
+ /**
14
+ * The keys required to sign and encrypt the JWT.
15
+ *
16
+ * These keys should be kept secret and never shared with any frontend, client, etc.
17
+ *
18
+ * @category Internal
19
+ */
20
+ export type JwtKeys = Readonly<{
21
+ /**
22
+ * Encryption key for JWTs. This is a Uint8Array because `EncryptJWT.encrypt` does not support
23
+ * `CryptoKey` for our chosen encryption algorithm.
24
+ */
25
+ encryptionKey: Readonly<Uint8Array>;
26
+ /** Signing key for JWTs. */
27
+ signingKey: Readonly<CryptoKey>;
28
+ }>;
29
+ /**
30
+ * Generate fresh and serialized JWT signing and encryption keys. These should be stored in a secure
31
+ * secrets database (such as AWS Secrets Manager) for later parsing by {@link parseJwtKeys}.
32
+ *
33
+ * These keys should be kept secret and never shared with any frontend, client, etc.
34
+ *
35
+ * @category Keys
36
+ */
37
+ export declare function generateNewJwtKeys(): Promise<RawJwtKeys>;
38
+ /**
39
+ * Parses an instance of {@link RawJwtKeys} and produces the final {@link JwtKeys} object required by
40
+ * all authentication functionality.
41
+ *
42
+ * @category Keys
43
+ */
44
+ export declare function parseJwtKeys(rawKeys: Readonly<RawJwtKeys>): Promise<Readonly<JwtKeys>>;
@@ -0,0 +1,57 @@
1
+ import { assertWrap } from '@augment-vir/assert';
2
+ import { base64url } from 'jose';
3
+ const signingKeyOptions = [
4
+ {
5
+ name: 'HMAC',
6
+ hash: 'SHA-512',
7
+ },
8
+ true,
9
+ [
10
+ 'sign',
11
+ 'verify',
12
+ ],
13
+ ];
14
+ /**
15
+ * Generate fresh and serialized JWT signing and encryption keys. These should be stored in a secure
16
+ * secrets database (such as AWS Secrets Manager) for later parsing by {@link parseJwtKeys}.
17
+ *
18
+ * These keys should be kept secret and never shared with any frontend, client, etc.
19
+ *
20
+ * @category Keys
21
+ */
22
+ export async function generateNewJwtKeys() {
23
+ return {
24
+ encryptionKey: assertWrap.isDefined((await globalThis.crypto.subtle.exportKey('jwk', await globalThis.crypto.subtle.generateKey({
25
+ name: 'AES-GCM',
26
+ length: 256,
27
+ }, true, [
28
+ 'encrypt',
29
+ 'decrypt',
30
+ ]))).k),
31
+ signingKey: assertWrap.isDefined((await globalThis.crypto.subtle.exportKey('jwk', await globalThis.crypto.subtle.generateKey(...signingKeyOptions))).k),
32
+ };
33
+ }
34
+ /**
35
+ * Parses an instance of {@link RawJwtKeys} and produces the final {@link JwtKeys} object required by
36
+ * all authentication functionality.
37
+ *
38
+ * @category Keys
39
+ */
40
+ export async function parseJwtKeys(rawKeys) {
41
+ if (!rawKeys.encryptionKey) {
42
+ throw new Error('JWT encryption key is empty');
43
+ }
44
+ else if (!rawKeys.signingKey) {
45
+ throw new Error('JWT signing key is empty');
46
+ }
47
+ return {
48
+ encryptionKey: base64url.decode(rawKeys.encryptionKey),
49
+ signingKey: await crypto.subtle.importKey('jwk', {
50
+ k: rawKeys.signingKey,
51
+ alg: 'HS512',
52
+ ext: signingKeyOptions[1],
53
+ key_ops: [...signingKeyOptions[2]],
54
+ kty: 'oct',
55
+ }, ...signingKeyOptions),
56
+ };
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { generateNewJwtKeys } from './jwt-keys.js';
2
+ console.info(await generateNewJwtKeys());
package/dist/jwt.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ import type { AnyObject, PartialWithUndefined } from '@augment-vir/common';
2
+ import { type AnyDuration, type DateLike } from 'date-vir';
3
+ import { JwtKeys } from './jwt-keys.js';
4
+ /**
5
+ * Params for {@link createJwt}.
6
+ *
7
+ * @category Internal
8
+ */
9
+ export type CreateJwtParams = Readonly<{
10
+ /**
11
+ * The keys required to sign and encrypt the JWT.
12
+ *
13
+ * These keys should be kept secret and never shared with any frontend, client, etc.
14
+ */
15
+ jwtKeys: Readonly<JwtKeys>;
16
+ /**
17
+ * The name of the company, the name of the service, or the URL to the service that originally
18
+ * issued the JWT. The same value must be used when creating and parsing a JWT or the parse will
19
+ * fail.
20
+ *
21
+ * This name can be anything you want.
22
+ *
23
+ * @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
24
+ */
25
+ issuer: string;
26
+ /**
27
+ * The arbitrary name or URL of the client intended to consume the JWT. The host and client must
28
+ * both know this name in order for the token to be signed and read correctly.
29
+ *
30
+ * This name can be anything you want.
31
+ *
32
+ * @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
33
+ */
34
+ audience: string;
35
+ /**
36
+ * The duration until the JWT expires.
37
+ *
38
+ * @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4
39
+ */
40
+ jwtDuration: Readonly<AnyDuration>;
41
+ }> & Readonly<PartialWithUndefined<{
42
+ /**
43
+ * Set a custom issued at date.
44
+ *
45
+ * This should usually not be overridden.
46
+ *
47
+ * @default Date.now()
48
+ * @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6
49
+ */
50
+ issuedAt: DateLike;
51
+ /**
52
+ * Set a custom date for when the JWT will become valid. The JWT will be considered
53
+ * invalid and not be processed until this date.
54
+ *
55
+ * This should usually not be overridden.
56
+ *
57
+ * @default
58
+ * none, the JWT will be immediately valid
59
+ * @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5
60
+ */
61
+ notValidUntil: DateLike;
62
+ }>>;
63
+ /**
64
+ * Creates a signed and encrypted JWT that contains the given data.
65
+ *
66
+ * @category Internal
67
+ */
68
+ export declare function createJwt<JwtData extends AnyObject = AnyObject>(
69
+ /** The data to be included in the JWT. */
70
+ data: JwtData, params: Readonly<CreateJwtParams>): Promise<string>;
71
+ /**
72
+ * Params for {@link parseJwt}.
73
+ *
74
+ * @category Internal
75
+ */
76
+ export type ParseJwtParams = Readonly<Pick<CreateJwtParams, 'issuer' | 'audience' | 'jwtKeys'>>;
77
+ /**
78
+ * Parse and extract all data from an encrypted and signed JWT.
79
+ *
80
+ * @category Internal
81
+ * @throws Errors if the decryption, signature verification, or other JWT requirements fail
82
+ */
83
+ export declare function parseJwt<JwtData extends AnyObject = AnyObject>(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<JwtData>;
package/dist/jwt.js ADDED
@@ -0,0 +1,61 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { calculateRelativeDate, createFullDateInUserTimezone, getNowInUtcTimezone, toTimestamp, } from 'date-vir';
3
+ import { EncryptJWT, jwtDecrypt, jwtVerify, SignJWT } from 'jose';
4
+ const encryptionProtectedHeader = { alg: 'dir', enc: 'A256GCM' };
5
+ const signingProtectedHeader = { alg: 'HS512' };
6
+ /**
7
+ * Creates a signed and encrypted JWT that contains the given data.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export async function createJwt(
12
+ /** The data to be included in the JWT. */
13
+ data, params) {
14
+ const rawJwt = new SignJWT({ data: data })
15
+ .setProtectedHeader(signingProtectedHeader)
16
+ .setIssuedAt(params.issuedAt
17
+ ? toTimestamp(createFullDateInUserTimezone(params.issuedAt))
18
+ : undefined)
19
+ .setIssuer(params.issuer)
20
+ .setAudience(params.audience)
21
+ .setExpirationTime(toTimestamp(calculateRelativeDate(getNowInUtcTimezone(), params.jwtDuration)));
22
+ if (params.notValidUntil) {
23
+ rawJwt.setNotBefore(toTimestamp(createFullDateInUserTimezone(params.notValidUntil)));
24
+ }
25
+ const signedJwt = await rawJwt.sign(params.jwtKeys.signingKey);
26
+ return await new EncryptJWT({ jwt: signedJwt })
27
+ .setProtectedHeader(encryptionProtectedHeader)
28
+ .encrypt(params.jwtKeys.encryptionKey);
29
+ }
30
+ /**
31
+ * Parse and extract all data from an encrypted and signed JWT.
32
+ *
33
+ * @category Internal
34
+ * @throws Errors if the decryption, signature verification, or other JWT requirements fail
35
+ */
36
+ export async function parseJwt(encryptedJwt, params) {
37
+ const decryptedJwt = await jwtDecrypt(encryptedJwt, params.jwtKeys.encryptionKey);
38
+ if (!check.deepEquals(decryptedJwt.protectedHeader, encryptionProtectedHeader)) {
39
+ throw new Error('Invalid encryption protected header.');
40
+ }
41
+ else if (!check.isString(decryptedJwt.payload.jwt)) {
42
+ throw new TypeError('Decrypted jwt is not a string.');
43
+ }
44
+ const verifiedJwt = await jwtVerify(decryptedJwt.payload.jwt, params.jwtKeys.signingKey, {
45
+ issuer: params.issuer,
46
+ audience: params.audience,
47
+ requiredClaims: [
48
+ 'iat',
49
+ 'aud',
50
+ 'iss',
51
+ ],
52
+ });
53
+ if (!verifiedJwt.payload.iat || verifiedJwt.payload.iat * 1000 > Date.now()) {
54
+ throw new Error('"iat" claim timestamp check failed');
55
+ }
56
+ const data = verifiedJwt.payload.data;
57
+ if (!check.deepEquals(verifiedJwt.protectedHeader, signingProtectedHeader)) {
58
+ throw new Error('Invalid signing protected header.');
59
+ }
60
+ return data;
61
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `accessRecord` type for {@link createMockLocalStorage}'s output.
3
+ *
4
+ * @category Internal
5
+ */
6
+ export type MockLocalStorageAccessRecord = {
7
+ getItem: string[];
8
+ removeItem: string[];
9
+ setItem: {
10
+ key: string;
11
+ value: string;
12
+ }[];
13
+ key: number[];
14
+ };
15
+ /**
16
+ * Create an empty `accessRecord` object, this is to be used in conjunction with
17
+ * {@link createMockLocalStorage}.
18
+ *
19
+ * @category Mock
20
+ */
21
+ export declare function createEmptyMockLocalStorageAccessRecord(): MockLocalStorageAccessRecord;
22
+ /**
23
+ * Create a LocalStorage mock.
24
+ *
25
+ * @category Mock
26
+ */
27
+ export declare function createMockLocalStorage(
28
+ /** Set values in here to initialize the mocked localStorage data store contents. */
29
+ init?: Record<string, string>): {
30
+ localStorage: Storage;
31
+ store: Record<string, string>;
32
+ accessRecord: MockLocalStorageAccessRecord;
33
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Create an empty `accessRecord` object, this is to be used in conjunction with
3
+ * {@link createMockLocalStorage}.
4
+ *
5
+ * @category Mock
6
+ */
7
+ export function createEmptyMockLocalStorageAccessRecord() {
8
+ return {
9
+ getItem: [],
10
+ removeItem: [],
11
+ setItem: [],
12
+ key: [],
13
+ };
14
+ }
15
+ /**
16
+ * Create a LocalStorage mock.
17
+ *
18
+ * @category Mock
19
+ */
20
+ export function createMockLocalStorage(
21
+ /** Set values in here to initialize the mocked localStorage data store contents. */
22
+ init = {}) {
23
+ const store = init;
24
+ const accessRecord = createEmptyMockLocalStorageAccessRecord();
25
+ const mockLocalStorage = {
26
+ clear() {
27
+ Object.keys(store).forEach((key) => {
28
+ delete store[key];
29
+ });
30
+ },
31
+ getItem(key) {
32
+ accessRecord.getItem.push(key);
33
+ return store[key] ?? null;
34
+ },
35
+ get length() {
36
+ return Object.keys(store).length;
37
+ },
38
+ key(index) {
39
+ accessRecord.key.push(index);
40
+ return Object.keys(store)[index] ?? null;
41
+ },
42
+ removeItem(key) {
43
+ accessRecord.removeItem.push(key);
44
+ delete store[key];
45
+ },
46
+ setItem(key, value) {
47
+ accessRecord.setItem.push({ key, value });
48
+ store[key] = value;
49
+ },
50
+ };
51
+ return {
52
+ localStorage: mockLocalStorage,
53
+ store,
54
+ accessRecord,
55
+ };
56
+ }
@@ -0,0 +1,38 @@
1
+ import { CreateJwtParams, ParseJwtParams } from './jwt.js';
2
+ /**
3
+ * Shape definition and source of truth for {@link UserJwtData}.
4
+ *
5
+ * @category Internal
6
+ */
7
+ export declare const userJwtDataShape: import("object-shape-tester").ShapeDefinition<{
8
+ /** The id from your database of the user you're authenticating. */
9
+ userId: string;
10
+ /**
11
+ * CSRF token. This can be any cryptographically secure randomized string.
12
+ *
13
+ * Consider using {@link generateCsrfToken} to generate this.
14
+ */
15
+ csrfToken: string;
16
+ }, false>;
17
+ /**
18
+ * Data required for user JWTs.
19
+ *
20
+ * @category Internal
21
+ */
22
+ export type UserJwtData = typeof userJwtDataShape.runtimeType;
23
+ /**
24
+ * Creates a new signed and encrypted {@link UserJwtData} when a client (frontend) successfully
25
+ * authenticates with the host (backend). This is used by host (backend) code to establish a new
26
+ * user session. The output of this function should be sent to the client (frontend) for storage.
27
+ *
28
+ * @category Internal
29
+ */
30
+ export declare function createUserJwt(data: Readonly<UserJwtData>, params: Readonly<CreateJwtParams>): Promise<string>;
31
+ /**
32
+ * Parses a {@link UserJwtData} generated from {@link createUserJwt}. This should be used on the host
33
+ * (backend) to a client (frontend) request. Do not use this function in client (frontend) code: it
34
+ * requires JWT signing keys which should not be shared with any client (frontend).
35
+ *
36
+ * @category Internal
37
+ */
38
+ export declare function parseUserJwt(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<UserJwtData | undefined>;
@@ -0,0 +1,41 @@
1
+ import { defineShape, isValidShape } from 'object-shape-tester';
2
+ import { createJwt, parseJwt } from './jwt.js';
3
+ /**
4
+ * Shape definition and source of truth for {@link UserJwtData}.
5
+ *
6
+ * @category Internal
7
+ */
8
+ export const userJwtDataShape = defineShape({
9
+ /** The id from your database of the user you're authenticating. */
10
+ userId: '',
11
+ /**
12
+ * CSRF token. This can be any cryptographically secure randomized string.
13
+ *
14
+ * Consider using {@link generateCsrfToken} to generate this.
15
+ */
16
+ csrfToken: '',
17
+ });
18
+ /**
19
+ * Creates a new signed and encrypted {@link UserJwtData} when a client (frontend) successfully
20
+ * authenticates with the host (backend). This is used by host (backend) code to establish a new
21
+ * user session. The output of this function should be sent to the client (frontend) for storage.
22
+ *
23
+ * @category Internal
24
+ */
25
+ export async function createUserJwt(data, params) {
26
+ return await createJwt(data, params);
27
+ }
28
+ /**
29
+ * Parses a {@link UserJwtData} generated from {@link createUserJwt}. This should be used on the host
30
+ * (backend) to a client (frontend) request. Do not use this function in client (frontend) code: it
31
+ * requires JWT signing keys which should not be shared with any client (frontend).
32
+ *
33
+ * @category Internal
34
+ */
35
+ export async function parseUserJwt(encryptedJwt, params) {
36
+ const parsed = await parseJwt(encryptedJwt, params);
37
+ if (!isValidShape(parsed, userJwtDataShape)) {
38
+ throw new TypeError('Verified jwt has wrong data.');
39
+ }
40
+ return parsed;
41
+ }