Skip to content

Commit e49836a

Browse files
committed
feat(nextjs): expose token APIs via AsgardeoContext
expose getIdToken, getDecodedIdToken, getAccessToken, and exchangeToken methods through AsgardeoContext in the Next.js SDK.
1 parent 0a78391 commit e49836a

File tree

8 files changed

+233
-10
lines changed

8 files changed

+233
-10
lines changed

packages/nextjs/src/AsgardeoNextClient.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,11 @@ class AsgardeoNextClient<T extends AsgardeoNextConfig = AsgardeoNextConfig> exte
411411
* Gets the access token from the session cookie if no sessionId is provided,
412412
* otherwise falls back to legacy client method.
413413
*/
414-
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
415-
async getAccessToken(_sessionId?: string): Promise<string> {
414+
async getAccessToken(sessionId?: string): Promise<string> {
415+
if (sessionId) {
416+
return this.asgardeo.getAccessToken(sessionId);
417+
}
418+
416419
const {default: getAccessToken} = await import('./server/actions/getAccessToken');
417420
const token: string | undefined = await getAccessToken();
418421

packages/nextjs/src/client/contexts/Asgardeo/AsgardeoContext.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
'use client';
2020

2121
import {AsgardeoContextProps as AsgardeoReactContextProps} from '@asgardeo/react';
22+
import {TokenExchangeRequestConfig, TokenResponse, IdToken} from '@asgardeo/node';
2223
import {Context, createContext} from 'react';
2324

2425
/**
@@ -43,6 +44,10 @@ const AsgardeoContext: Context<AsgardeoContextProps | null> = createContext<null
4344
signUp: () => Promise.resolve({} as any),
4445
signUpUrl: undefined,
4546
user: null,
47+
getDecodedIdToken: async (sessionId?:string) => Promise.resolve({} as IdToken),
48+
getIdToken: async () => Promise.resolve(undefined),
49+
getAccessToken: async (sessionId?:string) => Promise.resolve(undefined),
50+
exchangeToken: async (config: TokenExchangeRequestConfig, sessionId?:string) => Promise.resolve({} as TokenResponse | Response | undefined),
4651
});
4752

4853
AsgardeoContext.displayName = 'AsgardeoContext';

packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export type AsgardeoClientProviderProps = Partial<Omit<AsgardeoProviderProps, 'b
7979
) => Promise<{data: {user: User}; error: string; success: boolean}>;
8080
user: User | null;
8181
userProfile: UserProfile;
82+
getIdToken: AsgardeoContextProps['getIdToken'];
83+
getDecodedIdToken: AsgardeoContextProps['getDecodedIdToken'];
84+
getAccessToken: AsgardeoContextProps['getAccessToken'];
85+
exchangeToken: AsgardeoContextProps['exchangeToken'];
8286
};
8387

8488
const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>> = ({
@@ -104,6 +108,10 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
104108
getAllOrganizations,
105109
switchOrganization,
106110
brandingPreference,
111+
getIdToken,
112+
getDecodedIdToken,
113+
getAccessToken,
114+
exchangeToken,
107115
}: PropsWithChildren<AsgardeoClientProviderProps>) => {
108116
const reRenderCheckRef: RefObject<boolean> = useRef(false);
109117
const router: AppRouterInstance = useRouter();
@@ -292,6 +300,10 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
292300
() => ({
293301
applicationId,
294302
baseUrl,
303+
exchangeToken,
304+
getAccessToken,
305+
getDecodedIdToken,
306+
getIdToken,
295307
isLoading,
296308
isSignedIn,
297309
organizationHandle,
@@ -302,7 +314,7 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
302314
signUpUrl,
303315
user,
304316
}),
305-
[baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle],
317+
[baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, getIdToken, getDecodedIdToken, getAccessToken, exchangeToken],
306318
);
307319

308320
const handleProfileUpdate = (payload: User): void => {

packages/nextjs/src/server/AsgardeoProvider.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
'use server';
2020

21-
import {BrandingPreference, AsgardeoRuntimeError, IdToken, Organization, User, UserProfile} from '@asgardeo/node';
21+
import {BrandingPreference, AsgardeoRuntimeError, IdToken, Organization, TokenExchangeRequestConfig, User, UserProfile} from '@asgardeo/node';
2222
import {AsgardeoProviderProps} from '@asgardeo/react';
2323
import {FC, PropsWithChildren, ReactElement} from 'react';
2424
import createOrganization from './actions/createOrganization';
@@ -42,6 +42,10 @@ import AsgardeoClientProvider from '../client/contexts/Asgardeo/AsgardeoProvider
4242
import {AsgardeoNextConfig} from '../models/config';
4343
import logger from '../utils/logger';
4444
import {SessionTokenPayload} from '../utils/SessionManager';
45+
import getAccessToken from './actions/getAccessToken';
46+
import getIdToken from './actions/getIdToken';
47+
import getDecodedIdToken from './actions/getDecodedIdToken';
48+
import exchangeToken from './actions/exchangeToken';
4549

4650
/**
4751
* Props interface of {@link AsgardeoServerProvider}
@@ -209,6 +213,12 @@ const AsgardeoServerProvider: FC<PropsWithChildren<AsgardeoServerProviderProps>>
209213
switchOrganization={switchOrganization}
210214
brandingPreference={brandingPreference}
211215
createOrganization={createOrganization}
216+
getAccessToken={async () => {'use server'; return await getAccessToken();}}
217+
getIdToken={async () => {'use server'; return await getIdToken(sessionId);}}
218+
exchangeToken={async (exchangeConfig: TokenExchangeRequestConfig) => {
219+
'use server'; return await exchangeToken(exchangeConfig, sessionId)
220+
}}
221+
getDecodedIdToken={async () => {'use server'; return await getDecodedIdToken(sessionId);}}
212222
>
213223
{children}
214224
</AsgardeoClientProvider>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
'use server';
20+
21+
import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies';
22+
import {cookies} from 'next/headers';
23+
import {TokenExchangeRequestConfig, TokenResponse} from '@asgardeo/node';
24+
import AsgardeoNextClient from '../../AsgardeoNextClient';
25+
import SessionManager from '../../utils/SessionManager';
26+
27+
/**
28+
* Exchange tokens based on the provided configuration.
29+
* This supports various token exchange grant types like organization switching.
30+
*
31+
* @param config - Token exchange request configuration
32+
* @param sessionId - Optional session ID for the exchange
33+
* @returns The token response from the exchange operation
34+
* @throws {AsgardeoRuntimeError} If the token exchange fails
35+
*/
36+
const exchangeToken = async (
37+
config: TokenExchangeRequestConfig,
38+
sessionId?: string,
39+
): Promise<TokenResponse | Response | undefined> => {
40+
let resolvedSessionId: string | undefined = sessionId;
41+
42+
if (!resolvedSessionId) {
43+
const cookieStore: ReadonlyRequestCookies = await cookies();
44+
const sessionToken = cookieStore.get(SessionManager.getSessionCookieName())?.value;
45+
46+
if (sessionToken) {
47+
try {
48+
const sessionPayload = await SessionManager.verifySessionToken(sessionToken);
49+
resolvedSessionId = sessionPayload['sessionId'] as string;
50+
} catch (error) {
51+
return undefined;
52+
}
53+
}
54+
}
55+
56+
if (!resolvedSessionId) {
57+
return undefined;
58+
}
59+
60+
const client = AsgardeoNextClient.getInstance();
61+
try {
62+
return await client.exchangeToken(config, resolvedSessionId);
63+
} catch (error) {
64+
return undefined;
65+
}
66+
};
67+
68+
export default exchangeToken;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
'use server';
20+
21+
import {ReadonlyRequestCookies} from 'next/dist/server/web/spec-extension/adapters/request-cookies';
22+
import {cookies} from 'next/headers';
23+
import {IdToken} from '@asgardeo/node';
24+
import AsgardeoNextClient from '../../AsgardeoNextClient';
25+
import SessionManager from '../../utils/SessionManager';
26+
27+
/**
28+
* Get the decoded ID token from the current session.
29+
*
30+
* @param sessionId - Optional session ID to retrieve the decoded ID token for
31+
* @returns The decoded ID token payload
32+
* @throws {AsgardeoRuntimeError} If the decoded ID token cannot be retrieved
33+
*/
34+
const getDecodedIdToken = async (sessionId?: string): Promise<IdToken | undefined> => {
35+
let resolvedSessionId: string | undefined = sessionId;
36+
37+
if (!resolvedSessionId) {
38+
const cookieStore: ReadonlyRequestCookies = await cookies();
39+
const sessionToken = cookieStore.get(SessionManager.getSessionCookieName())?.value;
40+
41+
if (sessionToken) {
42+
try {
43+
const sessionPayload = await SessionManager.verifySessionToken(sessionToken);
44+
resolvedSessionId = sessionPayload['sessionId'] as string;
45+
} catch (error) {
46+
return undefined;
47+
}
48+
}
49+
}
50+
51+
if (!resolvedSessionId) {
52+
return undefined;
53+
}
54+
55+
const client = AsgardeoNextClient.getInstance();
56+
try {
57+
return await client.getDecodedIdToken(resolvedSessionId);
58+
} catch (error) {
59+
return undefined;
60+
}
61+
};
62+
63+
export default getDecodedIdToken;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
'use server';
20+
21+
import { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies';
22+
import { cookies } from 'next/headers';
23+
import SessionManager from '../../utils/SessionManager';
24+
import AsgardeoNextClient from '../../AsgardeoNextClient';
25+
26+
/**
27+
* Get the ID token from the session cookie.
28+
*
29+
* @returns The ID token if it exists, undefined otherwise
30+
*/
31+
const getIdToken = async (sessionId?: string): Promise<string | undefined> => {
32+
let resolvedSessionId: string | undefined = sessionId;
33+
34+
if (!resolvedSessionId) {
35+
const cookieStore: ReadonlyRequestCookies = await cookies();
36+
37+
const sessionToken = cookieStore.get(SessionManager.getSessionCookieName())?.value;
38+
39+
if (sessionToken) {
40+
try {
41+
const sessionPayload = await SessionManager.verifySessionToken(sessionToken);
42+
resolvedSessionId = sessionPayload['sessionId'] as string;
43+
} catch (error) {
44+
return undefined;
45+
}
46+
}
47+
}
48+
49+
if (!resolvedSessionId) {
50+
return undefined;
51+
}
52+
53+
const client = AsgardeoNextClient.getInstance();
54+
try {
55+
const idToken = await client.getIdToken(resolvedSessionId);
56+
return idToken;
57+
} catch (error) {
58+
return undefined;
59+
}
60+
};
61+
62+
export default getIdToken;

packages/react/src/contexts/Asgardeo/AsgardeoContext.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,29 @@ export type AsgardeoContextProps = {
4242
* @param config - Configuration for the token exchange request.
4343
* @returns A promise that resolves to the token response or the raw response.
4444
*/
45-
exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;
45+
exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response | undefined>;
4646
/**
4747
* Retrieves the access token stored in the storage.
4848
* This function retrieves the access token and returns it.
4949
* @remarks This does not work in the `webWorker` or any other worker environment.
50-
* @returns A promise that resolves to the access token.
50+
* @returns A promise that resolves to the access token, or undefined if not available.
5151
*/
52-
getAccessToken: () => Promise<string>;
52+
getAccessToken: () => Promise<string | undefined>;
5353
/**
5454
* Function to retrieve the decoded ID token.
5555
* This function decodes the ID token and returns its payload.
5656
* It can be used to access user claims and other information contained in the ID token.
5757
*
5858
* @returns A promise that resolves to the decoded ID token payload.
5959
*/
60-
getDecodedIdToken: () => Promise<IdToken>;
60+
getDecodedIdToken: () => Promise<IdToken | undefined>;
6161
/**
6262
* Function to retrieve the ID token.
6363
* This function retrieves the ID token and returns it.
6464
*
65-
* @returns A promise that resolves to the ID token.
65+
* @returns A promise that resolves to the ID token, or undefined if not available.
6666
*/
67-
getIdToken: () => Promise<string>;
67+
getIdToken: () => Promise<string | undefined>;
6868
/**
6969
* HTTP request function to make API calls.
7070
* @param requestConfig - Configuration for the HTTP request.

0 commit comments

Comments
 (0)