@atproto/jwk 0.1.0
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.
- package/CHANGELOG.md +7 -0
- package/LICENSE.txt +7 -0
- package/dist/alg.d.ts +3 -0
- package/dist/alg.d.ts.map +1 -0
- package/dist/alg.js +90 -0
- package/dist/alg.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +62 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/jwk.d.ts +2424 -0
- package/dist/jwk.d.ts.map +1 -0
- package/dist/jwk.js +112 -0
- package/dist/jwk.js.map +1 -0
- package/dist/jwks.d.ts +1770 -0
- package/dist/jwks.d.ts.map +1 -0
- package/dist/jwks.js +12 -0
- package/dist/jwks.js.map +1 -0
- package/dist/jwt-decode.d.ts +6 -0
- package/dist/jwt-decode.d.ts.map +1 -0
- package/dist/jwt-decode.js +20 -0
- package/dist/jwt-decode.js.map +1 -0
- package/dist/jwt-verify.d.ts +20 -0
- package/dist/jwt-verify.d.ts.map +1 -0
- package/dist/jwt-verify.js +3 -0
- package/dist/jwt-verify.js.map +1 -0
- package/dist/jwt.d.ts +1785 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +150 -0
- package/dist/jwt.js.map +1 -0
- package/dist/key.d.ts +38 -0
- package/dist/key.d.ts.map +1 -0
- package/dist/key.js +131 -0
- package/dist/key.js.map +1 -0
- package/dist/keyset.d.ts +41 -0
- package/dist/keyset.d.ts.map +1 -0
- package/dist/keyset.js +234 -0
- package/dist/keyset.js.map +1 -0
- package/dist/util.d.ts +48 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +143 -0
- package/dist/util.js.map +1 -0
- package/package.json +38 -0
- package/src/alg.ts +98 -0
- package/src/errors.ts +56 -0
- package/src/index.ts +10 -0
- package/src/jwk.ts +141 -0
- package/src/jwks.ts +15 -0
- package/src/jwt-decode.ts +27 -0
- package/src/jwt-verify.ts +22 -0
- package/src/jwt.ts +173 -0
- package/src/key.ts +93 -0
- package/src/keyset.ts +240 -0
- package/src/util.ts +181 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +4 -0
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RefinementCtx } from 'zod';
|
|
2
|
+
export type Simplify<T> = {
|
|
3
|
+
[K in keyof T]: T[K];
|
|
4
|
+
} & {};
|
|
5
|
+
export type Override<T, V> = Simplify<V & Omit<T, keyof V>>;
|
|
6
|
+
export type RequiredKey<T, K extends string> = Simplify<string extends K ? T : {
|
|
7
|
+
[L in K]: Exclude<L extends keyof T ? T[L] : unknown, undefined>;
|
|
8
|
+
} & Omit<T, K>>;
|
|
9
|
+
export declare const isDefined: <T>(i: T | undefined) => i is T;
|
|
10
|
+
export declare const preferredOrderCmp: <T>(order: readonly T[]) => (a: T, b: T) => number;
|
|
11
|
+
export declare function matchesAny<T extends string | number | symbol | boolean>(value: null | undefined | T | readonly T[]): (v: unknown) => v is T;
|
|
12
|
+
/**
|
|
13
|
+
* Decorator to cache the result of a getter on a class instance.
|
|
14
|
+
*/
|
|
15
|
+
export declare const cachedGetter: <T extends object, V>(target: (this: T) => V, _context: ClassGetterDecoratorContext<T, V>) => (this: T) => V;
|
|
16
|
+
export declare function parseB64uJson(input: string): unknown;
|
|
17
|
+
/**
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // jwtSchema will only allow base64url chars & "." (dot)
|
|
21
|
+
* const jwtSchema = z.string().superRefine(jwtCharsRefinement)
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const jwtCharsRefinement: (data: string, ctx: RefinementCtx) => void;
|
|
25
|
+
/**
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* type SegmentedString3 = SegmentedString<3> // `${string}.${string}.${string}`
|
|
29
|
+
* type SegmentedString4 = SegmentedString<4> // `${string}.${string}.${string}.${string}`
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @note
|
|
33
|
+
* This utility only provides one way type safety (A SegmentedString<4> can be
|
|
34
|
+
* assigned to SegmentedString<3> but not vice versa). The purpose of this
|
|
35
|
+
* utility is to improve DX by avoiding as many potential errors as build time.
|
|
36
|
+
* DO NOT rely on this to enforce security or data integrity.
|
|
37
|
+
*/
|
|
38
|
+
type SegmentedString<C extends number, Acc extends string[] = [string]> = Acc['length'] extends C ? `${Acc[0]}` : `${Acc[0]}.${SegmentedString<C, [string, ...Acc]>}`;
|
|
39
|
+
/**
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const jwtSchema = z.string().superRefine(segmentedStringRefinementFactory(3))
|
|
43
|
+
* type Jwt = z.infer<typeof jwtSchema> // `${string}.${string}.${string}`
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare const segmentedStringRefinementFactory: <C extends number>(count: C, minPartLength?: number) => (data: string, ctx: RefinementCtx) => data is SegmentedString<C, [string]>;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAgB,MAAM,KAAK,CAAA;AAGjD,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAA;AACvD,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AAE3D,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,QAAQ,CACrD,MAAM,SAAS,CAAC,GACZ,CAAC,GACD;KACG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,SAAS,CAAC;CACjE,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CACnB,CAAA;AAED,eAAO,MAAM,SAAS,SAAU,CAAC,GAAG,SAAS,WAA4B,CAAA;AAEzE,eAAO,MAAM,iBAAiB,aACjB,SAAS,CAAC,EAAE,SACnB,CAAC,KAAK,CAAC,WAOV,CAAA;AAEH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EACrE,KAAK,EAAE,IAAI,GAAG,SAAS,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE,GACzC,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,IAAI,CAAC,CAMxB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,gCACf,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,YACZ,4BAA4B,CAAC,EAAE,CAAC,CAAC,YAEpB,CAAC,MASzB,CAAA;AAGD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIpD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,SAAU,MAAM,OAAO,aAAa,KAAG,IA2BrE,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,eAAe,CAClB,CAAC,SAAS,MAAM,EAChB,GAAG,SAAS,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,IAC7B,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,GACvB,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GACX,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,CAAA;AAEvD;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,4BACpC,CAAC,oCAUM,MAAM,OAAO,aAAa,yCA2CzC,CAAA"}
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.segmentedStringRefinementFactory = exports.jwtCharsRefinement = exports.parseB64uJson = exports.cachedGetter = exports.matchesAny = exports.preferredOrderCmp = exports.isDefined = void 0;
|
|
4
|
+
const base64_1 = require("multiformats/bases/base64");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const isDefined = (i) => i !== undefined;
|
|
7
|
+
exports.isDefined = isDefined;
|
|
8
|
+
const preferredOrderCmp = (order) => (a, b) => {
|
|
9
|
+
const aIdx = order.indexOf(a);
|
|
10
|
+
const bIdx = order.indexOf(b);
|
|
11
|
+
if (aIdx === bIdx)
|
|
12
|
+
return 0;
|
|
13
|
+
if (aIdx === -1)
|
|
14
|
+
return 1;
|
|
15
|
+
if (bIdx === -1)
|
|
16
|
+
return -1;
|
|
17
|
+
return aIdx - bIdx;
|
|
18
|
+
};
|
|
19
|
+
exports.preferredOrderCmp = preferredOrderCmp;
|
|
20
|
+
function matchesAny(value) {
|
|
21
|
+
return value == null
|
|
22
|
+
? (v) => true
|
|
23
|
+
: Array.isArray(value)
|
|
24
|
+
? (v) => value.includes(v)
|
|
25
|
+
: (v) => v === value;
|
|
26
|
+
}
|
|
27
|
+
exports.matchesAny = matchesAny;
|
|
28
|
+
/**
|
|
29
|
+
* Decorator to cache the result of a getter on a class instance.
|
|
30
|
+
*/
|
|
31
|
+
const cachedGetter = (target, _context) => {
|
|
32
|
+
return function () {
|
|
33
|
+
const value = target.call(this);
|
|
34
|
+
Object.defineProperty(this, target.name, {
|
|
35
|
+
get: () => value,
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
});
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
exports.cachedGetter = cachedGetter;
|
|
43
|
+
const decoder = new TextDecoder();
|
|
44
|
+
function parseB64uJson(input) {
|
|
45
|
+
const inputBytes = base64_1.base64url.baseDecode(input);
|
|
46
|
+
const json = decoder.decode(inputBytes);
|
|
47
|
+
return JSON.parse(json);
|
|
48
|
+
}
|
|
49
|
+
exports.parseB64uJson = parseB64uJson;
|
|
50
|
+
/**
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* // jwtSchema will only allow base64url chars & "." (dot)
|
|
54
|
+
* const jwtSchema = z.string().superRefine(jwtCharsRefinement)
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
const jwtCharsRefinement = (data, ctx) => {
|
|
58
|
+
// Note: this is a hot path, let's avoid using a RegExp
|
|
59
|
+
let char;
|
|
60
|
+
for (let i = 0; i < data.length; i++) {
|
|
61
|
+
char = data.charCodeAt(i);
|
|
62
|
+
if (
|
|
63
|
+
// Base64 URL encoding (most frequent)
|
|
64
|
+
(65 <= char && char <= 90) || // A-Z
|
|
65
|
+
(97 <= char && char <= 122) || // a-z
|
|
66
|
+
(48 <= char && char <= 57) || // 0-9
|
|
67
|
+
char === 45 || // -
|
|
68
|
+
char === 95 || // _
|
|
69
|
+
// Boundary (least frequent, check last)
|
|
70
|
+
char === 46 // .
|
|
71
|
+
) {
|
|
72
|
+
// continue
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Invalid char might be a surrogate pair
|
|
76
|
+
const invalidChar = String.fromCodePoint(data.codePointAt(i));
|
|
77
|
+
return ctx.addIssue({
|
|
78
|
+
code: zod_1.ZodIssueCode.custom,
|
|
79
|
+
message: `Invalid character "${invalidChar}" in JWT at position ${i}`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
exports.jwtCharsRefinement = jwtCharsRefinement;
|
|
85
|
+
/**
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const jwtSchema = z.string().superRefine(segmentedStringRefinementFactory(3))
|
|
89
|
+
* type Jwt = z.infer<typeof jwtSchema> // `${string}.${string}.${string}`
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
const segmentedStringRefinementFactory = (count, minPartLength = 2) => {
|
|
93
|
+
if (!Number.isFinite(count) || count < 1 || (count | 0) !== count) {
|
|
94
|
+
throw new TypeError(`Count must be a natural number (got ${count})`);
|
|
95
|
+
}
|
|
96
|
+
const minTotalLength = count * minPartLength + (count - 1);
|
|
97
|
+
const errorPrefix = `Invalid JWT format`;
|
|
98
|
+
return (data, ctx) => {
|
|
99
|
+
if (data.length < minTotalLength) {
|
|
100
|
+
ctx.addIssue({
|
|
101
|
+
code: zod_1.ZodIssueCode.custom,
|
|
102
|
+
message: `${errorPrefix}: too short`,
|
|
103
|
+
});
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
let currentStart = 0;
|
|
107
|
+
for (let i = 0; i < count - 1; i++) {
|
|
108
|
+
const nextDot = data.indexOf('.', currentStart);
|
|
109
|
+
if (nextDot === -1) {
|
|
110
|
+
ctx.addIssue({
|
|
111
|
+
code: zod_1.ZodIssueCode.custom,
|
|
112
|
+
message: `${errorPrefix}: expected ${count} segments, got ${i + 1}`,
|
|
113
|
+
});
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (nextDot - currentStart < minPartLength) {
|
|
117
|
+
ctx.addIssue({
|
|
118
|
+
code: zod_1.ZodIssueCode.custom,
|
|
119
|
+
message: `${errorPrefix}: segment ${i + 1} is too short`,
|
|
120
|
+
});
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
currentStart = nextDot + 1;
|
|
124
|
+
}
|
|
125
|
+
if (data.indexOf('.', currentStart) !== -1) {
|
|
126
|
+
ctx.addIssue({
|
|
127
|
+
code: zod_1.ZodIssueCode.custom,
|
|
128
|
+
message: `${errorPrefix}: too many segments`,
|
|
129
|
+
});
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (data.length - currentStart < minPartLength) {
|
|
133
|
+
ctx.addIssue({
|
|
134
|
+
code: zod_1.ZodIssueCode.custom,
|
|
135
|
+
message: `${errorPrefix}: last segment is too short`,
|
|
136
|
+
});
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
exports.segmentedStringRefinementFactory = segmentedStringRefinementFactory;
|
|
143
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAAA,sDAAqD;AACrD,6BAAiD;AAc1C,MAAM,SAAS,GAAG,CAAI,CAAgB,EAAU,EAAE,CAAC,CAAC,KAAK,SAAS,CAAA;AAA5D,QAAA,SAAS,aAAmD;AAElE,MAAM,iBAAiB,GAC5B,CAAI,KAAmB,EAAE,EAAE,CAC3B,CAAC,CAAI,EAAE,CAAI,EAAE,EAAE;IACb,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,CAAC,CAAA;IAC3B,IAAI,IAAI,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,CAAA;IACzB,IAAI,IAAI,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,CAAA;IAC1B,OAAO,IAAI,GAAG,IAAI,CAAA;AACpB,CAAC,CAAA;AATU,QAAA,iBAAiB,qBAS3B;AAEH,SAAgB,UAAU,CACxB,KAA0C;IAE1C,OAAO,KAAK,IAAI,IAAI;QAClB,CAAC,CAAC,CAAC,CAAC,EAAU,EAAE,CAAC,IAAI;QACrB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACpB,CAAC,CAAC,CAAC,CAAC,EAAU,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC,CAAC,EAAU,EAAE,CAAC,CAAC,KAAK,KAAK,CAAA;AAClC,CAAC;AARD,gCAQC;AAED;;GAEG;AACI,MAAM,YAAY,GAAG,CAC1B,MAAsB,EACtB,QAA2C,EAC3C,EAAE;IACF,OAAO;QACL,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;YACvC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;YAChB,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAA;QACF,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;AACH,CAAC,CAAA;AAbY,QAAA,YAAY,gBAaxB;AAED,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;AACjC,SAAgB,aAAa,CAAC,KAAa;IACzC,MAAM,UAAU,GAAG,kBAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAJD,sCAIC;AAED;;;;;;GAMG;AACI,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAE,GAAkB,EAAQ,EAAE;IAC3E,uDAAuD;IACvD,IAAI,IAAI,CAAA;IAER,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QAEzB;QACE,sCAAsC;QACtC,CAAC,EAAE,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,IAAI,MAAM;YACpC,CAAC,EAAE,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,MAAM;YACrC,CAAC,EAAE,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,IAAI,MAAM;YACpC,IAAI,KAAK,EAAE,IAAI,IAAI;YACnB,IAAI,KAAK,EAAE,IAAI,IAAI;YACnB,wCAAwC;YACxC,IAAI,KAAK,EAAE,CAAC,IAAI;UAChB,CAAC;YACD,WAAW;QACb,CAAC;aAAM,CAAC;YACN,yCAAyC;YACzC,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAA;YAC9D,OAAO,GAAG,CAAC,QAAQ,CAAC;gBAClB,IAAI,EAAE,kBAAY,CAAC,MAAM;gBACzB,OAAO,EAAE,sBAAsB,WAAW,wBAAwB,CAAC,EAAE;aACtE,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;AACH,CAAC,CAAA;AA3BY,QAAA,kBAAkB,sBA2B9B;AAsBD;;;;;;GAMG;AACI,MAAM,gCAAgC,GAAG,CAC9C,KAAQ,EACR,aAAa,GAAG,CAAC,EACjB,EAAE;IACF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;QAClE,MAAM,IAAI,SAAS,CAAC,uCAAuC,KAAK,GAAG,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,GAAG,aAAa,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;IAC1D,MAAM,WAAW,GAAG,oBAAoB,CAAA;IAExC,OAAO,CAAC,IAAY,EAAE,GAAkB,EAA8B,EAAE;QACtE,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YACjC,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,kBAAY,CAAC,MAAM;gBACzB,OAAO,EAAE,GAAG,WAAW,aAAa;aACrC,CAAC,CAAA;YACF,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,YAAY,GAAG,CAAC,CAAA;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;YAC/C,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;gBACnB,GAAG,CAAC,QAAQ,CAAC;oBACX,IAAI,EAAE,kBAAY,CAAC,MAAM;oBACzB,OAAO,EAAE,GAAG,WAAW,cAAc,KAAK,kBAAkB,CAAC,GAAG,CAAC,EAAE;iBACpE,CAAC,CAAA;gBACF,OAAO,KAAK,CAAA;YACd,CAAC;YACD,IAAI,OAAO,GAAG,YAAY,GAAG,aAAa,EAAE,CAAC;gBAC3C,GAAG,CAAC,QAAQ,CAAC;oBACX,IAAI,EAAE,kBAAY,CAAC,MAAM;oBACzB,OAAO,EAAE,GAAG,WAAW,aAAa,CAAC,GAAG,CAAC,eAAe;iBACzD,CAAC,CAAA;gBACF,OAAO,KAAK,CAAA;YACd,CAAC;YACD,YAAY,GAAG,OAAO,GAAG,CAAC,CAAA;QAC5B,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,kBAAY,CAAC,MAAM;gBACzB,OAAO,EAAE,GAAG,WAAW,qBAAqB;aAC7C,CAAC,CAAA;YACF,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,YAAY,GAAG,aAAa,EAAE,CAAC;YAC/C,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,kBAAY,CAAC,MAAM;gBACzB,OAAO,EAAE,GAAG,WAAW,6BAA6B;aACrD,CAAC,CAAA;YACF,OAAO,KAAK,CAAA;QACd,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;AACH,CAAC,CAAA;AAtDY,QAAA,gCAAgC,oCAsD5C"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atproto/jwk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "A library for working with JSON Web Keys (JWKs) in TypeScript. This is meant to be extended by environment-specific libraries like @atproto/jwk-jose.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"atproto",
|
|
8
|
+
"jwk",
|
|
9
|
+
"jwks",
|
|
10
|
+
"jwt",
|
|
11
|
+
"json web key"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://atproto.com",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/bluesky-social/atproto",
|
|
17
|
+
"directory": "packages/oauth/jwk"
|
|
18
|
+
},
|
|
19
|
+
"type": "commonjs",
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"multiformats": "^9.9.0",
|
|
30
|
+
"zod": "^3.23.8"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.3.3"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc --build tsconfig.json"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/alg.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { JwkError } from './errors.js'
|
|
2
|
+
import { Jwk } from './jwk.js'
|
|
3
|
+
|
|
4
|
+
declare const process: undefined | { versions?: { node?: string } }
|
|
5
|
+
const IS_NODE_RUNTIME =
|
|
6
|
+
typeof process !== 'undefined' && typeof process?.versions?.node === 'string'
|
|
7
|
+
|
|
8
|
+
export function* jwkAlgorithms(jwk: Jwk): Generator<string> {
|
|
9
|
+
// Ed25519, Ed448, and secp256k1 always have "alg"
|
|
10
|
+
// OKP always has "use"
|
|
11
|
+
if (jwk.alg) {
|
|
12
|
+
yield jwk.alg
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
switch (jwk.kty) {
|
|
17
|
+
case 'EC': {
|
|
18
|
+
if (jwk.use === 'enc' || jwk.use === undefined) {
|
|
19
|
+
yield 'ECDH-ES'
|
|
20
|
+
yield 'ECDH-ES+A128KW'
|
|
21
|
+
yield 'ECDH-ES+A192KW'
|
|
22
|
+
yield 'ECDH-ES+A256KW'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (jwk.use === 'sig' || jwk.use === undefined) {
|
|
26
|
+
const crv = 'crv' in jwk ? jwk.crv : undefined
|
|
27
|
+
switch (crv) {
|
|
28
|
+
case 'P-256':
|
|
29
|
+
case 'P-384':
|
|
30
|
+
yield `ES${crv.slice(-3)}`
|
|
31
|
+
break
|
|
32
|
+
case 'P-521':
|
|
33
|
+
yield 'ES512'
|
|
34
|
+
break
|
|
35
|
+
case 'secp256k1':
|
|
36
|
+
if (IS_NODE_RUNTIME) yield 'ES256K'
|
|
37
|
+
break
|
|
38
|
+
default:
|
|
39
|
+
throw new JwkError(`Unsupported crv "${crv}"`)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
case 'OKP': {
|
|
47
|
+
if (!jwk.use) throw new JwkError('Missing "use" Parameter value')
|
|
48
|
+
yield 'ECDH-ES'
|
|
49
|
+
yield 'ECDH-ES+A128KW'
|
|
50
|
+
yield 'ECDH-ES+A192KW'
|
|
51
|
+
yield 'ECDH-ES+A256KW'
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case 'RSA': {
|
|
56
|
+
if (jwk.use === 'enc' || jwk.use === undefined) {
|
|
57
|
+
yield 'RSA-OAEP'
|
|
58
|
+
yield 'RSA-OAEP-256'
|
|
59
|
+
yield 'RSA-OAEP-384'
|
|
60
|
+
yield 'RSA-OAEP-512'
|
|
61
|
+
if (IS_NODE_RUNTIME) yield 'RSA1_5'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (jwk.use === 'sig' || jwk.use === undefined) {
|
|
65
|
+
yield 'PS256'
|
|
66
|
+
yield 'PS384'
|
|
67
|
+
yield 'PS512'
|
|
68
|
+
yield 'RS256'
|
|
69
|
+
yield 'RS384'
|
|
70
|
+
yield 'RS512'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case 'oct': {
|
|
77
|
+
if (jwk.use === 'enc' || jwk.use === undefined) {
|
|
78
|
+
yield 'A128GCMKW'
|
|
79
|
+
yield 'A192GCMKW'
|
|
80
|
+
yield 'A256GCMKW'
|
|
81
|
+
yield 'A128KW'
|
|
82
|
+
yield 'A192KW'
|
|
83
|
+
yield 'A256KW'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (jwk.use === 'sig' || jwk.use === undefined) {
|
|
87
|
+
yield 'HS256'
|
|
88
|
+
yield 'HS384'
|
|
89
|
+
yield 'HS512'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
default:
|
|
96
|
+
throw new JwkError(`Unsupported kty "${jwk.kty}"`)
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type ErrorOptions = { cause?: unknown }
|
|
2
|
+
|
|
3
|
+
export const ERR_JWKS_NO_MATCHING_KEY = 'ERR_JWKS_NO_MATCHING_KEY'
|
|
4
|
+
export const ERR_JWK_INVALID = 'ERR_JWK_INVALID'
|
|
5
|
+
export const ERR_JWK_NOT_FOUND = 'ERR_JWK_NOT_FOUND'
|
|
6
|
+
export const ERR_JWT_INVALID = 'ERR_JWT_INVALID'
|
|
7
|
+
export const ERR_JWT_CREATE = 'ERR_JWT_CREATE'
|
|
8
|
+
export const ERR_JWT_VERIFY = 'ERR_JWT_VERIFY'
|
|
9
|
+
|
|
10
|
+
export class JwkError extends TypeError {
|
|
11
|
+
constructor(
|
|
12
|
+
message = 'JWK error',
|
|
13
|
+
public readonly code = ERR_JWK_INVALID,
|
|
14
|
+
options?: ErrorOptions,
|
|
15
|
+
) {
|
|
16
|
+
super(message, options)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class JwtCreateError extends Error {
|
|
21
|
+
constructor(
|
|
22
|
+
message = 'Unable to create JWT',
|
|
23
|
+
public readonly code = ERR_JWT_CREATE,
|
|
24
|
+
options?: ErrorOptions,
|
|
25
|
+
) {
|
|
26
|
+
super(message, options)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static from(cause: unknown, code?: string, message?: string): JwtCreateError {
|
|
30
|
+
if (cause instanceof JwtCreateError) return cause
|
|
31
|
+
if (cause instanceof JwkError) {
|
|
32
|
+
return new JwtCreateError(message, cause.code, { cause })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return new JwtCreateError(message, code, { cause })
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class JwtVerifyError extends Error {
|
|
40
|
+
constructor(
|
|
41
|
+
message = 'Invalid JWT',
|
|
42
|
+
public readonly code = ERR_JWT_VERIFY,
|
|
43
|
+
options?: ErrorOptions,
|
|
44
|
+
) {
|
|
45
|
+
super(message, options)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static from(cause: unknown, code?: string, message?: string): JwtVerifyError {
|
|
49
|
+
if (cause instanceof JwtVerifyError) return cause
|
|
50
|
+
if (cause instanceof JwkError) {
|
|
51
|
+
return new JwtVerifyError(message, cause.code, { cause })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return new JwtVerifyError(message, code, { cause })
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './alg.js'
|
|
2
|
+
export * from './errors.js'
|
|
3
|
+
export * from './jwk.js'
|
|
4
|
+
export * from './jwks.js'
|
|
5
|
+
export * from './jwt-decode.js'
|
|
6
|
+
export * from './jwt-verify.js'
|
|
7
|
+
export * from './jwt.js'
|
|
8
|
+
export * from './key.js'
|
|
9
|
+
export * from './keyset.js'
|
|
10
|
+
export * from './util.js'
|
package/src/jwk.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const keyUsageSchema = z.enum([
|
|
4
|
+
'sign',
|
|
5
|
+
'verify',
|
|
6
|
+
'encrypt',
|
|
7
|
+
'decrypt',
|
|
8
|
+
'wrapKey',
|
|
9
|
+
'unwrapKey',
|
|
10
|
+
'deriveKey',
|
|
11
|
+
'deriveBits',
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
export type KeyUsage = z.infer<typeof keyUsageSchema>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The "use" and "key_ops" JWK members SHOULD NOT be used together;
|
|
18
|
+
* however, if both are used, the information they convey MUST be
|
|
19
|
+
* consistent. Applications should specify which of these members they
|
|
20
|
+
* use, if either is to be used by the application.
|
|
21
|
+
*
|
|
22
|
+
* @todo Actually check that "use" and "key_ops" are consistent when both are present.
|
|
23
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7517#section-4.3}
|
|
24
|
+
*/
|
|
25
|
+
export const jwkBaseSchema = z.object({
|
|
26
|
+
kty: z.string().min(1),
|
|
27
|
+
alg: z.string().min(1).optional(),
|
|
28
|
+
kid: z.string().min(1).optional(),
|
|
29
|
+
ext: z.boolean().optional(),
|
|
30
|
+
use: z.enum(['sig', 'enc']).optional(),
|
|
31
|
+
key_ops: z.array(keyUsageSchema).optional(),
|
|
32
|
+
|
|
33
|
+
x5c: z.array(z.string()).optional(), // X.509 Certificate Chain
|
|
34
|
+
x5t: z.string().min(1).optional(), // X.509 Certificate SHA-1 Thumbprint
|
|
35
|
+
'x5t#S256': z.string().min(1).optional(), // X.509 Certificate SHA-256 Thumbprint
|
|
36
|
+
x5u: z.string().url().optional(), // X.509 URL
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @todo: properly implement this
|
|
41
|
+
*/
|
|
42
|
+
export const jwkRsaKeySchema = jwkBaseSchema.extend({
|
|
43
|
+
kty: z.literal('RSA'),
|
|
44
|
+
alg: z
|
|
45
|
+
.enum(['RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512'])
|
|
46
|
+
.optional(),
|
|
47
|
+
|
|
48
|
+
n: z.string().min(1), // Modulus
|
|
49
|
+
e: z.string().min(1), // Exponent
|
|
50
|
+
|
|
51
|
+
d: z.string().min(1).optional(), // Private Exponent
|
|
52
|
+
p: z.string().min(1).optional(), // First Prime Factor
|
|
53
|
+
q: z.string().min(1).optional(), // Second Prime Factor
|
|
54
|
+
dp: z.string().min(1).optional(), // First Factor CRT Exponent
|
|
55
|
+
dq: z.string().min(1).optional(), // Second Factor CRT Exponent
|
|
56
|
+
qi: z.string().min(1).optional(), // First CRT Coefficient
|
|
57
|
+
oth: z
|
|
58
|
+
.array(
|
|
59
|
+
z.object({
|
|
60
|
+
r: z.string().optional(),
|
|
61
|
+
d: z.string().optional(),
|
|
62
|
+
t: z.string().optional(),
|
|
63
|
+
}),
|
|
64
|
+
)
|
|
65
|
+
.nonempty()
|
|
66
|
+
|
|
67
|
+
.optional(), // Other Primes Info
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
export const jwkEcKeySchema = jwkBaseSchema.extend({
|
|
71
|
+
kty: z.literal('EC'),
|
|
72
|
+
alg: z.enum(['ES256', 'ES384', 'ES512']).optional(),
|
|
73
|
+
crv: z.enum(['P-256', 'P-384', 'P-521']),
|
|
74
|
+
|
|
75
|
+
x: z.string().min(1),
|
|
76
|
+
y: z.string().min(1),
|
|
77
|
+
|
|
78
|
+
d: z.string().min(1).optional(), // ECC Private Key
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
export const jwkEcSecp256k1KeySchema = jwkBaseSchema.extend({
|
|
82
|
+
kty: z.literal('EC'),
|
|
83
|
+
alg: z.enum(['ES256K']).optional(),
|
|
84
|
+
crv: z.enum(['secp256k1']),
|
|
85
|
+
|
|
86
|
+
x: z.string().min(1),
|
|
87
|
+
y: z.string().min(1),
|
|
88
|
+
|
|
89
|
+
d: z.string().min(1).optional(), // ECC Private Key
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
export const jwkOkpKeySchema = jwkBaseSchema.extend({
|
|
93
|
+
kty: z.literal('OKP'),
|
|
94
|
+
alg: z.enum(['EdDSA']).optional(),
|
|
95
|
+
crv: z.enum(['Ed25519', 'Ed448']),
|
|
96
|
+
|
|
97
|
+
x: z.string().min(1),
|
|
98
|
+
d: z.string().min(1).optional(), // ECC Private Key
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
export const jwkSymKeySchema = jwkBaseSchema.extend({
|
|
102
|
+
kty: z.literal('oct'), // Octet Sequence (used to represent symmetric keys)
|
|
103
|
+
alg: z.enum(['HS256', 'HS384', 'HS512']).optional(),
|
|
104
|
+
|
|
105
|
+
k: z.string(), // Key Value (base64url encoded)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
export const jwkUnknownKeySchema = jwkBaseSchema.extend({
|
|
109
|
+
kty: z
|
|
110
|
+
.string()
|
|
111
|
+
.refine((v) => v !== 'RSA' && v !== 'EC' && v !== 'OKP' && v !== 'oct'),
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
export const jwkSchema = z.union([
|
|
115
|
+
jwkUnknownKeySchema,
|
|
116
|
+
jwkRsaKeySchema,
|
|
117
|
+
jwkEcKeySchema,
|
|
118
|
+
jwkEcSecp256k1KeySchema,
|
|
119
|
+
jwkOkpKeySchema,
|
|
120
|
+
jwkSymKeySchema,
|
|
121
|
+
])
|
|
122
|
+
|
|
123
|
+
export type Jwk = z.infer<typeof jwkSchema>
|
|
124
|
+
|
|
125
|
+
export const jwkValidator = jwkSchema
|
|
126
|
+
.refine((k) => k.use != null || k.key_ops != null, 'use or key_ops required')
|
|
127
|
+
.refine(
|
|
128
|
+
(k) =>
|
|
129
|
+
!k.use ||
|
|
130
|
+
!k.key_ops ||
|
|
131
|
+
k.key_ops.every((o) =>
|
|
132
|
+
k.use === 'sig'
|
|
133
|
+
? o === 'sign' || o === 'verify'
|
|
134
|
+
: o === 'encrypt' || o === 'decrypt',
|
|
135
|
+
),
|
|
136
|
+
'use and key_ops must be consistent',
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
export const jwkPubSchema = jwkValidator
|
|
140
|
+
.refine((k) => k.kid != null, 'kid is required')
|
|
141
|
+
.refine((k) => !('k' in k) && !('d' in k), 'private key not allowed')
|
package/src/jwks.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { jwkPubSchema, jwkSchema } from './jwk.js'
|
|
4
|
+
|
|
5
|
+
export const jwksSchema = z.object({
|
|
6
|
+
keys: z.array(jwkSchema),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export type Jwks = z.infer<typeof jwksSchema>
|
|
10
|
+
|
|
11
|
+
export const jwksPubSchema = z.object({
|
|
12
|
+
keys: z.array(jwkPubSchema),
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export type JwksPub = z.infer<typeof jwksPubSchema>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ERR_JWT_INVALID, JwtVerifyError } from './errors.js'
|
|
2
|
+
import {
|
|
3
|
+
JwtHeader,
|
|
4
|
+
JwtPayload,
|
|
5
|
+
jwtHeaderSchema,
|
|
6
|
+
jwtPayloadSchema,
|
|
7
|
+
} from './jwt.js'
|
|
8
|
+
import { parseB64uJson } from './util.js'
|
|
9
|
+
|
|
10
|
+
export function unsafeDecodeJwt(jwt: string): {
|
|
11
|
+
header: JwtHeader
|
|
12
|
+
payload: JwtPayload
|
|
13
|
+
} {
|
|
14
|
+
const { 0: headerEnc, 1: payloadEnc, length } = jwt.split('.')
|
|
15
|
+
if (length > 3 || length < 2) {
|
|
16
|
+
throw new JwtVerifyError(undefined, ERR_JWT_INVALID)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const header = jwtHeaderSchema.parse(parseB64uJson(headerEnc!))
|
|
20
|
+
if (length === 2 && header?.alg !== 'none') {
|
|
21
|
+
throw new JwtVerifyError(undefined, ERR_JWT_INVALID)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const payload = jwtPayloadSchema.parse(parseB64uJson(payloadEnc!))
|
|
25
|
+
|
|
26
|
+
return { header, payload }
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { JwtHeader, JwtPayload } from './jwt.js'
|
|
2
|
+
import { RequiredKey } from './util.js'
|
|
3
|
+
|
|
4
|
+
export type VerifyOptions<C extends string = string> = {
|
|
5
|
+
audience?: string | readonly string[]
|
|
6
|
+
/** in seconds */
|
|
7
|
+
clockTolerance?: number
|
|
8
|
+
issuer?: string | readonly string[]
|
|
9
|
+
/** in seconds */
|
|
10
|
+
maxTokenAge?: number
|
|
11
|
+
subject?: string
|
|
12
|
+
typ?: string
|
|
13
|
+
currentDate?: Date
|
|
14
|
+
requiredClaims?: readonly C[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type VerifyPayload = Record<string, unknown>
|
|
18
|
+
|
|
19
|
+
export type VerifyResult<P extends VerifyPayload, C extends string> = {
|
|
20
|
+
payload: RequiredKey<P & JwtPayload, C>
|
|
21
|
+
protectedHeader: JwtHeader
|
|
22
|
+
}
|