@svadmin/sso 0.11.0 → 0.11.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.
@@ -8,6 +8,35 @@ import { createAuthenticatedFetch as buildAuthenticatedFetch } from './authentic
8
8
  import { createSSOAuthNetworkError, createSSOAuthResponseError, isTerminalSessionError, SSOAuthError, } from './errors.js';
9
9
  import { createSessionManager, } from './session-manager.js';
10
10
  const DEFAULT_STORAGE_KEY = 'svadmin_sso';
11
+ const REQUIRED_OIDC_ENDPOINTS = [
12
+ 'authorization_endpoint',
13
+ 'token_endpoint',
14
+ 'userinfo_endpoint',
15
+ ];
16
+ function readOIDCConfig(body) {
17
+ const discovery = typeof body === 'object' && body !== null && !Array.isArray(body)
18
+ ? body
19
+ : {};
20
+ const missingEndpoints = REQUIRED_OIDC_ENDPOINTS.filter((endpoint) => {
21
+ const value = discovery[endpoint];
22
+ return typeof value !== 'string' || value.length === 0;
23
+ });
24
+ if (missingEndpoints.length > 0) {
25
+ throw new SSOAuthError(`OIDC discovery returned incomplete endpoints: missing ${missingEndpoints.join(', ')}`, 502, {
26
+ code: 'invalid_discovery_document',
27
+ body,
28
+ });
29
+ }
30
+ return {
31
+ authorization_endpoint: discovery.authorization_endpoint,
32
+ token_endpoint: discovery.token_endpoint,
33
+ userinfo_endpoint: discovery.userinfo_endpoint,
34
+ ...(typeof discovery.end_session_endpoint === 'string'
35
+ && discovery.end_session_endpoint.length > 0
36
+ ? { end_session_endpoint: discovery.end_session_endpoint }
37
+ : {}),
38
+ };
39
+ }
11
40
  function decodeJwtPayload(token) {
12
41
  if (!token)
13
42
  return null;
@@ -205,15 +234,7 @@ export function createSSOAuthProvider(config) {
205
234
  cause: error,
206
235
  });
207
236
  }
208
- if (typeof discovered.authorization_endpoint !== 'string'
209
- || typeof discovered.token_endpoint !== 'string'
210
- || typeof discovered.userinfo_endpoint !== 'string') {
211
- throw new SSOAuthError('OIDC discovery returned incomplete endpoints', 502, {
212
- code: 'invalid_discovery_document',
213
- body: discovered,
214
- });
215
- }
216
- oidcConfig = discovered;
237
+ oidcConfig = readOIDCConfig(discovered);
217
238
  return oidcConfig;
218
239
  }
219
240
  async function requestToken(endpoint, body, fallbackMessage) {
@@ -407,7 +428,16 @@ export function createSSOAuthProvider(config) {
407
428
  };
408
429
  },
409
430
  async check() {
410
- const existingSession = sessions.getSession();
431
+ let existingSession = sessions.getSession();
432
+ if (!existingSession) {
433
+ try {
434
+ await sessions.clearInvalidSession();
435
+ existingSession = sessions.getSession();
436
+ }
437
+ catch (error) {
438
+ return { authenticated: false, error: getErrorResult(error) };
439
+ }
440
+ }
411
441
  if (typeof window === 'undefined') {
412
442
  const now = Math.floor(Date.now() / 1000);
413
443
  return existingSession && (existingSession.expires_at === undefined || existingSession.expires_at > now)
@@ -31,6 +31,7 @@ export interface TokenStorageLike {
31
31
  }
32
32
  export interface SessionManager {
33
33
  getSession: () => SSOSession | null;
34
+ clearInvalidSession: () => Promise<void>;
34
35
  getAuthGeneration: () => number;
35
36
  saveSession: (session: SSOSession, event?: AuthStateChangeEvent) => void;
36
37
  clearSession: () => void;
@@ -306,6 +306,17 @@ export function createSessionManager(options) {
306
306
  });
307
307
  }
308
308
  }
309
+ function clearInvalidSession() {
310
+ const initialSnapshot = getSessionSnapshot();
311
+ if (initialSnapshot.raw === null || initialSnapshot.session)
312
+ return Promise.resolve();
313
+ return runAuthMutation(async () => {
314
+ const currentRaw = locallySignedOut ? null : options.storage.getItem(keys.tokens);
315
+ if (currentRaw === null || parseSession(currentRaw))
316
+ return;
317
+ clearTokenSession();
318
+ });
319
+ }
309
320
  function resolveRefreshRace(expectedRaw) {
310
321
  const currentRaw = locallySignedOut ? null : options.storage.getItem(keys.tokens);
311
322
  if (currentRaw === expectedRaw)
@@ -432,8 +443,11 @@ export function createSessionManager(options) {
432
443
  }
433
444
  async function getAccessToken(accessOptions = {}) {
434
445
  const snapshot = getSessionSnapshot();
435
- if (!snapshot.raw || !snapshot.session)
446
+ if (!snapshot.raw || !snapshot.session) {
447
+ if (snapshot.raw)
448
+ await clearInvalidSession();
436
449
  return null;
450
+ }
437
451
  const { raw: initialRaw, session } = snapshot;
438
452
  const minValiditySeconds = Math.max(0, accessOptions.minValiditySeconds ?? 0);
439
453
  const now = Math.floor(Date.now() / 1000);
@@ -532,6 +546,7 @@ export function createSessionManager(options) {
532
546
  scheduleRefresh(parseSession(lastObservedRaw));
533
547
  return {
534
548
  getSession,
549
+ clearInvalidSession,
535
550
  getAuthGeneration: () => authGeneration,
536
551
  saveSession,
537
552
  clearSession,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/sso",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "type": "module",
5
5
  "description": "OIDC/OAuth2 SSO AuthProvider plugin for svadmin — supports Okta, Azure AD, Cognito, Keycloak, Google",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  "dist"
33
33
  ],
34
34
  "peerDependencies": {
35
- "@svadmin/core": "^0.32.0"
35
+ "@svadmin/core": "^0.32.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "typescript": "^5.7.0"