Skip to content

Commit cb916a3

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 dc99542 commit cb916a3

File tree

8 files changed

+231
-8
lines changed

8 files changed

+231
-8
lines changed

packages/nextjs/src/AsgardeoNextClient.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ class AsgardeoNextClient<T extends AsgardeoNextConfig = AsgardeoNextConfig> exte
410410
* otherwise falls back to legacy client method.
411411
*/
412412
async getAccessToken(sessionId?: string): Promise<string> {
413+
if (sessionId) {
414+
return this.asgardeo.getAccessToken(sessionId);
415+
}
416+
413417
const {default: getAccessToken} = await import('./server/actions/getAccessToken');
414418
const token = await getAccessToken();
415419

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

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

2121
import {AsgardeoContextProps as AsgardeoReactContextProps} from '@asgardeo/react';
22-
import {EmbeddedFlowExecuteRequestConfig, EmbeddedSignInFlowHandleRequestPayload, User} from '@asgardeo/node';
22+
import {EmbeddedFlowExecuteRequestConfig, EmbeddedSignInFlowHandleRequestPayload, User, TokenExchangeRequestConfig, TokenResponse, IdToken} from '@asgardeo/node';
2323
import {Context, createContext} from 'react';
2424

2525
/**
@@ -44,6 +44,10 @@ const AsgardeoContext: Context<AsgardeoContextProps | null> = createContext<null
4444
signOut: () => Promise.resolve({} as any),
4545
signUp: () => Promise.resolve({} as any),
4646
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),
4751
});
4852

4953
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
@@ -77,6 +77,10 @@ export type AsgardeoClientProviderProps = Partial<Omit<AsgardeoProviderProps, 'b
7777
) => Promise<{data: {user: User}; error: string; success: boolean}>;
7878
user: User | null;
7979
userProfile: UserProfile;
80+
getIdToken: AsgardeoContextProps['getIdToken'];
81+
getDecodedIdToken: AsgardeoContextProps['getDecodedIdToken'];
82+
getAccessToken: AsgardeoContextProps['getAccessToken'];
83+
exchangeToken: AsgardeoContextProps['exchangeToken'];
8084
};
8185

8286
const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>> = ({
@@ -102,6 +106,10 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
102106
getAllOrganizations,
103107
switchOrganization,
104108
brandingPreference,
109+
getIdToken,
110+
getDecodedIdToken,
111+
getAccessToken,
112+
exchangeToken,
105113
}: PropsWithChildren<AsgardeoClientProviderProps>) => {
106114
const reRenderCheckRef: RefObject<boolean> = useRef(false);
107115
const router = useRouter();
@@ -309,8 +317,12 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
309317
signUpUrl,
310318
applicationId,
311319
organizationHandle,
320+
getIdToken,
321+
getDecodedIdToken,
322+
getAccessToken,
323+
exchangeToken,
312324
}),
313-
[baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle],
325+
[baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, getIdToken, getDecodedIdToken, getAccessToken, exchangeToken],
314326
);
315327

316328
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, Organization, User, UserProfile} from '@asgardeo/node';
21+
import {BrandingPreference, AsgardeoRuntimeError, Organization, User, UserProfile, TokenExchangeRequestConfig} from '@asgardeo/node';
2222
import {AsgardeoProviderProps} from '@asgardeo/react';
2323
import {FC, PropsWithChildren, ReactElement} from 'react';
2424
import createOrganization from './actions/createOrganization';
@@ -41,6 +41,10 @@ import AsgardeoNextClient from '../AsgardeoNextClient';
4141
import AsgardeoClientProvider from '../client/contexts/Asgardeo/AsgardeoProvider';
4242
import {AsgardeoNextConfig} from '../models/config';
4343
import logger from '../utils/logger';
44+
import getAccessToken from './actions/getAccessToken';
45+
import getIdToken from './actions/getIdToken';
46+
import getDecodedIdToken from './actions/getDecodedIdToken';
47+
import exchangeToken from './actions/exchangeToken';
4448

4549
/**
4650
* Props interface of {@link AsgardeoServerProvider}
@@ -194,6 +198,12 @@ const AsgardeoServerProvider: FC<PropsWithChildren<AsgardeoServerProviderProps>>
194198
switchOrganization={switchOrganization}
195199
brandingPreference={brandingPreference}
196200
createOrganization={createOrganization}
201+
getAccessToken={async () => {'use server'; return await getAccessToken();}}
202+
getIdToken={async () => {'use server'; return await getIdToken(sessionId);}}
203+
exchangeToken={async (exchangeConfig: TokenExchangeRequestConfig) => {
204+
'use server'; return await exchangeToken(exchangeConfig, sessionId)
205+
}}
206+
getDecodedIdToken={async () => {'use server'; return await getDecodedIdToken(sessionId);}}
197207
>
198208
{children}
199209
</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: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,30 +111,30 @@ export type AsgardeoContextProps = {
111111
*
112112
* @returns A promise that resolves to the decoded ID token payload.
113113
*/
114-
getDecodedIdToken: () => Promise<IdToken>;
114+
getDecodedIdToken: () => Promise<IdToken | undefined>;
115115

116116
/**
117117
* Function to retrieve the ID token.
118118
* This function retrieves the ID token and returns it.
119119
*
120120
* @returns A promise that resolves to the ID token.
121121
*/
122-
getIdToken: () => Promise<string>;
122+
getIdToken: () => Promise<string | undefined>;
123123

124124
/**
125125
* Retrieves the access token stored in the storage.
126126
* This function retrieves the access token and returns it.
127127
* @remarks This does not work in the `webWorker` or any other worker environment.
128-
* @returns A promise that resolves to the access token.
128+
* @returns A promise that resolves to the access token, or undefined if not available.
129129
*/
130-
getAccessToken: () => Promise<string>;
130+
getAccessToken: () => Promise<string | undefined>;
131131

132132
/**
133133
* Swaps the current access token with a new one based on the provided configuration (with a grant type).
134134
* @param config - Configuration for the token exchange request.
135135
* @returns A promise that resolves to the token response or the raw response.
136136
*/
137-
exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;
137+
exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response | undefined>;
138138

139139
/**
140140
* Re-initializes the client with a new configuration.

0 commit comments

Comments
 (0)