diff --git a/decisions.md b/decisions.md index 076e47c..ce8d0a7 100644 --- a/decisions.md +++ b/decisions.md @@ -315,3 +315,16 @@ Architectural and design decisions for the oidc-js project. Each entry captures **Decision**: Option 3. `trackTraffic(page)` now returns `{ requests(), navigations(), sequence() }`. The sequence log records fetch requests as `GET /path` or `POST /path` and navigations as `NAV /path`, in the order Playwright observes them. All three are asserted in tests that check traffic. **Rationale**: The combined sequence is the source of truth for protocol ordering. The separate `requests()` and `navigations()` remain as convenience filters for tests that only need to check "which fetches happened" without reasoning about navigation interleaving. The assumed race condition from decision 023 turned out to be incorrect. Playwright's `page.on("request")` fires deterministically for both fetch and document requests, and the combined log is stable across runs. Named constants (`GET_WELLKNOWN`, `POST_TOKEN`, `GET_USERINFO`, `NAV_AUTHORIZE`, `NAV_LOGOUT`) make the sequence assertions read like a protocol spec. + +### 027 - Proactive token refresh via setInterval polling (2026-05-08) + +**Context**: Token expiration was only detected reactively — by `RequireAuth` at render time or by HTTP interceptors catching 401s. This meant consumers using plain `fetch` had to manually check for 401 on every request, and `RequireAuth` briefly unmounted children when it detected expiration (losing form state). + +**Alternatives considered**: +1. Single `setTimeout` scheduled for `expiresAt - buffer` — simple but unreliable (browsers throttle background tabs, laptops sleep, timer fires late or not at all) +2. `setInterval` polling with a short interval (e.g. 10s) that checks `Date.now() >= expiresAt - expiryBuffer` each tick +3. Service Worker or `BroadcastChannel` based approach — complex, not universally supported + +**Decision**: Option 2. A `setInterval` in `OidcClient` polls every `autoRefreshInterval` seconds (default 10) and triggers `refresh()` when `isExpiredAt(expiresAt, expiryBuffer)` returns true. Enabled by default (`autoRefresh: true`). On refresh failure, the interval stops (no endpoint hammering); a successful manual refresh or new login restarts it. + +**Rationale**: This is the same approach `oidc-client-ts` uses (their `Timer` class polls every 5s). A short interval is drift-proof (always compares real time), sleep-proof (first tick after wake catches expiration), and cheap (one number comparison per tick). The implementation lives in `OidcClient`, so all framework adapters benefit via the existing subscribe/notify mechanism. The `expiryBuffer` config (already existed) controls how early the refresh fires — the token is still valid during the refresh request, so `RequireAuth` never sees an expired token and children stay mounted. diff --git a/docs-web/src/content/docs/angular/provide-auth.mdx b/docs-web/src/content/docs/angular/provide-auth.mdx index ed2408f..1983f35 100644 --- a/docs-web/src/content/docs/angular/provide-auth.mdx +++ b/docs-web/src/content/docs/angular/provide-auth.mdx @@ -38,7 +38,6 @@ bootstrapApplication(AppComponent, { | Option | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | - | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | - | Called when an error occurs during initialization | diff --git a/docs-web/src/content/docs/guides/token-refresh.mdx b/docs-web/src/content/docs/guides/token-refresh.mdx index 6edfbd2..b3b844a 100644 --- a/docs-web/src/content/docs/guides/token-refresh.mdx +++ b/docs-web/src/content/docs/guides/token-refresh.mdx @@ -25,9 +25,42 @@ sequenceDiagram OidcClient->>App: Render protected content ``` -## Automatic refresh with RequireAuth +## Proactive refresh -`RequireAuth` handles refresh automatically. When a user navigates to a protected route with an expired token: +By default, `OidcClient` proactively refreshes the access token *before* it expires using a short `setInterval` poll. Every tick compares the current time against `expiresAt - expiryBuffer` and triggers a refresh when the token is about to expire. This means the token is always fresh — no 401s, no interceptors needed, no fallback flash. + +This is enabled by default. You can configure the behavior with two options: + +```ts +const config = { + issuer: "https://auth.example.com", + clientId: "my-app", + redirectUri: "http://localhost:5173/callback", + expiryBuffer: 60, // refresh 60s before expiry (default: 30) + autoRefresh: true, // enabled by default + autoRefreshInterval: 10, // polling interval in seconds (default: 10) +}; +``` + +The polling interval is lightweight — one number comparison per tick, no re-renders or network calls until a refresh is actually needed. The approach is drift-proof and sleep-proof: after a laptop wakes from sleep, the very first tick catches the expiration. + + + +### Disabling proactive refresh + +Set `autoRefresh: false` to rely on reactive mechanisms instead (interceptors, `RequireAuth` re-mount): + +```tsx + + + +``` + +## Reactive refresh with RequireAuth + +`RequireAuth` handles refresh reactively. When a user navigates to a protected route with an expired token: 1. Checks `tokens.expiresAt` using `isExpiredAt()` from core (with an optional buffer) 2. If expired, calls `actions.refresh()` @@ -48,6 +81,10 @@ You can set an `expiryBuffer` (in seconds) on the provider config to refresh the ``` + + ## Manual refresh You can also trigger a refresh manually: diff --git a/docs-web/src/content/docs/guides/user-profile.mdx b/docs-web/src/content/docs/guides/user-profile.mdx index f3a60eb..0057574 100644 --- a/docs-web/src/content/docs/guides/user-profile.mdx +++ b/docs-web/src/content/docs/guides/user-profile.mdx @@ -35,7 +35,7 @@ user?.profile?.preferred_username; By default, `AuthProvider` fetches the user profile after login. You can disable this: ```tsx - + ``` diff --git a/docs-web/src/content/docs/lit/auth-controller.mdx b/docs-web/src/content/docs/lit/auth-controller.mdx index 5e44bbf..76a7b2b 100644 --- a/docs-web/src/content/docs/lit/auth-controller.mdx +++ b/docs-web/src/content/docs/lit/auth-controller.mdx @@ -46,7 +46,6 @@ customElements.define("my-app", MyApp); | Option | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | - | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | - | Called when an error occurs during initialization | diff --git a/docs-web/src/content/docs/preact/auth-provider.mdx b/docs-web/src/content/docs/preact/auth-provider.mdx index e1b1f47..3ec4521 100644 --- a/docs-web/src/content/docs/preact/auth-provider.mdx +++ b/docs-web/src/content/docs/preact/auth-provider.mdx @@ -38,7 +38,6 @@ function App() { | Prop | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | - | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | - | Called when an error occurs during initialization | | `children` | `ComponentChildren` | **required** | Child components | diff --git a/docs-web/src/content/docs/react/auth-provider.mdx b/docs-web/src/content/docs/react/auth-provider.mdx index 42f8046..4ab008f 100644 --- a/docs-web/src/content/docs/react/auth-provider.mdx +++ b/docs-web/src/content/docs/react/auth-provider.mdx @@ -34,7 +34,6 @@ function App() { | Prop | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | — | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | — | Called when an error occurs during initialization | | `children` | `ReactNode` | **required** | Child components | diff --git a/docs-web/src/content/docs/react/use-auth.mdx b/docs-web/src/content/docs/react/use-auth.mdx index 407d814..a41b849 100644 --- a/docs-web/src/content/docs/react/use-auth.mdx +++ b/docs-web/src/content/docs/react/use-auth.mdx @@ -49,7 +49,7 @@ interface AuthUser { } ``` -`null` when not authenticated. After login, `claims` is always populated from the ID token. `profile` is populated from the UserInfo endpoint if `fetchProfile` is `true` (default). +`null` when not authenticated. After login, `claims` is always populated from the ID token. `profile` is populated from the UserInfo endpoint if `config.fetchProfile` is `true` (default). ### tokens diff --git a/docs-web/src/content/docs/solid/auth-provider.mdx b/docs-web/src/content/docs/solid/auth-provider.mdx index 41ecd24..057273f 100644 --- a/docs-web/src/content/docs/solid/auth-provider.mdx +++ b/docs-web/src/content/docs/solid/auth-provider.mdx @@ -38,7 +38,6 @@ function App() { | Prop | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | - | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | - | Called when an error occurs during initialization | | `children` | `JSX.Element` | **required** | Child components | diff --git a/docs-web/src/content/docs/svelte/auth-provider.mdx b/docs-web/src/content/docs/svelte/auth-provider.mdx index 20462f8..d5577aa 100644 --- a/docs-web/src/content/docs/svelte/auth-provider.mdx +++ b/docs-web/src/content/docs/svelte/auth-provider.mdx @@ -36,7 +36,6 @@ npm install oidc-js-svelte | Prop | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | - | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | - | Called when an error occurs during initialization | | `children` | `Snippet` | **required** | Child content to render | diff --git a/docs-web/src/content/docs/vue/plugin.mdx b/docs-web/src/content/docs/vue/plugin.mdx index d3165af..77c99ef 100644 --- a/docs-web/src/content/docs/vue/plugin.mdx +++ b/docs-web/src/content/docs/vue/plugin.mdx @@ -38,7 +38,6 @@ app.mount("#app"); | Option | Type | Default | Description | |---|---|---|---| | `config` | `OidcConfig` | **required** | OIDC configuration (issuer, clientId, redirectUri, etc.) | -| `fetchProfile` | `boolean` | `true` | Whether to fetch the UserInfo endpoint after login | | `onLogin` | `(returnTo: string) => void` | - | Called after successful login with the URL to restore | | `onError` | `(error: Error) => void` | - | Called when an error occurs during initialization | diff --git a/packages/angular/package.json b/packages/angular/package.json index 7222466..a4177c6 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-angular", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for Angular. Signals, DI, and route guards with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/angular/src/auth.service.ts b/packages/angular/src/auth.service.ts index 68fb0a1..3c3c195 100644 --- a/packages/angular/src/auth.service.ts +++ b/packages/angular/src/auth.service.ts @@ -63,10 +63,7 @@ export class AuthService { this.router = inject(Router); const destroyRef = inject(DestroyRef); - this.client = new OidcClient({ - ...this.options.config, - fetchProfile: this.options.fetchProfile, - }); + this.client = new OidcClient(this.options.config); const unsub = this.client.subscribe((state: AuthState) => { this._user.set(state.user); diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index e49c228..3fe4964 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -10,4 +10,5 @@ export type { LoginOptions, } from "./types.js"; +export type { OidcClientConfig } from "oidc-js"; export type { OidcConfig, OidcUser, TokenSet } from "oidc-js-core"; diff --git a/packages/angular/src/tests/auth.service.test.ts b/packages/angular/src/tests/auth.service.test.ts index a312100..891d9ef 100644 --- a/packages/angular/src/tests/auth.service.test.ts +++ b/packages/angular/src/tests/auth.service.test.ts @@ -43,8 +43,7 @@ const CONFIG = { }; const mockOptions: Record = { - config: CONFIG, - fetchProfile: true, + config: { ...CONFIG, fetchProfile: true }, }; vi.mock("@angular/core", () => { diff --git a/packages/angular/src/types.ts b/packages/angular/src/types.ts index e899934..2c8b33b 100644 --- a/packages/angular/src/types.ts +++ b/packages/angular/src/types.ts @@ -1,7 +1,7 @@ -import type { OidcConfig } from "oidc-js-core"; - export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcClientConfig } from "oidc-js"; + /** * Configuration options for {@link provideAuth}. * @@ -10,9 +10,7 @@ export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js" */ export interface AuthProviderOptions { /** Core OIDC configuration (issuer, clientId, redirectUri, scopes, etc.). */ - config: OidcConfig; - /** Whether to fetch the userinfo profile after token exchange. Defaults to `true`. */ - fetchProfile?: boolean; + config: OidcClientConfig; /** * Called after a successful login callback with the `returnTo` path. * If not provided, the adapter uses Angular's `Router.navigateByUrl` to navigate. diff --git a/packages/client/package.json b/packages/client/package.json index 89fd4fb..f8ecf2c 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for JavaScript. Drop-in client with login, logout, token refresh, and zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index b290331..242e743 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -13,6 +13,7 @@ import { parseUserinfoResponse, buildLogoutUrl, decodeJwtPayload, + isExpiredAt, DEFAULT_EXPIRY_BUFFER, type OidcDiscovery, type OidcUser, @@ -65,6 +66,7 @@ export class OidcClient { private subscribers = new Set(); private abortController: AbortController | null = null; private refreshPromise: Promise | null = null; + private autoRefreshTimer: ReturnType | null = null; private _state: AuthState = { user: null, @@ -178,6 +180,8 @@ export class OidcClient { isLoading: false, }); + this.startAutoRefresh(); + return { returnTo }; } @@ -228,6 +232,7 @@ export class OidcClient { * with the current ID token hint and `postLogoutRedirectUri` from config. */ logout(): void { + this.stopAutoRefresh(); const idToken = this._state.tokens.id; this.setState({ @@ -305,6 +310,8 @@ export class OidcClient { error: null, }); + this.startAutoRefresh(); + return newTokens; } @@ -331,10 +338,34 @@ export class OidcClient { * Tears down the client by aborting any in-flight requests and removing all subscribers. */ destroy(): void { + this.stopAutoRefresh(); this.abortController?.abort(); this.subscribers.clear(); } + private startAutoRefresh(): void { + if (this.config.autoRefresh === false) return; + if (this.autoRefreshTimer) return; + + const intervalSeconds = this.config.autoRefreshInterval ?? 10; + const buffer = this.config.expiryBuffer ?? DEFAULT_EXPIRY_BUFFER; + + this.autoRefreshTimer = setInterval(() => { + const { expiresAt } = this._state.tokens; + if (!this._state.isAuthenticated || !this._state.tokens.refresh) return; + if (!isExpiredAt(expiresAt, buffer)) return; + + this.refresh().catch(() => { this.stopAutoRefresh(); }); + }, intervalSeconds * 1000); + } + + private stopAutoRefresh(): void { + if (this.autoRefreshTimer) { + clearInterval(this.autoRefreshTimer); + this.autoRefreshTimer = null; + } + } + /** * Internal helper that calls the userinfo endpoint and parses the response. * diff --git a/packages/client/src/tests/client.test.ts b/packages/client/src/tests/client.test.ts index 18af59e..0197f55 100644 --- a/packages/client/src/tests/client.test.ts +++ b/packages/client/src/tests/client.test.ts @@ -330,4 +330,174 @@ describe("OidcClient", () => { expect(fn).not.toHaveBeenCalled(); }); }); + + describe("autoRefresh", () => { + function setupAuthenticatedClient(configOverrides?: Partial) { + Object.defineProperty(window, "location", { + value: { + href: "http://localhost:3000?code=auth_code&state=test-state", + search: "?code=auth_code&state=test-state", + pathname: "/", + hash: "", + }, + writable: true, + configurable: true, + }); + + sessionStorage.setItem( + "oidc-js:auth-state", + JSON.stringify({ + codeVerifier: "test-verifier", + state: "test-state", + nonce: "test-nonce", + redirectUri: "http://localhost:3000/callback", + }), + ); + + return new OidcClient({ ...CONFIG, fetchProfile: false, ...configOverrides }); + } + + it("calls refresh when token is near expiry", async () => { + const expiringSoonToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + const tokenResponse = { ...TOKEN_RESPONSE, access_token: expiringSoonToken }; + mockFetchResponses(DISCOVERY, tokenResponse); + + const client = setupAuthenticatedClient({ autoRefreshInterval: 0.1, expiryBuffer: 30 }); + await client.init(); + + const freshToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 7200 }); + mockFetchResponses({ + access_token: freshToken, + token_type: "Bearer", + refresh_token: "rt_new", + }); + + await vi.waitFor(() => { + expect(client.state.tokens.access).toBe(freshToken); + }, { timeout: 1000 }); + + client.destroy(); + }); + + it("does not refresh when token is not near expiry", async () => { + mockFetchResponses(DISCOVERY, TOKEN_RESPONSE); + + const client = setupAuthenticatedClient({ autoRefreshInterval: 0.1 }); + await client.init(); + + const callCountAfterInit = fetchMock.mock.calls.length; + + await new Promise((r) => setTimeout(r, 300)); + + expect(fetchMock.mock.calls.length).toBe(callCountAfterInit); + + client.destroy(); + }); + + it("does not start when autoRefresh is false", async () => { + const expiringSoonToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + const tokenResponse = { ...TOKEN_RESPONSE, access_token: expiringSoonToken }; + mockFetchResponses(DISCOVERY, tokenResponse); + + const client = setupAuthenticatedClient({ autoRefresh: false, autoRefreshInterval: 0.1, expiryBuffer: 30 }); + await client.init(); + + const callCountAfterInit = fetchMock.mock.calls.length; + + await new Promise((r) => setTimeout(r, 300)); + + expect(fetchMock.mock.calls.length).toBe(callCountAfterInit); + + client.destroy(); + }); + + it("stops on logout", async () => { + const expiringSoonToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + const tokenResponse = { ...TOKEN_RESPONSE, access_token: expiringSoonToken }; + mockFetchResponses(DISCOVERY, tokenResponse); + + const client = setupAuthenticatedClient({ autoRefreshInterval: 0.1, expiryBuffer: 30 }); + await client.init(); + + client.logout(); + + const callCountAfterLogout = fetchMock.mock.calls.length; + + await new Promise((r) => setTimeout(r, 300)); + + expect(fetchMock.mock.calls.length).toBe(callCountAfterLogout); + }); + + it("stops polling after a failed refresh", async () => { + const expiringSoonToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + const tokenResponse = { ...TOKEN_RESPONSE, access_token: expiringSoonToken }; + mockFetchResponses(DISCOVERY, tokenResponse); + + const client = setupAuthenticatedClient({ autoRefreshInterval: 0.1, expiryBuffer: 30 }); + await client.init(); + + fetchMock.mockRejectedValueOnce(new Error("network error")); + + await vi.waitFor(() => { + expect(fetchMock.mock.calls.length).toBeGreaterThan(2); + }, { timeout: 1000 }); + + const callCountAfterFailure = fetchMock.mock.calls.length; + + await new Promise((r) => setTimeout(r, 300)); + + expect(fetchMock.mock.calls.length).toBe(callCountAfterFailure); + expect(client.state.isAuthenticated).toBe(true); + + client.destroy(); + }); + + it("restarts after a failed auto-refresh followed by a manual refresh", async () => { + const expiringSoonToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + const tokenResponse = { ...TOKEN_RESPONSE, access_token: expiringSoonToken }; + mockFetchResponses(DISCOVERY, tokenResponse); + + const client = setupAuthenticatedClient({ autoRefreshInterval: 0.1, expiryBuffer: 30 }); + await client.init(); + + // Auto-refresh fires and fails → interval stops + fetchMock.mockRejectedValueOnce(new Error("network error")); + + await vi.waitFor(() => { + expect(fetchMock.mock.calls.length).toBeGreaterThan(2); + }, { timeout: 1000 }); + + // Manual refresh succeeds → interval restarts + const freshToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + mockFetchResponses({ access_token: freshToken, token_type: "Bearer", refresh_token: "rt_new" }); + await client.refresh(); + + // Auto-refresh should fire again with the new (still near-expiry) token + const secondFreshToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 7200 }); + mockFetchResponses({ access_token: secondFreshToken, token_type: "Bearer", refresh_token: "rt_new2" }); + + await vi.waitFor(() => { + expect(client.state.tokens.access).toBe(secondFreshToken); + }, { timeout: 1000 }); + + client.destroy(); + }); + + it("stops on destroy", async () => { + const expiringSoonToken = makeJwt({ sub: "user-1", exp: nowSeconds() + 10 }); + const tokenResponse = { ...TOKEN_RESPONSE, access_token: expiringSoonToken }; + mockFetchResponses(DISCOVERY, tokenResponse); + + const client = setupAuthenticatedClient({ autoRefreshInterval: 0.1, expiryBuffer: 30 }); + await client.init(); + + client.destroy(); + + const callCountAfterDestroy = fetchMock.mock.calls.length; + + await new Promise((r) => setTimeout(r, 300)); + + expect(fetchMock.mock.calls.length).toBe(callCountAfterDestroy); + }); + }); }); diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index a62ad0f..990046b 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -60,6 +60,10 @@ export interface LoginOptions { export interface OidcClientConfig extends OidcConfig { /** Whether to fetch the userinfo profile after token exchange. Defaults to true. */ fetchProfile?: boolean; + /** Whether to proactively refresh the access token before it expires. Defaults to true. */ + autoRefresh?: boolean; + /** Polling interval in seconds for the auto-refresh check. Defaults to 10. */ + autoRefreshInterval?: number; } /** diff --git a/packages/kasper/package.json b/packages/kasper/package.json index 3d03a95..113c801 100644 --- a/packages/kasper/package.json +++ b/packages/kasper/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-kasper", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for Kasper.js. Signals, components, and auth guards with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/kasper/src/auth-provider.ts b/packages/kasper/src/auth-provider.ts index 8dd55b4..3d696d9 100644 --- a/packages/kasper/src/auth-provider.ts +++ b/packages/kasper/src/auth-provider.ts @@ -1,11 +1,10 @@ import { Component } from "kasper-js"; -import type { OidcConfig } from "oidc-js-core"; +import type { OidcClientConfig } from "oidc-js"; import type { OidcClient } from "oidc-js"; import { _initAuth, _destroyAuth } from "./context.js"; interface AuthProviderArgs { - config: OidcConfig; - fetchProfile?: boolean; + config: OidcClientConfig; onLogin?: (returnTo: string) => void; onError?: (error: Error) => void; } @@ -29,11 +28,10 @@ export class AuthProvider extends Component { onMount(): void { const config = this.args.config; - const fetchProfile = this.args.fetchProfile ?? true; const onLogin = this.args.onLogin; const onError = this.args.onError; - const { client, unsub } = _initAuth(config, fetchProfile); + const { client, unsub } = _initAuth(config); this._client = client; this._unsub = unsub; diff --git a/packages/kasper/src/context.ts b/packages/kasper/src/context.ts index ae4033d..951158b 100644 --- a/packages/kasper/src/context.ts +++ b/packages/kasper/src/context.ts @@ -1,6 +1,5 @@ import { signal, batch } from "kasper-js"; -import { OidcClient, type AuthState, type LoginOptions } from "oidc-js"; -import type { OidcConfig } from "oidc-js-core"; +import { OidcClient, type OidcClientConfig, type AuthState, type LoginOptions } from "oidc-js"; import type { AuthContextValue, AuthActions } from "./types.js"; const _user = signal(null); @@ -15,16 +14,15 @@ const _tokens = signal({ }); let _client: OidcClient | null = null; -let _config: OidcConfig | null = null; +let _config: OidcClientConfig | null = null; let _actions: AuthActions | null = null; let _unsub: (() => void) | null = null; /** @internal */ export function _initAuth( - config: OidcConfig, - fetchProfile: boolean, + config: OidcClientConfig, ): { client: OidcClient; unsub: () => void } { - const client = new OidcClient({ ...config, fetchProfile }); + const client = new OidcClient(config); _client = client; _config = config; _actions = { diff --git a/packages/kasper/src/index.ts b/packages/kasper/src/index.ts index 06bb566..7747cc2 100644 --- a/packages/kasper/src/index.ts +++ b/packages/kasper/src/index.ts @@ -10,4 +10,5 @@ export type { Signal, LoginOptions, } from "./types.js"; +export type { OidcClientConfig } from "oidc-js"; export type { OidcConfig, OidcUser, TokenSet } from "oidc-js-core"; diff --git a/packages/kasper/src/tests/auth-provider.test.ts b/packages/kasper/src/tests/auth-provider.test.ts index 0a4fb7d..835b9a8 100644 --- a/packages/kasper/src/tests/auth-provider.test.ts +++ b/packages/kasper/src/tests/auth-provider.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import type { OidcConfig } from "oidc-js-core"; +import type { OidcClientConfig } from "oidc-js"; const mockClientInstance = { subscribe: vi.fn((_cb: (state: unknown) => void) => vi.fn()), @@ -27,7 +27,7 @@ vi.mock("oidc-js", () => ({ import { AuthProvider } from "../auth-provider.js"; import { _destroyAuth, useAuth } from "../context.js"; -const CONFIG: OidcConfig = { +const CONFIG: OidcClientConfig = { issuer: "https://auth.example.com", clientId: "my-app", redirectUri: "http://localhost:3000/callback", @@ -45,8 +45,7 @@ afterEach(() => { }); interface AuthProviderArgs { - config: OidcConfig; - fetchProfile?: boolean; + config: OidcClientConfig; onLogin?: (returnTo: string) => void; onError?: (error: Error) => void; } @@ -71,14 +70,14 @@ describe("AuthProvider", () => { expect(mockClientInstance.init).toHaveBeenCalled(); }); - it("defaults fetchProfile to true", async () => { + it("passes config directly to OidcClient", async () => { const { OidcClient } = await vi.importMock("oidc-js"); const provider = createProvider({ config: CONFIG }); provider.onMount(); - expect(OidcClient).toHaveBeenCalledWith({ ...CONFIG, fetchProfile: true }); + expect(OidcClient).toHaveBeenCalledWith(CONFIG); }); it("passes fetchProfile=false when configured", async () => { @@ -86,8 +85,7 @@ describe("AuthProvider", () => { await vi.importMock("oidc-js"); const provider = createProvider({ - config: CONFIG, - fetchProfile: false, + config: { ...CONFIG, fetchProfile: false }, }); provider.onMount(); diff --git a/packages/kasper/src/tests/context.test.ts b/packages/kasper/src/tests/context.test.ts index b676ee0..5bcdf3b 100644 --- a/packages/kasper/src/tests/context.test.ts +++ b/packages/kasper/src/tests/context.test.ts @@ -52,7 +52,7 @@ describe("useAuth", () => { }); it("returns auth context after _initAuth", () => { - _initAuth(CONFIG, true); + _initAuth(CONFIG); const auth = useAuth(); expect(auth.config).toEqual(CONFIG); @@ -69,7 +69,7 @@ describe("useAuth", () => { }); it("exposes actions that delegate to OidcClient", () => { - _initAuth(CONFIG, true); + _initAuth(CONFIG); const auth = useAuth(); auth.actions.login(); @@ -87,25 +87,25 @@ describe("useAuth", () => { }); describe("_initAuth", () => { - it("creates OidcClient with config and fetchProfile", async () => { + it("creates OidcClient with config", async () => { const { OidcClient } = await vi.importMock( "oidc-js", ); - _initAuth(CONFIG, true); + _initAuth(CONFIG); - expect(OidcClient).toHaveBeenCalledWith({ ...CONFIG, fetchProfile: true }); + expect(OidcClient).toHaveBeenCalledWith(CONFIG); }); it("returns client and unsub function", () => { - const result = _initAuth(CONFIG, false); + const result = _initAuth(CONFIG); expect(result.client).toBe(mockClientInstance); expect(typeof result.unsub).toBe("function"); }); it("subscribes to client state changes", () => { - _initAuth(CONFIG, true); + _initAuth(CONFIG); expect(mockClientInstance.subscribe).toHaveBeenCalledWith( expect.any(Function), @@ -113,7 +113,7 @@ describe("_initAuth", () => { }); it("updates signals when subscriber fires", () => { - _initAuth(CONFIG, true); + _initAuth(CONFIG); const subscriber = mockClientInstance.subscribe.mock.calls[0][0]; subscriber({ @@ -134,7 +134,7 @@ describe("_initAuth", () => { describe("_destroyAuth", () => { it("resets all signals to initial values", () => { - _initAuth(CONFIG, true); + _initAuth(CONFIG); const subscriber = mockClientInstance.subscribe.mock.calls[0][0]; subscriber({ @@ -158,7 +158,7 @@ describe("logout unsubscribes before calling client", () => { const unsubFn = vi.fn(); mockClientInstance.subscribe.mockReturnValueOnce(unsubFn); - _initAuth(CONFIG, true); + _initAuth(CONFIG); const auth = useAuth(); auth.actions.logout(); diff --git a/packages/kasper/src/types.ts b/packages/kasper/src/types.ts index 49cebd4..a1a2e24 100644 --- a/packages/kasper/src/types.ts +++ b/packages/kasper/src/types.ts @@ -1,6 +1,5 @@ import type { Signal } from "kasper-js"; -import type { OidcConfig } from "oidc-js-core"; -import type { AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcClientConfig, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; import type { OidcUser } from "oidc-js-core"; export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; @@ -21,7 +20,7 @@ export interface AuthActions { /** Value returned by {@link useAuth}. All state properties are Kasper signals. */ export interface AuthContextValue { /** The OIDC configuration used to initialize the provider. */ - readonly config: OidcConfig; + readonly config: OidcClientConfig; /** The authenticated user, or null if not authenticated. */ readonly user: Signal; /** Whether the user is currently authenticated. */ diff --git a/packages/lit/package.json b/packages/lit/package.json index 32c8f9d..02241bb 100644 --- a/packages/lit/package.json +++ b/packages/lit/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-lit", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for Lit. Reactive controllers for login, logout, and token refresh with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/lit/src/auth-controller.ts b/packages/lit/src/auth-controller.ts index b057224..96728f1 100644 --- a/packages/lit/src/auth-controller.ts +++ b/packages/lit/src/auth-controller.ts @@ -1,6 +1,5 @@ import type { ReactiveController, ReactiveControllerHost } from "lit"; -import { OidcClient, type AuthState, type AuthUser, type AuthTokens, type LoginOptions } from "oidc-js"; -import type { OidcConfig } from "oidc-js-core"; +import { OidcClient, type OidcClientConfig, type AuthState, type AuthUser, type AuthTokens, type LoginOptions } from "oidc-js"; import type { AuthControllerOptions } from "./types.js"; /** @@ -85,7 +84,7 @@ export class AuthController implements ReactiveController { } /** The OIDC configuration passed to this controller. */ - get config(): OidcConfig { + get config(): OidcClientConfig { return this.options.config; } @@ -94,8 +93,8 @@ export class AuthController implements ReactiveController { * Creates the {@link OidcClient}, subscribes to state changes, and initializes the OIDC flow. */ hostConnected(): void { - const { config, fetchProfile, onLogin, onError } = this.options; - const client = new OidcClient({ ...config, fetchProfile }); + const { config, onLogin, onError } = this.options; + const client = new OidcClient(config); this.client = client; this.unsubscribe = client.subscribe((state: AuthState) => { diff --git a/packages/lit/src/index.ts b/packages/lit/src/index.ts index 2f16b62..932602d 100644 --- a/packages/lit/src/index.ts +++ b/packages/lit/src/index.ts @@ -12,4 +12,5 @@ export type { export type { RequireAuthOptions } from "./require-auth.js"; +export type { OidcClientConfig } from "oidc-js"; export type { OidcConfig, OidcUser, TokenSet } from "oidc-js-core"; diff --git a/packages/lit/src/tests/auth-controller.test.ts b/packages/lit/src/tests/auth-controller.test.ts index 1179f7e..7ba2755 100644 --- a/packages/lit/src/tests/auth-controller.test.ts +++ b/packages/lit/src/tests/auth-controller.test.ts @@ -89,7 +89,7 @@ describe("AuthController", () => { const { OidcClient } = await vi.importMock("oidc-js"); const host = createMockHost(); - const ctrl = new AuthController(host, { config: CONFIG, fetchProfile: false }); + const ctrl = new AuthController(host, { config: { ...CONFIG, fetchProfile: false } }); ctrl.hostConnected(); expect(OidcClient).toHaveBeenCalledWith({ ...CONFIG, fetchProfile: false }); diff --git a/packages/lit/src/types.ts b/packages/lit/src/types.ts index 4619d7a..49cacf1 100644 --- a/packages/lit/src/types.ts +++ b/packages/lit/src/types.ts @@ -1,8 +1,6 @@ -import type { OidcConfig } from "oidc-js-core"; - export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; -import type { AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcClientConfig, AuthTokens, LoginOptions } from "oidc-js"; import type { OidcUser } from "oidc-js-core"; /** @@ -24,9 +22,7 @@ export interface AuthActions { */ export interface AuthControllerOptions { /** OIDC configuration including issuer, clientId, and redirectUri. */ - config: OidcConfig; - /** Whether to fetch the userinfo profile after token exchange. Defaults to true. */ - fetchProfile?: boolean; + config: OidcClientConfig; /** * Called after a successful login callback with the `returnTo` path. * If not provided, the controller calls `window.history.replaceState` to update the URL. diff --git a/packages/preact/package.json b/packages/preact/package.json index 86a748e..1580cba 100644 --- a/packages/preact/package.json +++ b/packages/preact/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-preact", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for Preact. Hooks, provider, and auth guards with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/preact/src/context.tsx b/packages/preact/src/context.tsx index f75d63a..26801c6 100644 --- a/packages/preact/src/context.tsx +++ b/packages/preact/src/context.tsx @@ -8,15 +8,13 @@ import { useMemo, } from "preact/hooks"; import type { ComponentChildren } from "preact"; -import { OidcClient, type AuthState, type LoginOptions } from "oidc-js"; -import type { OidcConfig } from "oidc-js-core"; +import { OidcClient, type OidcClientConfig, type AuthState, type LoginOptions } from "oidc-js"; import type { AuthContextValue } from "./types.js"; const AuthContext = createContext(null); interface AuthProviderProps { - config: OidcConfig; - fetchProfile?: boolean; + config: OidcClientConfig; onLogin?: (returnTo: string) => void; onError?: (error: Error) => void; children: ComponentChildren; @@ -32,7 +30,6 @@ interface AuthProviderProps { */ export function AuthProvider({ config, - fetchProfile = true, onLogin, onError, children, @@ -53,7 +50,7 @@ export function AuthProvider({ onErrorRef.current = onError; useEffect(() => { - const client = new OidcClient({ ...config, fetchProfile }); + const client = new OidcClient(config); clientRef.current = client; const unsub = client.subscribe(setState); @@ -74,7 +71,7 @@ export function AuthProvider({ unsub(); client.destroy(); }; - }, [config, fetchProfile]); + }, [config]); const login = useCallback( async (options?: LoginOptions) => { diff --git a/packages/preact/src/index.ts b/packages/preact/src/index.ts index 5b67df4..db6fdd9 100644 --- a/packages/preact/src/index.ts +++ b/packages/preact/src/index.ts @@ -10,4 +10,5 @@ export type { LoginOptions, } from "./types.js"; +export type { OidcClientConfig } from "oidc-js"; export type { OidcConfig, OidcUser, TokenSet } from "oidc-js-core"; diff --git a/packages/preact/src/tests/context.test.tsx b/packages/preact/src/tests/context.test.tsx index 97673c2..0798f11 100644 --- a/packages/preact/src/tests/context.test.tsx +++ b/packages/preact/src/tests/context.test.tsx @@ -177,7 +177,7 @@ describe("AuthProvider", () => { } render( - h(AuthProvider, { config: CONFIG, fetchProfile: true }, h(Consumer, null)), + h(AuthProvider, { config: { ...CONFIG, fetchProfile: true } }, h(Consumer, null)), ); await waitFor(() => { @@ -245,7 +245,7 @@ describe("AuthProvider", () => { } render( - h(AuthProvider, { config: CONFIG, fetchProfile: false }, h(Consumer, null)), + h(AuthProvider, { config: { ...CONFIG, fetchProfile: false } }, h(Consumer, null)), ); await waitFor(() => { diff --git a/packages/preact/src/types.ts b/packages/preact/src/types.ts index 5262c37..3b3ddfe 100644 --- a/packages/preact/src/types.ts +++ b/packages/preact/src/types.ts @@ -1,8 +1,6 @@ -import type { OidcConfig } from "oidc-js-core"; - export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; -import type { AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcClientConfig, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; import type { OidcUser } from "oidc-js-core"; /** Actions available for controlling the authentication lifecycle. */ @@ -20,7 +18,7 @@ export interface AuthActions { /** The value provided by {@link AuthProvider} and consumed by {@link useAuth}. */ export interface AuthContextValue { /** The OIDC configuration used by the provider. */ - config: OidcConfig; + config: OidcClientConfig; /** The authenticated user, or null if not logged in. */ user: AuthUser | null; /** Whether the user is currently authenticated. */ diff --git a/packages/react/package.json b/packages/react/package.json index df80926..431b3c1 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-react", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for React. Provider, hooks, and route guards with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/react/src/context.tsx b/packages/react/src/context.tsx index 9b88b8b..bc803db 100644 --- a/packages/react/src/context.tsx +++ b/packages/react/src/context.tsx @@ -8,15 +8,13 @@ import { useMemo, type ReactNode, } from "react"; -import { OidcClient, type AuthState, type LoginOptions } from "oidc-js"; -import type { OidcConfig } from "oidc-js-core"; +import { OidcClient, type OidcClientConfig, type AuthState, type LoginOptions } from "oidc-js"; import type { AuthContextValue } from "./types.js"; const AuthContext = createContext(null); interface AuthProviderProps { - config: OidcConfig; - fetchProfile?: boolean; + config: OidcClientConfig; onLogin?: (returnTo: string) => void; onError?: (error: Error) => void; children: ReactNode; @@ -24,7 +22,6 @@ interface AuthProviderProps { export function AuthProvider({ config, - fetchProfile = true, onLogin, onError, children, @@ -45,7 +42,7 @@ export function AuthProvider({ onErrorRef.current = onError; useEffect(() => { - const client = new OidcClient({ ...config, fetchProfile }); + const client = new OidcClient(config); clientRef.current = client; const unsub = client.subscribe(setState); @@ -66,7 +63,7 @@ export function AuthProvider({ unsub(); client.destroy(); }; - }, [config, fetchProfile]); + }, [config]); const login = useCallback( async (options?: LoginOptions) => { diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 5b67df4..db6fdd9 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -10,4 +10,5 @@ export type { LoginOptions, } from "./types.js"; +export type { OidcClientConfig } from "oidc-js"; export type { OidcConfig, OidcUser, TokenSet } from "oidc-js-core"; diff --git a/packages/react/src/tests/context.test.tsx b/packages/react/src/tests/context.test.tsx index d16663d..f5cd675 100644 --- a/packages/react/src/tests/context.test.tsx +++ b/packages/react/src/tests/context.test.tsx @@ -91,7 +91,7 @@ function wrapper({ children }: { children: ReactNode }) { function wrapperWithProfile({ children }: { children: ReactNode }) { return ( - + {children} ); @@ -99,7 +99,7 @@ function wrapperWithProfile({ children }: { children: ReactNode }) { function wrapperNoProfile({ children }: { children: ReactNode }) { return ( - + {children} ); diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 07a7986..b8eccec 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -1,8 +1,6 @@ -import type { OidcConfig } from "oidc-js-core"; - export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; -import type { OidcClient, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcClient, OidcClientConfig, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; import type { OidcUser } from "oidc-js-core"; export interface AuthActions { @@ -13,7 +11,7 @@ export interface AuthActions { } export interface AuthContextValue { - config: OidcConfig; + config: OidcClientConfig; client: OidcClient; user: AuthUser | null; isAuthenticated: boolean; diff --git a/packages/solid/package.json b/packages/solid/package.json index 5131066..4d1c6e6 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-solid", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for SolidJS. Signals, context, and auth guards with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/solid/src/context.tsx b/packages/solid/src/context.tsx index 24c13e4..e32dd18 100644 --- a/packages/solid/src/context.tsx +++ b/packages/solid/src/context.tsx @@ -6,8 +6,7 @@ import { onCleanup, type ParentComponent, } from "solid-js"; -import { OidcClient, type AuthState, type LoginOptions } from "oidc-js"; -import type { OidcConfig } from "oidc-js-core"; +import { OidcClient, type OidcClientConfig, type AuthState, type LoginOptions } from "oidc-js"; import type { AuthContextValue } from "./types.js"; const AuthContext = createContext(); @@ -17,9 +16,7 @@ const AuthContext = createContext(); */ interface AuthProviderProps { /** OIDC configuration including issuer, clientId, and redirectUri. */ - config: OidcConfig; - /** Whether to fetch the userinfo profile after token exchange. Defaults to true. */ - fetchProfile?: boolean; + config: OidcClientConfig; /** Callback invoked after a successful login with the returnTo path. */ onLogin?: (returnTo: string) => void; /** Callback invoked when an authentication error occurs. */ @@ -53,10 +50,7 @@ export const AuthProvider: ParentComponent = (props) => { let client: OidcClient | null = null; onMount(() => { - const oidcClient = new OidcClient({ - ...props.config, - fetchProfile: props.fetchProfile ?? true, - }); + const oidcClient = new OidcClient(props.config); client = oidcClient; const unsub = oidcClient.subscribe((newState) => { diff --git a/packages/solid/src/index.ts b/packages/solid/src/index.ts index 5b67df4..db6fdd9 100644 --- a/packages/solid/src/index.ts +++ b/packages/solid/src/index.ts @@ -10,4 +10,5 @@ export type { LoginOptions, } from "./types.js"; +export type { OidcClientConfig } from "oidc-js"; export type { OidcConfig, OidcUser, TokenSet } from "oidc-js-core"; diff --git a/packages/solid/src/tests/context.test.tsx b/packages/solid/src/tests/context.test.tsx index 56aeb36..e709cbf 100644 --- a/packages/solid/src/tests/context.test.tsx +++ b/packages/solid/src/tests/context.test.tsx @@ -166,7 +166,7 @@ describe("AuthProvider", () => { let authRef: ReturnType | null = null; render(() => ( - + {(() => { authRef = useAuth(); return
{authRef.isAuthenticated ? "authed" : "not-authed"}
; diff --git a/packages/solid/src/types.ts b/packages/solid/src/types.ts index fe4a8a8..0cc883e 100644 --- a/packages/solid/src/types.ts +++ b/packages/solid/src/types.ts @@ -1,8 +1,6 @@ -import type { OidcConfig } from "oidc-js-core"; - export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; -import type { AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcClientConfig, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; import type { OidcUser } from "oidc-js-core"; /** @@ -28,7 +26,7 @@ export interface AuthActions { */ export interface AuthContextValue { /** The OIDC configuration used to initialize the provider. */ - config: OidcConfig; + config: OidcClientConfig; /** The authenticated user, or null if not logged in. */ user: AuthUser | null; /** Whether the user is currently authenticated. */ diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 6597adb..518f887 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-svelte", - "version": "1.1.0", + "version": "1.1.2", "description": "Simple OIDC authentication for Svelte 5. Context, runes, and auth guards with zero dependencies.", "type": "module", "svelte": "./dist/index.js", diff --git a/packages/svelte/src/AuthProvider.svelte b/packages/svelte/src/AuthProvider.svelte index 1d0cf58..1269611 100644 --- a/packages/svelte/src/AuthProvider.svelte +++ b/packages/svelte/src/AuthProvider.svelte @@ -16,16 +16,14 @@ ``` --> - + {#snippet children()} {#if path === "/callback"} diff --git a/tests/e2e/vue-app/src/main.ts b/tests/e2e/vue-app/src/main.ts index 51a617a..9b566fc 100644 --- a/tests/e2e/vue-app/src/main.ts +++ b/tests/e2e/vue-app/src/main.ts @@ -8,6 +8,8 @@ import ProtectedA from "./views/ProtectedA.vue"; import ProtectedB from "./views/ProtectedB.vue"; const fetchProfile = localStorage.getItem("e2e-fetchProfile") !== "false"; +const autoRefreshInterval = Number(localStorage.getItem("e2e-autoRefreshInterval")); + const idpPort = import.meta.env.VITE_IDP_PORT ?? "9999"; const appPort = import.meta.env.VITE_APP_PORT ?? "5173"; @@ -30,8 +32,9 @@ app.use(oidcPlugin, { redirectUri: `http://localhost:${appPort}/callback`, scopes: ["openid", "profile", "email", "offline_access"], postLogoutRedirectUri: `http://localhost:${appPort}`, + fetchProfile, + autoRefreshInterval: autoRefreshInterval || undefined, }, - fetchProfile, onLogin(returnTo: string) { router.replace(returnTo); },