Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/nextjs-missing-exports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@asgardeo/nextjs': patch
---

Complete the package's entry points. `@asgardeo/nextjs` now exports the `Loading` and `OrganizationList` components, and re-exports the hooks of the React SDK (`useUser`, `useOrganization`, `useTranslation`, `useTheme`, `useBrandingContext`, `useBranding`, `useFlow`, `useI18n`, `useForm`) so applications do not need to depend on `@asgardeo/react` themselves. `@asgardeo/nextjs/server` exports the server actions (`clearSession`, `isSignedIn`, `getSessionPayload`, `getUser`, `getUserProfile`, `httpRequest`, `signOut`, `switchOrganization`, the organization actions and more) for Server Components and Route Handlers, and the `asgardeo()` helper gained `isSignedIn`, `getSession`, `getUser`, `getUserProfile`, `signOut` and `clearSession`.
88 changes: 88 additions & 0 deletions packages/nextjs/src/__tests__/exports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* 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.
*/

import {describe, expect, it, vi} from 'vitest';
import * as clientExports from '../client';
import * as serverExports from '../server';

vi.mock('next/headers', () => ({cookies: vi.fn(), headers: vi.fn()}));
vi.mock('next/navigation', () => ({useRouter: vi.fn(), useSearchParams: vi.fn()}));

describe('package entry points', () => {
it('exports every client component and the hooks of the React SDK', () => {
const expected: string[] = [
'CreateOrganization',
'Loading',
'Organization',
'OrganizationList',
'OrganizationProfile',
'OrganizationSwitcher',
'SignIn',
'SignInButton',
'SignOutButton',
'SignUp',
'SignUpButton',
'SignedIn',
'SignedOut',
'User',
'UserDropdown',
'UserProfile',
'useAsgardeo',
'useBranding',
'useBrandingContext',
'useFlow',
'useForm',
'useI18n',
'useOrganization',
'useTheme',
'useTranslation',
'useUser',
];

expected.forEach((name: string) => {
expect(typeof (clientExports as Record<string, unknown>)[name], name).toMatch(/function|object/);
});
});

it('exports the server helper, the provider and the server actions', () => {
const expected: string[] = [
'AsgardeoProvider',
'asgardeo',
'clearSession',
'createOrganization',
'getAccessToken',
'getAllOrganizations',
'getMyOrganizations',
'getOrganization',
'getSessionId',
'getSessionPayload',
'getUser',
'getUserProfile',
'httpRequest',
'isSignedIn',
'refreshToken',
'signOut',
'switchOrganization',
'updateUserProfile',
];

expected.forEach((name: string) => {
expect(typeof (serverExports as Record<string, unknown>)[name], name).toBe('function');
});
});
});
23 changes: 23 additions & 0 deletions packages/nextjs/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@

export {default as useAsgardeo} from './contexts/Asgardeo/useAsgardeo';

// Hooks of the underlying React SDK, re-exported so applications do not have to depend on `@asgardeo/react`
// themselves (a second copy of that package would come with its own, unrelated, React contexts).
export {
useBranding,
useBrandingContext,
useFlow,
useForm,
useI18n,
useOrganization,
useTheme,
useTranslation,
useUser,
} from '@asgardeo/react';

export {default as Loading} from './components/control/Loading/Loading';
export type {LoadingProps} from './components/control/Loading/Loading';

export {default as OrganizationList} from './components/presentation/OrganizationList/OrganizationList';
export type {
OrganizationListConfig,
OrganizationListProps,
} from './components/presentation/OrganizationList/OrganizationList';

export {default as Organization} from './components/presentation/Organization/Organization';
export {OrganizationProps} from './components/presentation/Organization/Organization';

Expand Down
62 changes: 61 additions & 1 deletion packages/nextjs/src/server/asgardeo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,51 @@
* under the License.
*/

import {TokenExchangeRequestConfig, TokenResponse} from '@asgardeo/node';
import {TokenExchangeRequestConfig, TokenResponse, User, UserProfile} from '@asgardeo/node';
import clearSessionAction from './actions/clearSession';
import getSessionIdAction from './actions/getSessionId';
import getSessionPayloadAction from './actions/getSessionPayload';
import isSignedInAction from './actions/isSignedIn';
import signOutAction from './actions/signOutAction';
import AsgardeoNextClient from '../AsgardeoNextClient';
import {AsgardeoNextConfig} from '../models/config';
import {SessionTokenPayload} from '../utils/SessionManager';

/**
* Server-side helper for Server Components, Route Handlers and Server Actions.
*
* @example
* ```ts
* import {asgardeo} from '@asgardeo/nextjs/server';
*
* export default async function Page() {
* const {isSignedIn, getUser} = await asgardeo();
*
* if (!(await isSignedIn())) {
* redirect('/signin');
* }
*
* const user = await getUser();
* ...
* }
* ```
*/
const asgardeo = async (): Promise<{
/** Deletes the session cookies without contacting the identity server. */
clearSession: () => Promise<void>;
exchangeToken: (config: TokenExchangeRequestConfig, sessionId: string) => Promise<TokenResponse | Response>;
getAccessToken: (sessionId: string) => Promise<string>;
/** The verified session cookie payload, or `undefined` when there is no valid session. */
getSession: () => Promise<SessionTokenPayload | undefined>;
getSessionId: () => Promise<string | undefined>;
/** The signed-in user (SCIM2 profile, falling back to the ID token claims). */
getUser: (sessionId?: string) => Promise<User>;
/** The signed-in user's profile with its schemas. */
getUserProfile: (sessionId?: string) => Promise<UserProfile>;
isSignedIn: (sessionId?: string) => Promise<boolean>;
reInitialize: (config: Partial<AsgardeoNextConfig>) => Promise<boolean>;
/** Signs the user out: clears the session cookies and resolves the identity server's logout URL. */
signOut: () => Promise<{data?: {afterSignOutUrl?: string}; error?: unknown; success: boolean}>;
}> => {
const getAccessToken = async (sessionId: string): Promise<string> => {
const client: AsgardeoNextClient = AsgardeoNextClient.getInstance();
Expand All @@ -34,6 +69,25 @@ const asgardeo = async (): Promise<{

const getSessionId = async (): Promise<string | undefined> => getSessionIdAction();

const getSession = async (): Promise<SessionTokenPayload | undefined> => getSessionPayloadAction();

const isSignedIn = async (sessionId?: string): Promise<boolean> => isSignedInAction(sessionId);

const getUser = async (sessionId?: string): Promise<User> => {
const client: AsgardeoNextClient = AsgardeoNextClient.getInstance();
return client.getUser(sessionId);
};

const getUserProfile = async (sessionId?: string): Promise<UserProfile> => {
const client: AsgardeoNextClient = AsgardeoNextClient.getInstance();
return client.getUserProfile(sessionId);
};

const signOut = async (): Promise<{data?: {afterSignOutUrl?: string}; error?: unknown; success: boolean}> =>
signOutAction();

const clearSession = async (): Promise<void> => clearSessionAction();

const exchangeToken = async (
config: TokenExchangeRequestConfig,
sessionId: string,
Expand All @@ -48,10 +102,16 @@ const asgardeo = async (): Promise<{
};

return {
clearSession,
exchangeToken,
getAccessToken,
getSession,
getSessionId,
getUser,
getUserProfile,
isSignedIn,
reInitialize,
signOut,
};
};

Expand Down
21 changes: 21 additions & 0 deletions packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,24 @@ export {default as asgardeo} from './asgardeo';

export {default as AsgardeoProvider} from './AsgardeoProvider.js';
export * from './AsgardeoProvider.js';

// Server actions, for use in Server Components, Route Handlers and other Server Actions.
export {default as clearSession} from './actions/clearSession';
export {default as createOrganization} from './actions/createOrganization';
export {default as getAccessToken} from './actions/getAccessToken';
export {default as getAllOrganizations} from './actions/getAllOrganizations';
export {default as getMyOrganizations} from './actions/getMyOrganizations';
export {default as getOrganization} from './actions/getOrganizationAction';
export {default as getSessionId} from './actions/getSessionId';
export {default as getSessionPayload} from './actions/getSessionPayload';
export {default as getUser} from './actions/getUserAction';
export {default as getUserProfile} from './actions/getUserProfileAction';
export {default as httpRequest} from './actions/httpRequestAction';
export type {HttpRequestActionResult} from './actions/httpRequestAction';
export {default as isSignedIn} from './actions/isSignedIn';
export {default as refreshToken} from './actions/refreshToken';
export type {RefreshResult} from './actions/refreshToken';
export {default as signOut} from './actions/signOutAction';
export {default as switchOrganization} from './actions/switchOrganization';
export {default as updateUserProfile} from './actions/updateUserProfileAction';
export type {SessionTokenPayload} from '../utils/SessionManager';
Loading