From a7e20068a459e2b1d766471bfbd8298fee29b895 Mon Sep 17 00:00:00 2001 From: eugenioenko Date: Wed, 6 May 2026 20:33:02 -0700 Subject: [PATCH] feat: return values from refresh/fetchProfile and expose client instance refresh() now returns Promise and fetchProfile() returns Promise, enabling direct use in HTTP interceptors without waiting for framework state updates. The React adapter also exposes the OidcClient instance via useAuth(). Closes #67, closes #68 Bumps all packages to 1.1.0. Co-Authored-By: Claude Opus 4.6 --- packages/angular/package.json | 2 +- packages/angular/src/auth.service.ts | 8 ++++---- packages/client/package.json | 2 +- packages/client/src/client.ts | 11 +++++++---- packages/core/package.json | 2 +- packages/kasper/package.json | 2 +- packages/kasper/src/types.ts | 5 +++-- packages/lit/package.json | 2 +- packages/lit/src/auth-controller.ts | 9 +++++---- packages/lit/src/types.ts | 7 ++++--- packages/preact/package.json | 2 +- packages/preact/src/context.tsx | 5 +++-- packages/preact/src/types.ts | 5 +++-- packages/react/package.json | 2 +- packages/react/src/context.tsx | 7 ++++--- packages/react/src/tests/auth-required.test.tsx | 1 + packages/react/src/types.ts | 8 +++++--- packages/solid/package.json | 2 +- packages/solid/src/context.tsx | 5 +++-- packages/solid/src/types.ts | 5 +++-- packages/svelte/package.json | 2 +- packages/svelte/src/types.ts | 5 +++-- packages/vue/package.json | 2 +- packages/vue/src/plugin.ts | 4 ++-- packages/vue/src/types.ts | 5 +++-- 25 files changed, 63 insertions(+), 47 deletions(-) diff --git a/packages/angular/package.json b/packages/angular/package.json index 4693e6f..7222466 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-angular", - "version": "1.0.8", + "version": "1.1.0", "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 ea0bf97..68fb0a1 100644 --- a/packages/angular/src/auth.service.ts +++ b/packages/angular/src/auth.service.ts @@ -127,8 +127,8 @@ export class AuthService { * * @throws Error if no refresh token is available. */ - async refresh(): Promise { - await this.client.refresh(); + async refresh() { + return this.client.refresh(); } /** @@ -136,7 +136,7 @@ export class AuthService { * * @throws Error if no access token is available. */ - async fetchProfile(): Promise { - await this.client.fetchProfile(); + async fetchProfile() { + return this.client.fetchProfile(); } } diff --git a/packages/client/package.json b/packages/client/package.json index 8df8783..89fd4fb 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js", - "version": "1.0.8", + "version": "1.1.0", "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 f68e155..ceb8f7d 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -51,7 +51,7 @@ export class OidcClient { private discovery: OidcDiscovery | null = null; private subscribers = new Set(); private abortController: AbortController | null = null; - private refreshPromise: Promise | null = null; + private refreshPromise: Promise | null = null; private _state: AuthState = { user: null, @@ -243,7 +243,7 @@ export class OidcClient { * * @throws Error if no refresh token is available or discovery has not been fetched. */ - async refresh(): Promise { + async refresh(): Promise { if (this.refreshPromise) { return this.refreshPromise; } @@ -255,7 +255,7 @@ export class OidcClient { return this.refreshPromise; } - private async refreshInternal(): Promise { + private async refreshInternal(): Promise { const refreshToken = this._state.tokens.refresh; if (!this.discovery || !refreshToken) { @@ -289,6 +289,8 @@ export class OidcClient { isAuthenticated: true, error: null, }); + + return newTokens; } /** @@ -298,7 +300,7 @@ export class OidcClient { * * @throws Error if no access token is available or discovery has not been fetched. */ - async fetchProfile(): Promise { + async fetchProfile(): Promise { if (!this.discovery || !this._state.tokens.access) { throw new Error("No access token available"); } @@ -307,6 +309,7 @@ export class OidcClient { if (this._state.user) { this.setState({ user: { ...this._state.user, profile } }); } + return profile; } /** diff --git a/packages/core/package.json b/packages/core/package.json index e795d71..b17e65d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-core", - "version": "1.0.8", + "version": "1.1.0", "description": "Zero-dependency OIDC/OAuth 2.0 functions for JavaScript. Pure, functional, works everywhere.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/kasper/package.json b/packages/kasper/package.json index c72d7e4..3d03a95 100644 --- a/packages/kasper/package.json +++ b/packages/kasper/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-kasper", - "version": "1.0.8", + "version": "1.1.0", "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/types.ts b/packages/kasper/src/types.ts index c7b4f15..49cebd4 100644 --- a/packages/kasper/src/types.ts +++ b/packages/kasper/src/types.ts @@ -1,6 +1,7 @@ import type { Signal } from "kasper-js"; import type { OidcConfig } from "oidc-js-core"; import type { AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcUser } from "oidc-js-core"; export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; export type { Signal } from "kasper-js"; @@ -12,9 +13,9 @@ export interface AuthActions { /** Logs the user out and redirects to the post-logout URI. */ logout: () => void; /** Refreshes the access token using the refresh token. */ - refresh: () => Promise; + refresh: () => Promise; /** Fetches the user's profile from the userinfo endpoint. */ - fetchProfile: () => Promise; + fetchProfile: () => Promise; } /** Value returned by {@link useAuth}. All state properties are Kasper signals. */ diff --git a/packages/lit/package.json b/packages/lit/package.json index ae7ff3d..32c8f9d 100644 --- a/packages/lit/package.json +++ b/packages/lit/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-lit", - "version": "1.0.8", + "version": "1.1.0", "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 75e0b8b..b057224 100644 --- a/packages/lit/src/auth-controller.ts +++ b/packages/lit/src/auth-controller.ts @@ -148,8 +148,9 @@ export class AuthController implements ReactiveController { * * @throws Error if no refresh token is available or discovery has not been fetched. */ - async refresh(): Promise { - await this.client?.refresh(); + async refresh() { + const result = await this.client?.refresh(); + return result ?? { access: null, id: null, refresh: null, expiresAt: null }; } /** @@ -157,7 +158,7 @@ export class AuthController implements ReactiveController { * * @throws Error if no access token is available or discovery has not been fetched. */ - async fetchProfile(): Promise { - await this.client?.fetchProfile(); + async fetchProfile() { + return (await this.client?.fetchProfile()) ?? null; } } diff --git a/packages/lit/src/types.ts b/packages/lit/src/types.ts index bb2c4dd..4619d7a 100644 --- a/packages/lit/src/types.ts +++ b/packages/lit/src/types.ts @@ -2,7 +2,8 @@ import type { OidcConfig } from "oidc-js-core"; export type { IdTokenClaims, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; -import type { LoginOptions } from "oidc-js"; +import type { AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcUser } from "oidc-js-core"; /** * Actions available on the {@link AuthController} for triggering authentication operations. @@ -13,9 +14,9 @@ export interface AuthActions { /** Logs the user out and redirects to the OP's end-session endpoint. */ logout: () => void; /** Uses the stored refresh token to obtain new tokens. */ - refresh: () => Promise; + refresh: () => Promise; /** Fetches the user's profile from the userinfo endpoint. */ - fetchProfile: () => Promise; + fetchProfile: () => Promise; } /** diff --git a/packages/preact/package.json b/packages/preact/package.json index adbd9f8..86a748e 100644 --- a/packages/preact/package.json +++ b/packages/preact/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-preact", - "version": "1.0.8", + "version": "1.1.0", "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 79b25e8..f75d63a 100644 --- a/packages/preact/src/context.tsx +++ b/packages/preact/src/context.tsx @@ -88,11 +88,12 @@ export function AuthProvider({ }, []); const refresh = useCallback(async () => { - await clientRef.current?.refresh(); + const result = await clientRef.current?.refresh(); + return result ?? { access: null, id: null, refresh: null, expiresAt: null }; }, []); const doFetchProfile = useCallback(async () => { - await clientRef.current?.fetchProfile(); + return (await clientRef.current?.fetchProfile()) ?? null; }, []); const actions = useMemo( diff --git a/packages/preact/src/types.ts b/packages/preact/src/types.ts index f017cf1..5262c37 100644 --- a/packages/preact/src/types.ts +++ b/packages/preact/src/types.ts @@ -3,6 +3,7 @@ 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 { OidcUser } from "oidc-js-core"; /** Actions available for controlling the authentication lifecycle. */ export interface AuthActions { @@ -11,9 +12,9 @@ export interface AuthActions { /** Logs the user out and optionally redirects to the OP's end-session endpoint. */ logout: () => void; /** Refreshes the access token using the stored refresh token. */ - refresh: () => Promise; + refresh: () => Promise; /** Fetches the user's profile from the userinfo endpoint. */ - fetchProfile: () => Promise; + fetchProfile: () => Promise; } /** The value provided by {@link AuthProvider} and consumed by {@link useAuth}. */ diff --git a/packages/react/package.json b/packages/react/package.json index 02f6a0d..df80926 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-react", - "version": "1.0.8", + "version": "1.1.0", "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 9573b97..9b88b8b 100644 --- a/packages/react/src/context.tsx +++ b/packages/react/src/context.tsx @@ -80,11 +80,12 @@ export function AuthProvider({ }, []); const refresh = useCallback(async () => { - await clientRef.current?.refresh(); + const result = await clientRef.current?.refresh(); + return result ?? { access: null, id: null, refresh: null, expiresAt: null }; }, []); const doFetchProfile = useCallback(async () => { - await clientRef.current?.fetchProfile(); + return (await clientRef.current?.fetchProfile()) ?? null; }, []); const actions = useMemo( @@ -93,7 +94,7 @@ export function AuthProvider({ ); const value: AuthContextValue = useMemo( - () => ({ config, ...state, actions }), + () => ({ config, client: clientRef.current!, ...state, actions }), [config, state, actions], ); diff --git a/packages/react/src/tests/auth-required.test.tsx b/packages/react/src/tests/auth-required.test.tsx index 9d76ba1..db6b210 100644 --- a/packages/react/src/tests/auth-required.test.tsx +++ b/packages/react/src/tests/auth-required.test.tsx @@ -25,6 +25,7 @@ function makeActions(overrides: Partial = {}) { function makeAuth(overrides: Partial = {}): AuthContextValue { return { config: { issuer: "https://auth.example.com", clientId: "app" }, + client: {} as AuthContextValue["client"], user: null, isAuthenticated: false, isLoading: false, diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index b2b361b..07a7986 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -2,17 +2,19 @@ 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 { OidcClient, AuthUser, AuthTokens, LoginOptions } from "oidc-js"; +import type { OidcUser } from "oidc-js-core"; export interface AuthActions { login: (options?: LoginOptions) => void; logout: () => void; - refresh: () => Promise; - fetchProfile: () => Promise; + refresh: () => Promise; + fetchProfile: () => Promise; } export interface AuthContextValue { config: OidcConfig; + client: OidcClient; user: AuthUser | null; isAuthenticated: boolean; isLoading: boolean; diff --git a/packages/solid/package.json b/packages/solid/package.json index 6cb924d..5131066 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-solid", - "version": "1.0.8", + "version": "1.1.0", "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 743f04e..24c13e4 100644 --- a/packages/solid/src/context.tsx +++ b/packages/solid/src/context.tsx @@ -90,11 +90,12 @@ export const AuthProvider: ParentComponent = (props) => { }; const refresh = async () => { - await client?.refresh(); + const result = await client?.refresh(); + return result ?? { access: null, id: null, refresh: null, expiresAt: null }; }; const doFetchProfile = async () => { - await client?.fetchProfile(); + return (await client?.fetchProfile()) ?? null; }; const actions = { login, logout, refresh, fetchProfile: doFetchProfile }; diff --git a/packages/solid/src/types.ts b/packages/solid/src/types.ts index f3e5c67..fe4a8a8 100644 --- a/packages/solid/src/types.ts +++ b/packages/solid/src/types.ts @@ -3,6 +3,7 @@ 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 { OidcUser } from "oidc-js-core"; /** * Actions available for controlling authentication flow. @@ -15,9 +16,9 @@ export interface AuthActions { /** Logs out the current user and clears auth state. */ logout: () => void; /** Refreshes the access token using the stored refresh token. */ - refresh: () => Promise; + refresh: () => Promise; /** Fetches the user's profile from the userinfo endpoint. */ - fetchProfile: () => Promise; + fetchProfile: () => Promise; } /** diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 430cec6..6597adb 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-svelte", - "version": "1.0.8", + "version": "1.1.0", "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/types.ts b/packages/svelte/src/types.ts index df131f7..5b39f98 100644 --- a/packages/svelte/src/types.ts +++ b/packages/svelte/src/types.ts @@ -3,6 +3,7 @@ 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 { OidcUser } from "oidc-js-core"; /** Actions available to interact with the OIDC authentication flow. */ export interface AuthActions { @@ -11,9 +12,9 @@ export interface AuthActions { /** Logs the user out and redirects to the end-session endpoint. */ logout: () => void; /** Refreshes the access token using the stored refresh token. */ - refresh: () => Promise; + refresh: () => Promise; /** Fetches the user's profile from the userinfo endpoint. */ - fetchProfile: () => Promise; + fetchProfile: () => Promise; } /** Reactive authentication context value provided by {@link AuthProvider}. */ diff --git a/packages/vue/package.json b/packages/vue/package.json index fecdaea..8f813f6 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "oidc-js-vue", - "version": "1.0.8", + "version": "1.1.0", "description": "Simple OIDC authentication for Vue. Plugin, composables, and navigation guards with zero dependencies.", "type": "module", "main": "./dist/index.cjs", diff --git a/packages/vue/src/plugin.ts b/packages/vue/src/plugin.ts index 7f15555..59af1b2 100644 --- a/packages/vue/src/plugin.ts +++ b/packages/vue/src/plugin.ts @@ -92,11 +92,11 @@ export const oidcPlugin = { }; const refresh = async () => { - await client.refresh(); + return client.refresh(); }; const doFetchProfile = async () => { - await client.fetchProfile(); + return client.fetchProfile(); }; const actions: AuthActions = { diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 7e9d80b..202e3c7 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -3,6 +3,7 @@ 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 { OidcUser } from "oidc-js-core"; /** * Actions available for authentication operations. @@ -16,9 +17,9 @@ export interface AuthActions { /** Logs the user out and redirects to the OP's end-session endpoint. */ logout: () => void; /** Uses the stored refresh token to obtain a new set of tokens. */ - refresh: () => Promise; + refresh: () => Promise; /** Fetches the user's profile from the userinfo endpoint. */ - fetchProfile: () => Promise; + fetchProfile: () => Promise; } /**