diff --git a/packages/nextjs/src/AsgardeoNextClient.ts b/packages/nextjs/src/AsgardeoNextClient.ts index 093eff24c..b2ba624e3 100644 --- a/packages/nextjs/src/AsgardeoNextClient.ts +++ b/packages/nextjs/src/AsgardeoNextClient.ts @@ -433,6 +433,12 @@ class AsgardeoNextClient exte return this.asgardeo.getDecodedIdToken(sessionId as string, idToken); } + async getIdToken(sessionId?: string): Promise { + await this.ensureInitialized(); + const resolvedSessionId: string = sessionId || ((await getSessionId()) as string); + return this.asgardeo.getIdToken(resolvedSessionId); + } + override getConfiguration(): T { return this.asgardeo.getConfigData() as unknown as T; } diff --git a/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoContext.ts b/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoContext.ts index 56a756cec..8627a9a29 100644 --- a/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoContext.ts +++ b/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoContext.ts @@ -19,7 +19,7 @@ 'use client'; import {AsgardeoContextProps as AsgardeoReactContextProps} from '@asgardeo/react'; -import {EmbeddedFlowExecuteRequestConfig, EmbeddedSignInFlowHandleRequestPayload, User} from '@asgardeo/node'; +import {EmbeddedFlowExecuteRequestConfig, EmbeddedSignInFlowHandleRequestPayload, User, TokenExchangeRequestConfig, TokenResponse, IdToken} from '@asgardeo/node'; import {Context, createContext} from 'react'; /** @@ -44,6 +44,10 @@ const AsgardeoContext: Context = createContext Promise.resolve({} as any), signUp: () => Promise.resolve({} as any), user: null, + getDecodedIdToken: async (sessionId?:string) => Promise.resolve({} as IdToken), + getIdToken: async () => Promise.resolve(undefined), + getAccessToken: async (sessionId?:string) => Promise.resolve(undefined), + exchangeToken: async (config: TokenExchangeRequestConfig, sessionId?:string) => Promise.resolve({} as TokenResponse | Response), }); AsgardeoContext.displayName = 'AsgardeoContext'; diff --git a/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx b/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx index 4b52db7d0..6dedefcb3 100644 --- a/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx +++ b/packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx @@ -77,6 +77,10 @@ export type AsgardeoClientProviderProps = Partial Promise<{data: {user: User}; error: string; success: boolean}>; user: User | null; userProfile: UserProfile; + getIdToken: AsgardeoContextProps['getIdToken']; + getDecodedIdToken: AsgardeoContextProps['getDecodedIdToken']; + getAccessToken: AsgardeoContextProps['getAccessToken']; + exchangeToken: AsgardeoContextProps['exchangeToken']; }; const AsgardeoClientProvider: FC> = ({ @@ -102,6 +106,10 @@ const AsgardeoClientProvider: FC> getAllOrganizations, switchOrganization, brandingPreference, + getIdToken, + getDecodedIdToken, + getAccessToken, + exchangeToken, }: PropsWithChildren) => { const reRenderCheckRef: RefObject = useRef(false); const router = useRouter(); @@ -309,8 +317,12 @@ const AsgardeoClientProvider: FC> signUpUrl, applicationId, organizationHandle, + getIdToken, + getDecodedIdToken, + getAccessToken, + exchangeToken, }), - [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle], + [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, getIdToken, getDecodedIdToken, getAccessToken, exchangeToken], ); const handleProfileUpdate = (payload: User): void => { diff --git a/packages/nextjs/src/server/AsgardeoProvider.tsx b/packages/nextjs/src/server/AsgardeoProvider.tsx index b053fa723..a37032f12 100644 --- a/packages/nextjs/src/server/AsgardeoProvider.tsx +++ b/packages/nextjs/src/server/AsgardeoProvider.tsx @@ -18,7 +18,7 @@ 'use server'; -import {BrandingPreference, AsgardeoRuntimeError, Organization, User, UserProfile} from '@asgardeo/node'; +import {BrandingPreference, AsgardeoRuntimeError, Organization, User, UserProfile, TokenExchangeRequestConfig} from '@asgardeo/node'; import {AsgardeoProviderProps} from '@asgardeo/react'; import {FC, PropsWithChildren, ReactElement} from 'react'; import createOrganization from './actions/createOrganization'; @@ -41,6 +41,10 @@ import AsgardeoNextClient from '../AsgardeoNextClient'; import AsgardeoClientProvider from '../client/contexts/Asgardeo/AsgardeoProvider'; import {AsgardeoNextConfig} from '../models/config'; import logger from '../utils/logger'; +import getAccessToken from './actions/getAccessToken'; +import getIdToken from './actions/getIdToken'; +import getDecodedIdToken from './actions/getDecodedIdToken'; +import exchangeToken from './actions/exchangeToken'; /** * Props interface of {@link AsgardeoServerProvider} @@ -194,6 +198,12 @@ const AsgardeoServerProvider: FC> switchOrganization={switchOrganization} brandingPreference={brandingPreference} createOrganization={createOrganization} + getAccessToken={async () => await getAccessToken()} + getIdToken={async () => await getIdToken(sessionId)} + exchangeToken={async (exchangeConfig: TokenExchangeRequestConfig) => + await exchangeToken(exchangeConfig, sessionId) + } + getDecodedIdToken={async () => await getDecodedIdToken(sessionId)} > {children} diff --git a/packages/nextjs/src/server/actions/exchangeToken.ts b/packages/nextjs/src/server/actions/exchangeToken.ts new file mode 100644 index 000000000..6e0de69c2 --- /dev/null +++ b/packages/nextjs/src/server/actions/exchangeToken.ts @@ -0,0 +1,41 @@ +/** + * 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. + */ + +'use server'; + +import {TokenExchangeRequestConfig, TokenResponse} from '@asgardeo/node'; +import AsgardeoNextClient from '../../AsgardeoNextClient'; + +/** + * Exchange tokens based on the provided configuration. + * This supports various token exchange grant types like organization switching. + * + * @param config - Token exchange request configuration + * @param sessionId - Optional session ID for the exchange + * @returns The token response from the exchange operation + * @throws {AsgardeoRuntimeError} If the token exchange fails + */ +const exchangeToken = async ( + config: TokenExchangeRequestConfig, + sessionId?: string, +): Promise => { + const client = AsgardeoNextClient.getInstance(); + return client.exchangeToken(config, sessionId); +}; + +export default exchangeToken; diff --git a/packages/nextjs/src/server/actions/getDecodedIdToken.ts b/packages/nextjs/src/server/actions/getDecodedIdToken.ts new file mode 100644 index 000000000..d0bfc18ca --- /dev/null +++ b/packages/nextjs/src/server/actions/getDecodedIdToken.ts @@ -0,0 +1,36 @@ +/** + * 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. + */ + +'use server'; + +import {IdToken} from '@asgardeo/node'; +import AsgardeoNextClient from '../../AsgardeoNextClient'; + +/** + * Get the decoded ID token from the current session. + * + * @param sessionId - Optional session ID to retrieve the decoded ID token for + * @returns The decoded ID token payload + * @throws {AsgardeoRuntimeError} If the decoded ID token cannot be retrieved + */ +const getDecodedIdToken = async (sessionId?: string): Promise => { + const client = AsgardeoNextClient.getInstance(); + return client.getDecodedIdToken(sessionId); +}; + +export default getDecodedIdToken; diff --git a/packages/nextjs/src/server/actions/getIdToken.ts b/packages/nextjs/src/server/actions/getIdToken.ts new file mode 100644 index 000000000..7bd515575 --- /dev/null +++ b/packages/nextjs/src/server/actions/getIdToken.ts @@ -0,0 +1,47 @@ +/** + * 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. + */ + +'use server'; + +import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies'; +import {cookies} from 'next/headers'; +import SessionManager from '../../utils/SessionManager'; + +/** + * Get the ID token from the session cookie. + * + * @returns The ID token if it exists, undefined otherwise + */ +const getIdToken = async (sessionId?: string): Promise => { + const cookieStore: ReadonlyRequestCookies = await cookies(); + + const idToken = cookieStore.get(SessionManager.getSessionCookieName())?.value; + + if (idToken) { + try { + const sessionPayload = await SessionManager.verifySessionToken(idToken); + return sessionPayload['idToken'] as string; + } catch (error) { + return undefined; + } + } + + return undefined; +}; + +export default getIdToken; diff --git a/packages/react/src/contexts/Asgardeo/AsgardeoContext.ts b/packages/react/src/contexts/Asgardeo/AsgardeoContext.ts index 933b30856..969cd8262 100644 --- a/packages/react/src/contexts/Asgardeo/AsgardeoContext.ts +++ b/packages/react/src/contexts/Asgardeo/AsgardeoContext.ts @@ -111,7 +111,7 @@ export type AsgardeoContextProps = { * * @returns A promise that resolves to the decoded ID token payload. */ - getDecodedIdToken: () => Promise; + getDecodedIdToken: () => Promise; /** * Function to retrieve the ID token. @@ -119,15 +119,15 @@ export type AsgardeoContextProps = { * * @returns A promise that resolves to the ID token. */ - getIdToken: () => Promise; + getIdToken: () => Promise; /** * Retrieves the access token stored in the storage. * This function retrieves the access token and returns it. * @remarks This does not work in the `webWorker` or any other worker environment. - * @returns A promise that resolves to the access token. + * @returns A promise that resolves to the access token, or undefined if not available. */ - getAccessToken: () => Promise; + getAccessToken: () => Promise; /** * Swaps the current access token with a new one based on the provided configuration (with a grant type).