unjwt 0.8.2 → 0.8.4
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/dist/_chunks/_codec-jwe.d.mts +24 -28
- package/dist/_chunks/_codec-jws.mjs +33 -33
- package/dist/_chunks/{jwk.mjs → _import-key.mjs} +36 -741
- package/dist/_chunks/_jwt-internal.mjs +28 -25
- package/dist/_chunks/index.d.mts +5 -3
- package/dist/_chunks/jwe.d.mts +1 -0
- package/dist/_chunks/jwe.mjs +8 -7
- package/dist/_chunks/jwk.d.mts +205 -0
- package/dist/_chunks/jws.d.mts +4 -1
- package/dist/_chunks/jws.mjs +10 -10
- package/dist/_chunks/jwt.d.mts +2 -1
- package/dist/_chunks/libs/unsecure.mjs +143 -130
- package/dist/adapters/elysia/index.d.mts +1 -1
- package/dist/adapters/elysia/index.mjs +3 -2
- package/dist/adapters/h3/index.d.mts +1 -1
- package/dist/adapters/h3/index.mjs +2 -1
- package/dist/adapters/h3v1/index.d.mts +1 -1
- package/dist/adapters/h3v1/index.mjs +2 -1
- package/dist/core/jwe.mjs +1 -1
- package/dist/core/jwk.d.mts +2 -204
- package/dist/core/jwk.mjs +712 -3
- package/dist/core/jws.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -1
- package/dist/session/index.d.mts +22 -22
- package/dist/session/index.mjs +22 -19
- package/package.json +3 -2
|
@@ -39,7 +39,7 @@ interface SessionBaseConfig<MaxAge extends ExpiresIn | undefined = ExpiresIn | u
|
|
|
39
39
|
* first 15 minutes change nothing; the first read after that issues a fresh hour. Must be
|
|
40
40
|
* shorter than `maxAge`.
|
|
41
41
|
*
|
|
42
|
-
* A read that cannot write (a read-only
|
|
42
|
+
* A read that cannot write (a read-only event, a response already sent) does not reissue;
|
|
43
43
|
* the next request, carrying the same token, does.
|
|
44
44
|
*
|
|
45
45
|
* @default undefined — the session expires at `iat + maxAge` however often it is read.
|
|
@@ -124,63 +124,59 @@ export type OpenedSessionState<T extends SessionRecord, MaxAge extends ExpiresIn
|
|
|
124
124
|
createdAt: number;
|
|
125
125
|
};
|
|
126
126
|
/** One session, defined once from its config and used on any request. */
|
|
127
|
-
export interface SessionDefinition<T extends SessionRecord = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined,
|
|
127
|
+
export interface SessionDefinition<T extends SessionRecord = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent = unknown> {
|
|
128
128
|
/** Reads the request's session once and returns its manager; later calls on the same request share that state. */
|
|
129
|
-
use: (
|
|
129
|
+
use: (event: TEvent) => Promise<SessionManager<T, MaxAge>>;
|
|
130
130
|
/** The token the request carries (this response's own write first), unopened. */
|
|
131
|
-
token: (
|
|
131
|
+
token: (event: TEvent) => string | undefined;
|
|
132
132
|
/** Mints a token for `data` with a fresh `jti` and `iat`, and `exp` from `maxAge` (`options.maxAge` overrides it); reads and writes nothing. */
|
|
133
133
|
issue: (data?: Partial<SessionData<T>>, options?: SessionIssueOptions) => Promise<string>;
|
|
134
134
|
/** Opens a token into session state, checking `typ`, pinned algorithms, `jti`/`iat` and `exp` when `maxAge` is set; the key-lookup hook receives no request. */
|
|
135
135
|
open: (token: string) => Promise<OpenedSessionState<T, MaxAge>>;
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
export interface SessionRequestEvent {
|
|
139
|
-
readonly req: Request;
|
|
137
|
+
interface SessionRequestBase {
|
|
140
138
|
/** The response being prepared; without it the session is read-only. */
|
|
141
139
|
readonly res?: {
|
|
142
140
|
readonly headers: Headers;
|
|
143
141
|
};
|
|
144
|
-
/**
|
|
142
|
+
/** Per-request state shared with the framework, where `sessions` is kept; without it state is keyed on `req`, else on this object. */
|
|
145
143
|
readonly context?: Record<string, unknown>;
|
|
146
144
|
}
|
|
147
|
-
/** A request by its
|
|
148
|
-
export
|
|
145
|
+
/** A request event: by its `Request`, or by its `Headers` (a `Request` itself, a WebSocket upgrade's request). */
|
|
146
|
+
export type SessionRequestEvent = SessionRequestBase & ({
|
|
147
|
+
readonly req: Request;
|
|
148
|
+
readonly headers?: Headers;
|
|
149
|
+
} | {
|
|
149
150
|
readonly headers: Headers;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
readonly headers: Headers;
|
|
153
|
-
};
|
|
154
|
-
/** Per-request state shared with the framework; without it state is keyed on this object. */
|
|
155
|
-
readonly context?: Record<string, unknown>;
|
|
156
|
-
}
|
|
151
|
+
readonly req?: Request;
|
|
152
|
+
});
|
|
157
153
|
/** A token handed in directly; sending a re-issued one is the caller's job. */
|
|
158
|
-
export interface
|
|
154
|
+
export interface SessionHandedToken {
|
|
159
155
|
token: string | undefined;
|
|
160
156
|
}
|
|
161
|
-
/** What
|
|
162
|
-
export type
|
|
157
|
+
/** What the Web session driver takes: a request event, or a token in hand. */
|
|
158
|
+
export type WebSessionEvent = SessionRequestEvent | SessionHandedToken;
|
|
163
159
|
/** The cookies of one request and its response, as a framework exposes them. */
|
|
164
160
|
export interface CookieJar<A extends ExpirableCookieAttributes = ExpirableCookieAttributes> {
|
|
165
161
|
/** The request's cookie. */
|
|
166
162
|
read(name: string): string | undefined;
|
|
167
163
|
/** The value this response already assigned to `name` (`""` once deleted), when the framework exposes it. */
|
|
168
164
|
written?(name: string): string | undefined;
|
|
169
|
-
/** Absent when this
|
|
165
|
+
/** Absent when this event cannot take a `Set-Cookie` (a request without a response). */
|
|
170
166
|
write?(name: string, value: string, attrs: A): void;
|
|
171
167
|
}
|
|
172
168
|
/** The primitives a framework supplies; the session core builds the cookie and header transports over them. */
|
|
173
|
-
export interface SessionDriver<
|
|
169
|
+
export interface SessionDriver<TEvent> {
|
|
174
170
|
/** Message prefix for every error the adapter emits. */
|
|
175
171
|
prefix: string;
|
|
176
172
|
/** The per-request record session state and the session-name registry live in. */
|
|
177
|
-
state(
|
|
178
|
-
header(
|
|
179
|
-
cookies(
|
|
173
|
+
state(event: TEvent): Record<string, unknown>;
|
|
174
|
+
header(event: TEvent, name: string): string | null | undefined;
|
|
175
|
+
cookies(event: TEvent): CookieJar;
|
|
180
176
|
/** Platform signal that the response can still change (Node `headersSent`); absent = trust the jar. */
|
|
181
|
-
writable?(
|
|
182
|
-
/** A token the
|
|
183
|
-
handed?(
|
|
177
|
+
writable?(event: TEvent): boolean;
|
|
178
|
+
/** A token the event carries directly, when it does: no cookie or header is consulted. */
|
|
179
|
+
handed?(event: TEvent): {
|
|
184
180
|
token: string | undefined;
|
|
185
181
|
} | undefined;
|
|
186
182
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sanitizeObjectCopy } from "./libs/unsecure.mjs";
|
|
2
2
|
import { JWTError, computeDurationInSeconds, expiredClaimMessage, isCryptoKey, isJWK, isJWKSet, isJWTError, isPlainObject, isPrivateJWK, isPublicJWK, isSymmetricJWK } from "./_jwt-internal.mjs";
|
|
3
|
-
import { importKey, quoteTokenValue } from "./
|
|
3
|
+
import { importKey, quoteTokenValue } from "./_import-key.mjs";
|
|
4
4
|
import { sign, verify } from "./jws.mjs";
|
|
5
5
|
import { decrypt, encrypt } from "./jwe.mjs";
|
|
6
6
|
const RESERVED_CLAIMS = [
|
|
@@ -46,8 +46,8 @@ function rethrowAsInvalidToken(error, options) {
|
|
|
46
46
|
const TOKEN = /^[!#$%&'*+\-.^_`|~\w]+$/;
|
|
47
47
|
const CHUNK_SUFFIX = /\.\d+$/;
|
|
48
48
|
function assertSessionName(name, options) {
|
|
49
|
-
if (!isToken(name)) throw invalidConfigError(`${options.prefix} Session "name" must be a cookie name (
|
|
50
|
-
if (CHUNK_SUFFIX.test(name)) throw invalidConfigError(`${options.prefix} Session "name"
|
|
49
|
+
if (!isToken(name)) throw invalidConfigError(`${options.prefix} Session "name" must be a valid cookie name (got ${quoteTokenValue(name)}).`);
|
|
50
|
+
if (CHUNK_SUFFIX.test(name)) throw invalidConfigError(`${options.prefix} Session "name" ${quoteTokenValue(name)} cannot end in a dot and digits.`);
|
|
51
51
|
}
|
|
52
52
|
function isToken(value) {
|
|
53
53
|
return typeof value === "string" && TOKEN.test(value);
|
|
@@ -57,7 +57,7 @@ function assertCookiePrefix(name, attrs, options) {
|
|
|
57
57
|
const host = lower.startsWith("__host-");
|
|
58
58
|
if (!host && !lower.startsWith("__secure-")) return;
|
|
59
59
|
const rule = host ? "\"__Host-\" cookie must be Secure, with Path=/ and no Domain" : "\"__Secure-\" cookie must be Secure";
|
|
60
|
-
if (attrs.secure === false || host && (attrs.domain !== void 0 || attrs.path !== void 0 && attrs.path !== "/")) throw invalidConfigError(`${options.prefix} Session ${quoteTokenValue(name)}: a ${rule}
|
|
60
|
+
if (attrs.secure === false || host && (attrs.domain !== void 0 || attrs.path !== void 0 && attrs.path !== "/")) throw invalidConfigError(`${options.prefix} Session ${quoteTokenValue(name)}: a ${rule}.`);
|
|
61
61
|
}
|
|
62
62
|
function assertRolling(config, options) {
|
|
63
63
|
const { rolling } = config;
|
|
@@ -65,10 +65,10 @@ function assertRolling(config, options) {
|
|
|
65
65
|
if (config.maxAge === void 0) throw invalidConfigError(`${options.prefix} "rolling" needs a "maxAge" to extend.`);
|
|
66
66
|
if (rolling === true) return;
|
|
67
67
|
if (!isExpiresIn(rolling)) throw new JWTError(`${options.prefix} "rolling" must be true or a duration.`, "ERR_INVALID_OPTION");
|
|
68
|
-
if (computeDurationInSeconds(rolling) >= computeDurationInSeconds(config.maxAge)) throw invalidConfigError(`${options.prefix} "rolling" must be shorter than "maxAge"
|
|
68
|
+
if (computeDurationInSeconds(rolling) >= computeDurationInSeconds(config.maxAge)) throw invalidConfigError(`${options.prefix} "rolling" must be shorter than "maxAge".`);
|
|
69
69
|
}
|
|
70
70
|
function assertKeyMaterial(key, options) {
|
|
71
|
-
if (typeof key === "string") throw invalidKeyError(`${options.prefix} Session "key"
|
|
71
|
+
if (typeof key === "string") throw invalidKeyError(`${options.prefix} Session "key" cannot be a string, use deriveJWKFromPassword().`);
|
|
72
72
|
if (!key) throw invalidKeyError(`${options.prefix} Session "key" is required.`);
|
|
73
73
|
}
|
|
74
74
|
async function assertImportableKey(key, options) {
|
|
@@ -109,7 +109,7 @@ function sessionRequiredClaims(configured, options) {
|
|
|
109
109
|
return [...required];
|
|
110
110
|
}
|
|
111
111
|
function sanitizeSessionUpdate(update, options) {
|
|
112
|
-
if (!isPlainObject(update)) throw new JWTError(`${options.prefix} Session data must be a plain object
|
|
112
|
+
if (!isPlainObject(update)) throw new JWTError(`${options.prefix} Session data must be a plain object.`, "ERR_INVALID_OPTION");
|
|
113
113
|
const sanitized = Object.create(null);
|
|
114
114
|
for (const key of Object.keys(update)) {
|
|
115
115
|
if (FORBIDDEN_KEYS.includes(key)) throw new JWTError(`${options.prefix} Session data cannot carry the key "${key}".`, "ERR_INVALID_OPTION");
|
|
@@ -205,7 +205,7 @@ function assertKeyPair(pair, options) {
|
|
|
205
205
|
if (!options.isPublic(pair.publicKey)) throw invalidKeyError(`${prefix} Invalid ${family} key: "publicKey" must be ${options.publicHalf}.`, pair);
|
|
206
206
|
}
|
|
207
207
|
function assertPinnedCryptoKey(key, options) {
|
|
208
|
-
if (isCryptoKey(key) && !options.pinned) throw invalidConfigError(`${options.prefix} A CryptoKey session key carries no algorithm
|
|
208
|
+
if (isCryptoKey(key) && !options.pinned) throw invalidConfigError(`${options.prefix} A CryptoKey session key carries no algorithm, set ${options.hint}.`);
|
|
209
209
|
}
|
|
210
210
|
function sessionPayload(state) {
|
|
211
211
|
const iat = Math.floor(state.createdAt / 1e3);
|
|
@@ -219,7 +219,7 @@ function sessionPayload(state) {
|
|
|
219
219
|
}
|
|
220
220
|
function assertNotFromTheFuture(payload, options) {
|
|
221
221
|
const tolerance = options.clockTolerance === void 0 ? 0 : computeDurationInSeconds(options.clockTolerance);
|
|
222
|
-
if (payload.iat > Math.floor(options.now / 1e3) + tolerance) throw invalidTokenError(`${options.prefix} Invalid session token:
|
|
222
|
+
if (payload.iat > Math.floor(options.now / 1e3) + tolerance) throw invalidTokenError(`${options.prefix} Invalid session token: "iat" is in the future (${(/* @__PURE__ */ new Date(payload.iat * 1e3)).toISOString()}).`);
|
|
223
223
|
}
|
|
224
224
|
function openedSession(payload) {
|
|
225
225
|
const data = Object.create(null);
|
|
@@ -294,7 +294,7 @@ function getEncryptKey(config, prefix) {
|
|
|
294
294
|
assertJWEKeyPair(key, prefix);
|
|
295
295
|
resolved = key.publicKey;
|
|
296
296
|
}
|
|
297
|
-
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWE key
|
|
297
|
+
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWE key, expected a JWK or a CryptoKey.`, key);
|
|
298
298
|
assertPinnedAlgEnc(resolved, config, prefix);
|
|
299
299
|
return resolved;
|
|
300
300
|
}
|
|
@@ -307,7 +307,7 @@ function getDecryptKey(config, prefix) {
|
|
|
307
307
|
assertJWEKeyPair(key, prefix);
|
|
308
308
|
resolved = key.privateKey;
|
|
309
309
|
}
|
|
310
|
-
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWE key
|
|
310
|
+
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWE key, expected a JWK, a private JWK or a CryptoKey.`, key);
|
|
311
311
|
assertPinnedAlgEnc(resolved, config, prefix);
|
|
312
312
|
return resolved;
|
|
313
313
|
}
|
|
@@ -409,21 +409,21 @@ function decodeValue(value) {
|
|
|
409
409
|
return value;
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
|
-
function resolveTransport(driver,
|
|
413
|
-
const handed = driver.handed?.(
|
|
412
|
+
function resolveTransport(driver, event, options) {
|
|
413
|
+
const handed = driver.handed?.(event);
|
|
414
414
|
if (handed) return {
|
|
415
415
|
read: () => handed.token,
|
|
416
416
|
readonly: false,
|
|
417
417
|
writable: () => true
|
|
418
418
|
};
|
|
419
419
|
const { config, name } = options;
|
|
420
|
-
const header = headerReader(driver,
|
|
420
|
+
const header = headerReader(driver, event, options);
|
|
421
421
|
if (config.cookie === false) return {
|
|
422
422
|
read: header,
|
|
423
423
|
readonly: false,
|
|
424
424
|
writable: () => true
|
|
425
425
|
};
|
|
426
|
-
const jar = driver.cookies(
|
|
426
|
+
const jar = driver.cookies(event);
|
|
427
427
|
const store = responseStore(jar);
|
|
428
428
|
const transport = {
|
|
429
429
|
read: () => {
|
|
@@ -432,7 +432,7 @@ function resolveTransport(driver, context, options) {
|
|
|
432
432
|
return (written ?? readChunked(jar, name)) || header?.() || void 0;
|
|
433
433
|
},
|
|
434
434
|
readonly: !store,
|
|
435
|
-
writable: () => driver.writable?.(
|
|
435
|
+
writable: () => driver.writable?.(event) ?? true
|
|
436
436
|
};
|
|
437
437
|
if (!store) return transport;
|
|
438
438
|
transport.write = (token, { attrs, chunkMaxLength }) => {
|
|
@@ -448,7 +448,7 @@ function resolveTransport(driver, context, options) {
|
|
|
448
448
|
return transport;
|
|
449
449
|
}
|
|
450
450
|
function assertTokenSource(transport, options) {
|
|
451
|
-
if (!transport.read) throw invalidConfigError(`${options.prefix} Session has no token source
|
|
451
|
+
if (!transport.read) throw invalidConfigError(`${options.prefix} Session has no token source, enable "cookie" or "sessionHeader".`);
|
|
452
452
|
}
|
|
453
453
|
function assertWritable(transport, options) {
|
|
454
454
|
const { operation, prefix } = options;
|
|
@@ -458,7 +458,7 @@ function assertWritable(transport, options) {
|
|
|
458
458
|
function assertSessionHeader(header, options) {
|
|
459
459
|
if (header === void 0 || typeof header === "boolean") return;
|
|
460
460
|
if (isToken(header)) return;
|
|
461
|
-
throw invalidConfigError(`${options.prefix} Session "sessionHeader" must be true, false or a header name
|
|
461
|
+
throw invalidConfigError(`${options.prefix} Session "sessionHeader" must be true, false or a header name (got ${quoteTokenValue(header)}).`);
|
|
462
462
|
}
|
|
463
463
|
function sessionHeaderName(sessionName, configured) {
|
|
464
464
|
if (typeof configured === "string") return configured.toLowerCase();
|
|
@@ -471,11 +471,11 @@ function headerToken(value) {
|
|
|
471
471
|
if (/^bearer$/i.test(first)) return words.length === 2 ? second : void 0;
|
|
472
472
|
return words.length === 1 && first ? first : void 0;
|
|
473
473
|
}
|
|
474
|
-
function headerReader(driver,
|
|
474
|
+
function headerReader(driver, event, options) {
|
|
475
475
|
const { config, name } = options;
|
|
476
476
|
if (config.sessionHeader === false) return void 0;
|
|
477
477
|
const header = sessionHeaderName(name, config.sessionHeader);
|
|
478
|
-
return () => headerToken(driver.header(
|
|
478
|
+
return () => headerToken(driver.header(event, header));
|
|
479
479
|
}
|
|
480
480
|
function responseStore(jar) {
|
|
481
481
|
const write = jar.write?.bind(jar);
|
|
@@ -486,7 +486,7 @@ function responseStore(jar) {
|
|
|
486
486
|
};
|
|
487
487
|
}
|
|
488
488
|
const kSessionNames = Symbol("unjwt_session_names");
|
|
489
|
-
function bindSession(
|
|
489
|
+
function bindSession(event, config, options) {
|
|
490
490
|
const { driver, codec, actions, hookArgs } = options;
|
|
491
491
|
const { prefix } = driver;
|
|
492
492
|
const name = config.name || codec.defaultName;
|
|
@@ -497,22 +497,22 @@ function bindSession(context, config, options) {
|
|
|
497
497
|
...config.cookie
|
|
498
498
|
}, { prefix });
|
|
499
499
|
assertRolling(config, { prefix });
|
|
500
|
-
const record = driver.state(
|
|
500
|
+
const record = driver.state(event);
|
|
501
501
|
claimSessionName(record, {
|
|
502
502
|
name,
|
|
503
503
|
family: codec.family,
|
|
504
504
|
prefix
|
|
505
505
|
});
|
|
506
506
|
return {
|
|
507
|
-
|
|
507
|
+
event,
|
|
508
508
|
config,
|
|
509
509
|
driver,
|
|
510
510
|
codec,
|
|
511
511
|
actions,
|
|
512
512
|
name,
|
|
513
|
-
args: hookArgs(
|
|
513
|
+
args: hookArgs(event),
|
|
514
514
|
store: recordSlotStore(record, `${codec.family}:${name}`),
|
|
515
|
-
transport: resolveTransport(driver,
|
|
515
|
+
transport: resolveTransport(driver, event, {
|
|
516
516
|
config,
|
|
517
517
|
name
|
|
518
518
|
})
|
|
@@ -561,7 +561,7 @@ function claimSessionName(record, options) {
|
|
|
561
561
|
return;
|
|
562
562
|
}
|
|
563
563
|
if (held === family) return;
|
|
564
|
-
throw invalidConfigError(`${prefix} Session name "${name}" is already used by a ${held.toUpperCase()} session on this request
|
|
564
|
+
throw invalidConfigError(`${prefix} Session name "${name}" is already used by a ${held.toUpperCase()} session on this request.`);
|
|
565
565
|
}
|
|
566
566
|
const kSessionPending = Symbol("unjwt_session_pending");
|
|
567
567
|
function sessionInstant(source) {
|
|
@@ -828,19 +828,19 @@ function snapshotSession(session) {
|
|
|
828
828
|
function defineSession(config, options) {
|
|
829
829
|
const { driver, codec, hookArgs } = options;
|
|
830
830
|
const actions = sessionActions();
|
|
831
|
-
const bind = (
|
|
831
|
+
const bind = (event) => bindSession(event, config, {
|
|
832
832
|
driver,
|
|
833
833
|
codec,
|
|
834
834
|
actions,
|
|
835
835
|
hookArgs
|
|
836
836
|
});
|
|
837
837
|
return {
|
|
838
|
-
use: async (
|
|
839
|
-
const binding = bind(
|
|
838
|
+
use: async (event) => {
|
|
839
|
+
const binding = bind(event);
|
|
840
840
|
await loadSession(binding);
|
|
841
841
|
return sessionManager(binding);
|
|
842
842
|
},
|
|
843
|
-
token: (
|
|
843
|
+
token: (event) => sessionToken(bind(event)),
|
|
844
844
|
issue: async (data, issueOptions) => {
|
|
845
845
|
await codec.assertKeys(config);
|
|
846
846
|
const issue = codec.issuer(config);
|
|
@@ -914,7 +914,7 @@ function getSignKey(config, prefix) {
|
|
|
914
914
|
assertJWSKeyPair(key, prefix);
|
|
915
915
|
resolved = key.privateKey;
|
|
916
916
|
}
|
|
917
|
-
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWS key
|
|
917
|
+
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWS key, expected a symmetric JWK, a private JWK or a CryptoKey.`, key);
|
|
918
918
|
assertPinnedAlg(resolved, config, prefix);
|
|
919
919
|
return resolved;
|
|
920
920
|
}
|
|
@@ -927,7 +927,7 @@ function getVerifyKey(config, prefix) {
|
|
|
927
927
|
assertJWSKeyPair(key, prefix);
|
|
928
928
|
resolved = Array.isArray(key.publicKey) ? { keys: key.publicKey } : key.publicKey;
|
|
929
929
|
}
|
|
930
|
-
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWS key
|
|
930
|
+
if (!resolved) throw invalidKeyError(`${prefix} Invalid JWS key, expected a symmetric JWK, a public JWK/set or a CryptoKey.`, key);
|
|
931
931
|
assertPinnedAlg(resolved, config, prefix);
|
|
932
932
|
return resolved;
|
|
933
933
|
}
|
|
@@ -936,7 +936,7 @@ function assertJWSKeyPair(key, prefix) {
|
|
|
936
936
|
prefix,
|
|
937
937
|
family: "JWS",
|
|
938
938
|
isPublic: (half) => isCryptoKey(half) || isPublicJWK(half) || isPublicKeyList(Array.isArray(half) ? half : isJWKSet(half) ? half.keys : void 0),
|
|
939
|
-
publicHalf: "a public JWK, a non-empty
|
|
939
|
+
publicHalf: "a public JWK, a non-empty JWK set or a CryptoKey"
|
|
940
940
|
});
|
|
941
941
|
}
|
|
942
942
|
function isPublicKeyList(keys) {
|