@sourceregistry/sveltekit-oidc 1.6.8 → 1.6.10
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/README.md +2 -1
- package/dist/client/OIDCContext.svelte +35 -2
- package/dist/client/context.d.ts +1 -0
- package/dist/server/index.js +21 -13
- package/dist/server/jwt.d.ts +1 -1
- package/dist/server/jwt.js +2 -2
- package/dist/server/session.d.ts +1 -0
- package/dist/server/session.js +10 -0
- package/dist/server/store.js +4 -1
- package/dist/server/types.d.ts +10 -0
- package/dist/server/utils.d.ts +2 -0
- package/dist/server/utils.js +19 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -298,8 +298,9 @@ Set these environment variables to enable it:
|
|
|
298
298
|
- `cookieSecret` should be a strong random secret and must stay stable across instances.
|
|
299
299
|
- Built-in `returnTo` and logout redirect values are restricted to same-origin paths. Use your own callback `onsuccess` logic if you need custom redirect policy.
|
|
300
300
|
- `clockSkewSeconds` defaults to `30` and tolerates small clock drift between your app and the identity provider.
|
|
301
|
+
- Local browser sessions expire after 8 hours by default (`sessionMaxAgeSeconds`). They also end when an unrefreshable access token expires; set a shorter value for higher-risk applications.
|
|
301
302
|
- `createInMemoryBackChannelLogoutStore()` is suitable for local development or single-instance deployments. Use Redis, SQL, or another shared store for production.
|
|
302
|
-
- The library validates `id_token` and `logout_token` values through `@sourceregistry/node-jwt` and provider JWKS metadata.
|
|
303
|
+
- The library validates `id_token` and `logout_token` values through `@sourceregistry/node-jwt` and provider JWKS metadata. ID tokens must contain `exp`, `iat`, and a matching nonce; UserInfo subjects must match the validated ID token.
|
|
303
304
|
- `groups` are normalized onto the session from `groups` and `roles` claims when present.
|
|
304
305
|
- Use `transformClaims`, `transformUser`, and `transformSession` to project provider-specific claims into your own session shape.
|
|
305
306
|
- `check_session_iframe` monitoring only runs when `monitorSession` is enabled, the provider advertises that endpoint, and the session includes `session_state`.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script lang="ts" generics="TClaims extends OIDCUserClaims = OIDCUserClaims">
|
|
9
|
-
import {invalidateAll} from '$app/navigation';
|
|
9
|
+
import {invalidateAll, beforeNavigate} from '$app/navigation';
|
|
10
10
|
import {tick} from 'svelte';
|
|
11
11
|
import type {Snippet} from 'svelte';
|
|
12
12
|
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
let iframe = $state<HTMLIFrameElement | undefined>(undefined);
|
|
46
46
|
let status = $state<'authenticated' | 'unauthenticated' | 'expired' | 'revoked'>('unauthenticated');
|
|
47
|
+
let revalidating = $state(false);
|
|
47
48
|
let handledUnauthenticated = $state(false);
|
|
48
49
|
|
|
49
50
|
const metadata = $derived(config.metadata);
|
|
@@ -77,6 +78,9 @@
|
|
|
77
78
|
get status() {
|
|
78
79
|
return status;
|
|
79
80
|
},
|
|
81
|
+
get revalidating() {
|
|
82
|
+
return revalidating;
|
|
83
|
+
},
|
|
80
84
|
login: (returnTo?: string) => login(returnTo),
|
|
81
85
|
logout,
|
|
82
86
|
revalidate
|
|
@@ -110,7 +114,32 @@
|
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
async function revalidate() {
|
|
113
|
-
|
|
117
|
+
if (revalidating) return;
|
|
118
|
+
revalidating = true;
|
|
119
|
+
|
|
120
|
+
// If the server redirects to the login path during a background
|
|
121
|
+
// revalidation (e.g. session lost, HMR store reset), intercept the
|
|
122
|
+
// SvelteKit router navigation so we can handle it through
|
|
123
|
+
// handleRedirect instead of a jarring mid-page router transition.
|
|
124
|
+
let sessionExpiredDuringRevalidation = false;
|
|
125
|
+
const stopIntercept = beforeNavigate(({ to, cancel }) => {
|
|
126
|
+
if (to?.url.pathname.startsWith(resolvedLoginPath)) {
|
|
127
|
+
cancel();
|
|
128
|
+
sessionExpiredDuringRevalidation = true;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await invalidateAll();
|
|
134
|
+
} finally {
|
|
135
|
+
stopIntercept();
|
|
136
|
+
revalidating = false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (sessionExpiredDuringRevalidation) {
|
|
140
|
+
status = 'expired';
|
|
141
|
+
void handleRedirect(redirectOnExpired);
|
|
142
|
+
}
|
|
114
143
|
}
|
|
115
144
|
|
|
116
145
|
async function handleRedirect(mode: RedirectMode) {
|
|
@@ -132,6 +161,10 @@
|
|
|
132
161
|
|
|
133
162
|
$effect(() => {
|
|
134
163
|
const isAuthenticated = Boolean(session?.isAuthenticated);
|
|
164
|
+
// Suppress authenticated→unauthenticated transition while a revalidation is
|
|
165
|
+
// in-flight — the effect re-runs once revalidating flips false with the
|
|
166
|
+
// final session state, preventing a mid-refresh flicker.
|
|
167
|
+
if (revalidating && !isAuthenticated) return;
|
|
135
168
|
status = isAuthenticated ? 'authenticated' : status === 'authenticated' ? 'unauthenticated' : status;
|
|
136
169
|
});
|
|
137
170
|
|
package/dist/client/context.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type OIDCClientContextValue<TClaims extends OIDCUserClaims = OIDCUserClai
|
|
|
10
10
|
groups: OIDCPublicSession<TClaims>['groups'];
|
|
11
11
|
metadata?: Pick<OIDCDiscoveryDocument, 'issuer' | 'check_session_iframe' | 'end_session_endpoint' | 'backchannel_logout_supported' | 'backchannel_logout_session_supported'>;
|
|
12
12
|
status: 'authenticated' | 'unauthenticated' | 'expired' | 'revoked';
|
|
13
|
+
revalidating: boolean;
|
|
13
14
|
login: (returnTo?: string) => void;
|
|
14
15
|
logout: (clearSessionOnly?: boolean) => Promise<void>;
|
|
15
16
|
revalidate: () => Promise<void>;
|
package/dist/server/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { randomBytes } from 'node:crypto';
|
|
|
3
3
|
import { decode, fromWeb, verify } from '@sourceregistry/node-jwt/promises';
|
|
4
4
|
import { createOIDCCookieStore } from './cookies.js';
|
|
5
5
|
import { asAuthorizationHeader, createClientSecretJwtAssertion, createPrivateKeyJwtAssertion, fetchJson } from './jwt.js';
|
|
6
|
-
import { normalizeTokens, shouldRefresh } from './session.js';
|
|
7
|
-
import { absoluteUrl, base64UrlEncode, buildCookieOptions, collectGroups, createPKCEPair, internalRedirectPath, normalizeIssuer, normalizeScope, parseProviderError, toPublicSession } from './utils.js';
|
|
6
|
+
import { isSessionExpired, normalizeTokens, shouldRefresh } from './session.js';
|
|
7
|
+
import { absoluteUrl, base64UrlEncode, buildCookieOptions, collectGroups, createPKCEPair, internalRedirectPath, normalizeIssuer, normalizeScope, parseProviderError, toPublicSession, validateIdTokenClaims, validateUserInfoSubject } from './utils.js';
|
|
8
8
|
export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
|
|
9
9
|
import { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
|
|
10
10
|
function buildLogger(logger) {
|
|
@@ -38,12 +38,17 @@ export function createOIDC(options) {
|
|
|
38
38
|
const cookieOptions = buildCookieOptions(options.cookieOptions);
|
|
39
39
|
const clockSkewSeconds = options.clockSkewSeconds ?? 30;
|
|
40
40
|
const refreshToleranceSeconds = options.refreshToleranceSeconds ?? 30;
|
|
41
|
+
const sessionMaxAgeSeconds = options.sessionMaxAgeSeconds ?? 60 * 60 * 8;
|
|
42
|
+
if (!Number.isFinite(sessionMaxAgeSeconds) || sessionMaxAgeSeconds <= 0) {
|
|
43
|
+
throw new TypeError('sessionMaxAgeSeconds must be a positive finite number');
|
|
44
|
+
}
|
|
41
45
|
const sessionCookieName = options.sessionCookieName ?? 'oidc_session';
|
|
42
46
|
const stateCookieName = options.stateCookieName ?? 'oidc_auth_state';
|
|
43
47
|
const defaultScope = normalizeScope(options.scope);
|
|
44
48
|
const loginPath = options.loginPath ?? '/auth/login';
|
|
45
49
|
const redirectPath = options.redirectPath ?? '/auth/callback';
|
|
46
50
|
const clientAuthMethod = options.clientAuthMethod ?? (options.clientSecret ? 'client_secret_basic' : 'none');
|
|
51
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
47
52
|
const cookieStore = createOIDCCookieStore(options.cookieSecret, sessionCookieName, stateCookieName, cookieOptions);
|
|
48
53
|
let metadataPromise;
|
|
49
54
|
let jwksPromise;
|
|
@@ -101,7 +106,7 @@ export function createOIDC(options) {
|
|
|
101
106
|
if (!discoveryUrl) {
|
|
102
107
|
throw error(500, { message: 'OIDC issuer or discoveryUrl must be configured' });
|
|
103
108
|
}
|
|
104
|
-
const document = await fetchJson(discoveryUrl);
|
|
109
|
+
const document = await fetchJson(discoveryUrl, undefined, fetchImpl);
|
|
105
110
|
return {
|
|
106
111
|
...document,
|
|
107
112
|
...options.endpoints,
|
|
@@ -117,7 +122,7 @@ export function createOIDC(options) {
|
|
|
117
122
|
if (!metadata.jwks_uri) {
|
|
118
123
|
throw error(500, { message: 'OIDC jwks_uri is required to validate id_token values' });
|
|
119
124
|
}
|
|
120
|
-
return fromWeb(metadata.jwks_uri, { overrideEndpointCheck: true });
|
|
125
|
+
return fromWeb(metadata.jwks_uri, { overrideEndpointCheck: true, fetch: fetchImpl });
|
|
121
126
|
});
|
|
122
127
|
}
|
|
123
128
|
return jwksPromise;
|
|
@@ -206,7 +211,7 @@ export function createOIDC(options) {
|
|
|
206
211
|
method: 'POST',
|
|
207
212
|
headers: auth.headers,
|
|
208
213
|
body: auth.body
|
|
209
|
-
});
|
|
214
|
+
}, fetchImpl);
|
|
210
215
|
}
|
|
211
216
|
async function refreshTokens(refreshToken) {
|
|
212
217
|
const metadata = await getMetadata();
|
|
@@ -217,7 +222,7 @@ export function createOIDC(options) {
|
|
|
217
222
|
method: 'POST',
|
|
218
223
|
headers: auth.headers,
|
|
219
224
|
body: auth.body
|
|
220
|
-
});
|
|
225
|
+
}, fetchImpl);
|
|
221
226
|
}
|
|
222
227
|
async function validateIdToken(idToken, nonce) {
|
|
223
228
|
const metadata = await getMetadata();
|
|
@@ -227,15 +232,10 @@ export function createOIDC(options) {
|
|
|
227
232
|
algorithms: metadata.id_token_signing_alg_values_supported,
|
|
228
233
|
clockSkew: clockSkewSeconds
|
|
229
234
|
});
|
|
235
|
+
validateIdTokenClaims(claims, nonce);
|
|
230
236
|
const transformedClaims = options.transformClaims
|
|
231
237
|
? await options.transformClaims(claims)
|
|
232
238
|
: claims;
|
|
233
|
-
if (transformedClaims.nonce !== nonce) {
|
|
234
|
-
throw error(401, { message: 'Invalid id_token nonce' });
|
|
235
|
-
}
|
|
236
|
-
if (!transformedClaims.sub) {
|
|
237
|
-
throw error(401, { message: 'id_token subject is required' });
|
|
238
|
-
}
|
|
239
239
|
return transformedClaims;
|
|
240
240
|
}
|
|
241
241
|
async function validateBackChannelLogoutToken(logoutToken) {
|
|
@@ -265,7 +265,7 @@ export function createOIDC(options) {
|
|
|
265
265
|
headers: {
|
|
266
266
|
authorization: `Bearer ${accessToken}`
|
|
267
267
|
}
|
|
268
|
-
});
|
|
268
|
+
}, fetchImpl);
|
|
269
269
|
}
|
|
270
270
|
async function isRevoked(session) {
|
|
271
271
|
if (!session || !backChannelLogoutStore) {
|
|
@@ -278,6 +278,10 @@ export function createOIDC(options) {
|
|
|
278
278
|
if (!session) {
|
|
279
279
|
return null;
|
|
280
280
|
}
|
|
281
|
+
if (isSessionExpired(session, sessionMaxAgeSeconds)) {
|
|
282
|
+
await clearPersistedSession(cookies, persisted?.id);
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
281
285
|
if (await isRevoked(session)) {
|
|
282
286
|
await clearPersistedSession(cookies, persisted?.id);
|
|
283
287
|
return null;
|
|
@@ -291,6 +295,9 @@ export function createOIDC(options) {
|
|
|
291
295
|
? await validateIdToken(tokenResponse.id_token, session.nonce)
|
|
292
296
|
: session.claims;
|
|
293
297
|
const rawUser = options.fetchUserInfo !== false ? await fetchUserInfo(tokenResponse.access_token) : session.user;
|
|
298
|
+
if (claims) {
|
|
299
|
+
validateUserInfoSubject(claims, rawUser);
|
|
300
|
+
}
|
|
294
301
|
const user = options.transformUser
|
|
295
302
|
? await options.transformUser(rawUser, { claims })
|
|
296
303
|
: rawUser;
|
|
@@ -385,6 +392,7 @@ export function createOIDC(options) {
|
|
|
385
392
|
const rawUser = options.fetchUserInfo === false
|
|
386
393
|
? undefined
|
|
387
394
|
: await fetchUserInfo(tokenResponse.access_token).catch(() => undefined);
|
|
395
|
+
validateUserInfoSubject(claims, rawUser);
|
|
388
396
|
const user = options.transformUser
|
|
389
397
|
? await options.transformUser(rawUser, { claims })
|
|
390
398
|
: rawUser;
|
package/dist/server/jwt.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OIDCClientAssertionOptions, OIDCClientSecretJwtOptions, OIDCPrivateKeyJwtOptions } from './types.js';
|
|
2
|
-
export declare function fetchJson<T>(url: string, init?: RequestInit): Promise<T>;
|
|
2
|
+
export declare function fetchJson<T>(url: string, init?: RequestInit, fetchImpl?: typeof fetch): Promise<T>;
|
|
3
3
|
export declare function asAuthorizationHeader(clientId: string, clientSecret: string): string;
|
|
4
4
|
export declare function createClientSecretJwtAssertion(options: OIDCClientAssertionOptions & OIDCClientSecretJwtOptions): Promise<string>;
|
|
5
5
|
export declare function createPrivateKeyJwtAssertion(options: OIDCClientAssertionOptions & OIDCPrivateKeyJwtOptions): Promise<string>;
|
package/dist/server/jwt.js
CHANGED
|
@@ -2,8 +2,8 @@ import { error } from '@sveltejs/kit';
|
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { sign } from '@sourceregistry/node-jwt/promises';
|
|
4
4
|
import { base64UrlEncode } from './utils.js';
|
|
5
|
-
export async function fetchJson(url, init) {
|
|
6
|
-
const response = await
|
|
5
|
+
export async function fetchJson(url, init, fetchImpl = fetch) {
|
|
6
|
+
const response = await fetchImpl(url, init);
|
|
7
7
|
if (!response.ok) {
|
|
8
8
|
const body = await response.text().catch(() => '');
|
|
9
9
|
throw error(response.status, {
|
package/dist/server/session.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { OIDCSession, OIDCSessionTokens, OIDCTokenResponse, OIDCUserClaims } from './types.js';
|
|
2
2
|
export declare function normalizeTokens(tokenResponse: OIDCTokenResponse, defaultScope: string[], existing?: OIDCSessionTokens, now?: number): OIDCSessionTokens;
|
|
3
3
|
export declare function shouldRefresh<TClaims extends OIDCUserClaims = OIDCUserClaims>(session: OIDCSession<TClaims>, refreshToleranceSeconds: number, now?: number): boolean;
|
|
4
|
+
export declare function isSessionExpired<TClaims extends OIDCUserClaims = OIDCUserClaims>(session: OIDCSession<TClaims>, sessionMaxAgeSeconds: number, now?: number): boolean;
|
package/dist/server/session.js
CHANGED
|
@@ -17,3 +17,13 @@ export function shouldRefresh(session, refreshToleranceSeconds, now = Math.floor
|
|
|
17
17
|
}
|
|
18
18
|
return session.tokens.expiresAt - refreshToleranceSeconds <= now;
|
|
19
19
|
}
|
|
20
|
+
export function isSessionExpired(session, sessionMaxAgeSeconds, now = Math.floor(Date.now() / 1000)) {
|
|
21
|
+
if (session.createdAt + sessionMaxAgeSeconds <= now) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
// Without a refresh token, an expired access token cannot establish that
|
|
25
|
+
// the user's session is still valid. Do not keep the local session alive.
|
|
26
|
+
return !session.tokens.refreshToken &&
|
|
27
|
+
session.tokens.expiresAt !== undefined &&
|
|
28
|
+
session.tokens.expiresAt <= now;
|
|
29
|
+
}
|
package/dist/server/store.js
CHANGED
|
@@ -17,7 +17,10 @@ export function createInMemoryBackChannelLogoutStore() {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
export function createInMemorySessionStore() {
|
|
20
|
-
|
|
20
|
+
// Persist the Map on globalThis so Vite HMR module re-evaluations don't
|
|
21
|
+
// create a fresh store and orphan all live session cookies.
|
|
22
|
+
const g = globalThis;
|
|
23
|
+
const sessions = (g['__oidc_sessions__'] ??= new Map());
|
|
21
24
|
return {
|
|
22
25
|
async get(sessionId) {
|
|
23
26
|
return sessions.get(sessionId) ?? null;
|
package/dist/server/types.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ export type OIDCUserClaims = Record<string, unknown> & {
|
|
|
53
53
|
iss?: string;
|
|
54
54
|
aud?: string | string[];
|
|
55
55
|
exp?: number;
|
|
56
|
+
iat?: number;
|
|
56
57
|
nbf?: number;
|
|
57
58
|
nonce?: string;
|
|
58
59
|
};
|
|
@@ -159,6 +160,8 @@ export type OIDCOptions<TClaims extends OIDCUserClaims = OIDCUserClaims, TSessio
|
|
|
159
160
|
cookieOptions?: Partial<CookieOptions>;
|
|
160
161
|
clockSkewSeconds?: number;
|
|
161
162
|
refreshToleranceSeconds?: number;
|
|
163
|
+
/** Maximum lifetime of a local browser session. Defaults to 8 hours. */
|
|
164
|
+
sessionMaxAgeSeconds?: number;
|
|
162
165
|
defaultLoginRedirect?: string;
|
|
163
166
|
defaultLogoutRedirect?: string;
|
|
164
167
|
sessionStore?: OIDCSessionStore<TClaims, TSession> | 'memory';
|
|
@@ -176,6 +179,13 @@ export type OIDCOptions<TClaims extends OIDCUserClaims = OIDCUserClaims, TSessio
|
|
|
176
179
|
}) => MaybePromise<TSession>;
|
|
177
180
|
endpoints?: Partial<OIDCDiscoveryDocument>;
|
|
178
181
|
logger?: OIDCLogger | false;
|
|
182
|
+
/**
|
|
183
|
+
* Custom fetch implementation used for all OIDC network calls
|
|
184
|
+
* (discovery, token, userinfo, JWKS). Useful in dev to work around
|
|
185
|
+
* self-signed certs via a custom https.Agent — do not disable
|
|
186
|
+
* TLS verification in production.
|
|
187
|
+
*/
|
|
188
|
+
fetch?: typeof fetch;
|
|
179
189
|
};
|
|
180
190
|
export type OIDCLoginOptions = {
|
|
181
191
|
returnTo?: string;
|
package/dist/server/utils.d.ts
CHANGED
|
@@ -22,4 +22,6 @@ export declare function absoluteUrl(event: {
|
|
|
22
22
|
export declare function internalRedirectPath(event: {
|
|
23
23
|
url: URL;
|
|
24
24
|
}, pathOrUrl: string | undefined, fallback?: string): string;
|
|
25
|
+
export declare function validateIdTokenClaims(claims: OIDCUserClaims, nonce: string): void;
|
|
26
|
+
export declare function validateUserInfoSubject(claims: OIDCUserClaims, user: OIDCUserClaims | undefined): void;
|
|
25
27
|
export declare function toPublicSession<TClaims extends OIDCUserClaims = OIDCUserClaims>(session: OIDCSession<TClaims> | null): OIDCPublicSession<TClaims> | null;
|
package/dist/server/utils.js
CHANGED
|
@@ -119,6 +119,25 @@ export function internalRedirectPath(event, pathOrUrl, fallback = '/') {
|
|
|
119
119
|
return fallback;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
export function validateIdTokenClaims(claims, nonce) {
|
|
123
|
+
if (!Number.isFinite(claims.exp)) {
|
|
124
|
+
throw error(401, { message: 'id_token expiration is required' });
|
|
125
|
+
}
|
|
126
|
+
if (!Number.isFinite(claims.iat)) {
|
|
127
|
+
throw error(401, { message: 'id_token issued-at time is required' });
|
|
128
|
+
}
|
|
129
|
+
if (claims.nonce !== nonce) {
|
|
130
|
+
throw error(401, { message: 'Invalid id_token nonce' });
|
|
131
|
+
}
|
|
132
|
+
if (!claims.sub) {
|
|
133
|
+
throw error(401, { message: 'id_token subject is required' });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export function validateUserInfoSubject(claims, user) {
|
|
137
|
+
if (user && user.sub !== claims.sub) {
|
|
138
|
+
throw error(401, { message: 'UserInfo subject does not match id_token subject' });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
122
141
|
export function toPublicSession(session) {
|
|
123
142
|
if (!session)
|
|
124
143
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sourceregistry/sveltekit-oidc",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.10",
|
|
4
4
|
"description": "OIDC authentication helpers for SvelteKit applications",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"scripts": {
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"oauth"
|
|
85
85
|
],
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@sourceregistry/node-jwt": "^1.5.
|
|
87
|
+
"@sourceregistry/node-jwt": "^1.5.11"
|
|
88
88
|
},
|
|
89
89
|
"release": {
|
|
90
90
|
"branches": [
|