Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/better-towns-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@asgardeo/javascript': minor
'@asgardeo/react': minor
---

Expose OIDC discovery response from `useAsgardeo`
9 changes: 9 additions & 0 deletions packages/javascript/src/AsgardeoJavaScriptClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
EmbeddedSignInFlowInitiateResponse,
EmbeddedSignInFlowStatus,
} from './models/embedded-signin-flow';
import {OIDCDiscoveryApiResponse} from './models/oidc-discovery';
import {AllOrganizationsApiResponse, Organization} from './models/organization';
import {Storage} from './models/store';
import {TokenExchangeRequestConfig, TokenResponse} from './models/token';
Expand Down Expand Up @@ -68,6 +69,14 @@ class AsgardeoJavaScriptClient<T = Config> implements AsgardeoClient<T> {
this.baseURL = config?.baseUrl ?? '';
}

public async getDiscoveryResponse(): Promise<OIDCDiscoveryApiResponse | null> {
if (!this.storageManager) {
return null;
}

return this.storageManager.loadOpenIDProviderConfiguration();
}

/* eslint-disable class-methods-use-this, @typescript-eslint/no-unused-vars */
switchOrganization(_organization: Organization, _sessionId?: string): Promise<TokenResponse | Response> {
throw new Error('Method not implemented.');
Expand Down
30 changes: 24 additions & 6 deletions packages/javascript/src/__legacy__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import {AuthClientConfig, StrictAuthClientConfig} from './models';
import OIDCDiscoveryConstants from '../constants/OIDCDiscoveryConstants';
import OIDCRequestConstants from '../constants/OIDCRequestConstants';
import PKCEConstants from '../constants/PKCEConstants';
import OIDCDiscoveryConstantsV2 from '../constants/v2/OIDCDiscoveryConstants';
import {AsgardeoAuthException} from '../errors/exception';
import {IsomorphicCrypto} from '../IsomorphicCrypto';
import {Crypto} from '../models/crypto';
import {ExtendedAuthorizeRequestUrlParams} from '../models/oauth-request';
import {OIDCDiscoveryApiResponse} from '../models/oidc-discovery';
import {OIDCEndpoints} from '../models/oidc-endpoints';
import {Platform} from '../models/platforms';
import {SessionData, UserSession} from '../models/session';
import {Storage, TemporaryStore} from '../models/store';
import {TokenResponse, IdToken, TokenExchangeRequestConfig} from '../models/token';
Expand Down Expand Up @@ -149,7 +151,8 @@ export class AsgardeoAuthClient<T> {
// Ref: https://github.com/asgardeo/asgardeo-auth-js-core/pull/205
AsgardeoAuthClient.authHelperInstance = this.authHelper;

let {applicationId} = config;
const {applicationId, platform, endpoints} = config;
let resolvedApplicationId: string | undefined = applicationId;

if (applicationId) {
await this.storageManager.setPersistedData({
Expand All @@ -159,14 +162,23 @@ export class AsgardeoAuthClient<T> {
const persistedData: TemporaryStore = await this.storageManager.getPersistedData();

if (persistedData['applicationId']) {
applicationId = persistedData['applicationId'] as string;
resolvedApplicationId = persistedData['applicationId'] as string;
}
}

const resolvedEndpoints: Partial<OIDCEndpoints> = endpoints || {};

if (platform === Platform.AsgardeoV2) {
if (!resolvedEndpoints['wellKnown']) {
resolvedEndpoints['wellKnown'] = OIDCDiscoveryConstantsV2.Endpoints.WELL_KNOWN;
}
}

await this.storageManager.setConfigData({
...DefaultConfig,
...config,
applicationId,
applicationId: resolvedApplicationId,
endpoints: resolvedEndpoints,
scope: processOpenIDScopes(config.scopes),
});
}
Expand Down Expand Up @@ -434,13 +446,19 @@ export class AsgardeoAuthClient<T> {
return Promise.resolve();
}

const {wellKnownEndpoint} = configData as any;
const {wellKnownEndpoint, platform, discovery, baseUrl, endpoints} = configData as any;

const resolvedWellKnownEndpoint: string | undefined =
wellKnownEndpoint ||
(platform === Platform.AsgardeoV2 && discovery?.wellKnown?.enabled
? `${baseUrl}${endpoints?.wellKnown ?? '/.well-known/openid-configuration'}`
: undefined);

if (wellKnownEndpoint) {
if (resolvedWellKnownEndpoint) {
let response: Response;

try {
response = await fetch(wellKnownEndpoint);
response = await fetch(resolvedWellKnownEndpoint);
if (response.status !== 200 || !response.ok) {
throw new Error();
}
Expand Down
7 changes: 7 additions & 0 deletions packages/javascript/src/__legacy__/models/client-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import {OAuthResponseMode} from '../../models/oauth-response';
import {OIDCEndpoints} from '../../models/oidc-endpoints';
import {Platform} from '../../models/platforms';

export interface DefaultAuthClientConfig {
afterSignInUrl: string;
Expand All @@ -42,6 +43,12 @@ export interface DefaultAuthClientConfig {
*/
targetOrganizationId?: string;
};
/**
* Optional platform where the application is running.
* This helps the SDK to optimize its behavior based on the platform.
* If not provided, the SDK will attempt to auto-detect the platform.
*/
platform?: keyof typeof Platform;
prompt?: string;
responseMode?: OAuthResponseMode;
scopes?: string | string[] | undefined;
Expand Down
54 changes: 54 additions & 0 deletions packages/javascript/src/constants/v2/OIDCDiscoveryConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
* Constants related to OpenID Connect (OIDC) metadata and endpoints.
* This object contains all the standard OIDC endpoints and storage keys
* used throughout the application for authentication and authorization.
*
* @remarks
* The constants are organized into two main sections:
* 1. Endpoints - Contains all OIDC standard endpoint paths
* 2. Storage - Contains keys used for storing OIDC-related data
*
* @example
* ```typescript
* // Using an endpoint
* const wellKnownEndpoint = OIDCDiscoveryConstants.Endpoints.WELL_KNOWN;
* ```
*/
const OIDCDiscoveryConstants: {
readonly Endpoints: {
readonly WELL_KNOWN: string;
};
} = {
/**
* Collection of standard OIDC endpoint paths used for authentication flows.
* These endpoints are relative paths that should be appended to the base URL
* of your identity provider.
*/
Endpoints: {
/**
* OpenID Connect discovery document endpoint.
* Used to fetch provider metadata from the authorization server.
*/
WELL_KNOWN: '/.well-known/openid-configuration',
},
} as const;

export default OIDCDiscoveryConstants;
1 change: 1 addition & 0 deletions packages/javascript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export {
ExtendedAuthorizeRequestUrlParams,
} from './models/oauth-request';
export {OIDCEndpoints} from './models/oidc-endpoints';
export {OIDCDiscoveryApiResponse} from './models/oidc-discovery';
export {Storage, TemporaryStore} from './models/store';
export {User, UserProfile} from './models/user';
export {SessionData} from './models/session';
Expand Down
81 changes: 81 additions & 0 deletions packages/javascript/src/models/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,86 @@ export interface BaseConfig<T = unknown> extends WithPreferences {
*/
clientSecret?: string | undefined;

/**
* OpenID Connect discovery configuration.
* Controls how the SDK resolves endpoint URLs from the authorization server.
* Each discovery mechanism is independently configurable.
*
* @example
* // Use a custom well-known discovery document URL
* discovery: { wellKnown: { endpoint: "https://custom.example.com/.well-known/openid-configuration" } }
*
* @example
* // Disable well-known discovery entirely
* discovery: { wellKnown: { enabled: false } }
*/
discovery?: {
/**
* Configuration for OpenID Connect Discovery via the well-known endpoint (RFC 8414).
* The SDK fetches `{baseUrl}/oauth2/token/.well-known/openid-configuration` by default.
*/
wellKnown?: {
/**
* Whether to fetch and use the well-known discovery document to resolve endpoint URLs.
* @default true
*/
enabled?: boolean;
};
};

/**
* Optional overrides for the OIDC protocol endpoints.
* By default, the SDK derives all endpoint URLs from the well-known discovery document
* located at `{baseUrl}/oauth2/token/.well-known/openid-configuration`.
* Use this when your authorization server exposes endpoints at non-standard paths,
* or when a custom domain differs from `baseUrl`.
*
* Individual overrides take precedence over values resolved from the discovery document.
*
* @example
* endpoints: {
* wellKnown: "https://custom-domain.example.com/.well-known/openid-configuration",
* authorization: "https://custom-domain.example.com/oauth2/authorize",
* }
*/
endpoints?: {
/**
* The authorization endpoint URL.
* If not provided, resolved from the well-known discovery document.
*/
authorization?: string;
/**
* The end-session (logout) endpoint URL.
* If not provided, resolved from the well-known discovery document.
*/
endSession?: string;
/**
* The introspection endpoint URL.
* If not provided, resolved from the well-known discovery document.
*/
introspection?: string;
/**
* The JSON Web Key Set (JWKS) endpoint URL used to fetch public keys for token verification.
* If not provided, resolved from the well-known discovery document.
*/
jwks?: string;
/**
* The token endpoint URL.
* If not provided, resolved from the well-known discovery document.
*/
token?: string;
/**
* The userinfo endpoint URL.
* If not provided, resolved from the well-known discovery document.
*/
userInfo?: string;
/**
* The OpenID Connect discovery document URL.
* Defaults to `{baseUrl}/oauth2/token/.well-known/openid-configuration`.
*/
wellKnown?: string;
};

/**
* Optional instance ID for multi-auth context support.
* Use this when you need multiple authentication contexts in the same application.
Expand Down Expand Up @@ -226,6 +306,7 @@ export interface BaseConfig<T = unknown> extends WithPreferences {
* @see {@link https://openid.net/specs/openid-connect-session-management-1_0.html#IframeBasedSessionManagement}
*/
syncSession?: boolean;

/**
* Token validation configuration.
* This allows you to configure how the SDK validates tokens received from the authorization server.
Expand Down
17 changes: 13 additions & 4 deletions packages/react/src/AsgardeoReactClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
EmbeddedSignInFlowResponseV2,
executeEmbeddedSignUpFlowV2,
EmbeddedSignInFlowStatusV2,
OIDCDiscoveryApiResponse,
} from '@asgardeo/browser';
import AuthAPI from './__temp__/api';
import getAllOrganizations from './api/getAllOrganizations';
Expand Down Expand Up @@ -124,17 +125,19 @@ class AsgardeoReactClient<T extends AsgardeoReactConfig = AsgardeoReactConfig> e
resolvedOrganizationHandle = deriveOrganizationHandleFromBaseUrl(config?.baseUrl);
}

return this.withLoading(async () =>
this.asgardeo.init({...config, organizationHandle: resolvedOrganizationHandle} as any),
);
return this.withLoading(async () => {
this.initializeConfig = {...config, organizationHandle: resolvedOrganizationHandle};

return this.asgardeo.init(this.initializeConfig as any);
});
}

override reInitialize(config: Partial<AsgardeoReactConfig>): Promise<boolean> {
return this.withLoading(async () => {
let isInitialized: boolean;

try {
await this.asgardeo.reInitialize(config);
await this.asgardeo.reInitialize(config as any);

isInitialized = true;
} catch (error) {
Expand Down Expand Up @@ -522,6 +525,12 @@ class AsgardeoReactClient<T extends AsgardeoReactConfig = AsgardeoReactConfig> e
return undefined;
}

override async getDiscoveryResponse(): Promise<OIDCDiscoveryApiResponse | null> {
const storageManager: any = await this.asgardeo.getStorageManager();

return storageManager.loadOpenIDProviderConfiguration();
}

async request(requestConfig?: HttpRequestConfig): Promise<HttpResponse<any>> {
return this.asgardeo.httpRequest(requestConfig);
}
Expand Down
15 changes: 15 additions & 0 deletions packages/react/src/contexts/Asgardeo/AsgardeoContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
HttpRequestConfig,
HttpResponse,
IdToken,
OIDCDiscoveryApiResponse,
Organization,
SignInOptions,
TokenExchangeRequestConfig,
Expand All @@ -38,6 +39,17 @@ export type AsgardeoContextProps = {
applicationId: string | undefined;
baseUrl: string | undefined;
clientId: string | undefined;
/**
* OIDC discovery data.
*/
discovery: {
/**
* The response from the `.well-known/openid-configuration` endpoint.
* Contains server capabilities, supported endpoints, and metadata.
* `null` while loading or when discovery has not been fetched.
*/
wellKnown: OIDCDiscoveryApiResponse | null;
};
/**
* Swaps the current access token with a new one based on the provided configuration (with a grant type).
* @param config - Configuration for the token exchange request.
Expand Down Expand Up @@ -186,6 +198,9 @@ const AsgardeoContext: Context<AsgardeoContextProps | null> = createContext<null
baseUrl: undefined,
clearSession: () => {},
clientId: undefined,
discovery: {
wellKnown: null,
},
exchangeToken: null,
getAccessToken: null,
getDecodedIdToken: null,
Expand Down
Loading
Loading