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/early-boxes-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@asgardeo/nextjs': patch
'@asgardeo/react': patch
---

Fix issues in next components
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@

'use client';

import {FC, ReactElement, useState} from 'react';

import {CreateOrganizationPayload, AsgardeoRuntimeError} from '@asgardeo/node';
import {BaseCreateOrganization, BaseCreateOrganizationProps, useOrganization} from '@asgardeo/react';
import {CreateOrganizationPayload} from '@asgardeo/node';
import useAsgardeo from '../../../contexts/Asgardeo/useAsgardeo';
import createOrganizationAction from '../../../../server/actions/createOrganizationAction';
import {FC, ReactElement, useState} from 'react';
import getSessionId from '../../../../server/actions/getSessionId';
import useAsgardeo from '../../../contexts/Asgardeo/useAsgardeo';

/**
* Props interface for the CreateOrganization component.
Expand Down Expand Up @@ -80,7 +78,7 @@ export const CreateOrganization: FC<CreateOrganizationProps> = ({
...props
}: CreateOrganizationProps): ReactElement => {
const {isSignedIn, baseUrl} = useAsgardeo();
const {currentOrganization, revalidateMyOrganizations} = useOrganization();
const {currentOrganization, revalidateMyOrganizations, createOrganization} = useOrganization();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

Expand All @@ -104,14 +102,22 @@ export const CreateOrganization: FC<CreateOrganizationProps> = ({
let result: any;

if (onCreateOrganization) {
// Use the provided custom creation function
result = await onCreateOrganization(payload);
} else {
// Use the default API
if (!baseUrl) {
throw new Error('Base URL is required for organization creation');
}
result = await createOrganizationAction(

if (!createOrganization) {
throw new AsgardeoRuntimeError(
`createOrganization function is not available.`,
'CreateOrganization-handleSubmit-RuntimeError-001',
'nextjs',
'The createOrganization function must be provided by the Organization context.',
);
}

result = await createOrganization(
{
...payload,
parentId,
Expand All @@ -121,7 +127,9 @@ export const CreateOrganization: FC<CreateOrganizationProps> = ({
}

// Refresh organizations list to include the new organization
await revalidateMyOrganizations();
if (revalidateMyOrganizations) {
await revalidateMyOrganizations();
}

// Call success callback if provided
if (onSuccess) {
Expand Down
38 changes: 21 additions & 17 deletions packages/nextjs/src/client/contexts/Asgardeo/AsgardeoProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import {
AllOrganizationsApiResponse,
AsgardeoRuntimeError,
EmbeddedFlowExecuteRequestConfig,
EmbeddedFlowExecuteRequestPayload,
EmbeddedSignInFlowHandleRequestPayload,
Expand All @@ -30,6 +29,8 @@ import {
User,
UserProfile,
BrandingPreference,
TokenResponse,
CreateOrganizationPayload,
} from '@asgardeo/node';
import {
I18nProvider,
Expand All @@ -40,38 +41,39 @@ import {
OrganizationProvider,
BrandingProvider,
} from '@asgardeo/react';
import {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react';
import {useRouter, useSearchParams} from 'next/navigation';
import {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react';
import AsgardeoContext, {AsgardeoContextProps} from './AsgardeoContext';

/**
* Props interface of {@link AsgardeoClientProvider}
*/
export type AsgardeoClientProviderProps = Partial<Omit<AsgardeoProviderProps, 'baseUrl' | 'clientId'>> &
Pick<AsgardeoProviderProps, 'baseUrl' | 'clientId'> & {
signOut: AsgardeoContextProps['signOut'];
signIn: AsgardeoContextProps['signIn'];
signUp: AsgardeoContextProps['signUp'];
applicationId: AsgardeoContextProps['applicationId'];
organizationHandle: AsgardeoContextProps['organizationHandle'];
brandingPreference?: BrandingPreference | null;
createOrganization: (payload: CreateOrganizationPayload, sessionId: string) => Promise<Organization>;
currentOrganization: Organization;
getAllOrganizations: (options?: any, sessionId?: string) => Promise<AllOrganizationsApiResponse>;
handleOAuthCallback: (
code: string,
state: string,
sessionState?: string,
) => Promise<{success: boolean; error?: string; redirectUrl?: string}>;
) => Promise<{error?: string; redirectUrl?: string; success: boolean}>;
isSignedIn: boolean;
userProfile: UserProfile;
currentOrganization: Organization;
user: User | null;
myOrganizations: Organization[];
organizationHandle: AsgardeoContextProps['organizationHandle'];
revalidateMyOrganizations?: (sessionId?: string) => Promise<Organization[]>;
signIn: AsgardeoContextProps['signIn'];
signOut: AsgardeoContextProps['signOut'];
signUp: AsgardeoContextProps['signUp'];
switchOrganization: (organization: Organization, sessionId?: string) => Promise<TokenResponse | Response>;
updateProfile: (
requestConfig: UpdateMeProfileConfig,
sessionId?: string,
) => Promise<{success: boolean; data: {user: User}; error: string}>;
getAllOrganizations: (options?: any, sessionId?: string) => Promise<AllOrganizationsApiResponse>;
myOrganizations: Organization[];
revalidateMyOrganizations?: (sessionId?: string) => Promise<Organization[]>;
brandingPreference?: BrandingPreference | null;
switchOrganization: (organization: Organization, sessionId?: string) => Promise<void>;
) => Promise<{data: {user: User}; error: string; success: boolean}>;
user: User | null;
userProfile: UserProfile;
};

const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>> = ({
Expand All @@ -81,6 +83,7 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
signOut,
signUp,
handleOAuthCallback,
createOrganization,
preferences,
isSignedIn,
signInUrl,
Expand Down Expand Up @@ -297,10 +300,11 @@ const AsgardeoClientProvider: FC<PropsWithChildren<AsgardeoClientProviderProps>>
<FlowProvider>
<UserProvider profile={userProfile} onUpdateProfile={handleProfileUpdate} updateProfile={updateProfile}>
<OrganizationProvider
createOrganization={createOrganization}
getAllOrganizations={getAllOrganizations}
myOrganizations={myOrganizations}
currentOrganization={currentOrganization}
onOrganizationSwitch={switchOrganization}
onOrganizationSwitch={switchOrganization as any}
revalidateMyOrganizations={revalidateMyOrganizations as any}
>
{children}
Expand Down
64 changes: 19 additions & 45 deletions packages/nextjs/src/server/AsgardeoProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,27 @@

'use server';

import {BrandingPreference, AsgardeoRuntimeError, Organization, User, UserProfile} from '@asgardeo/node';
import {AsgardeoProviderProps} from '@asgardeo/react';
import {FC, PropsWithChildren, ReactElement} from 'react';
import {
BrandingPreference,
AllOrganizationsApiResponse,
AsgardeoRuntimeError,
Organization,
User,
UserProfile,
IdToken,
} from '@asgardeo/node';
import AsgardeoClientProvider from '../client/contexts/Asgardeo/AsgardeoProvider';
import AsgardeoNextClient from '../AsgardeoNextClient';
import signInAction from './actions/signInAction';
import signOutAction from './actions/signOutAction';
import {AsgardeoNextConfig} from '../models/config';
import isSignedIn from './actions/isSignedIn';
import getUserAction from './actions/getUserAction';
import createOrganization from './actions/createOrganization';
import getAllOrganizations from './actions/getAllOrganizations';
import getBrandingPreference from './actions/getBrandingPreference';
import getCurrentOrganizationAction from './actions/getCurrentOrganizationAction';
import getMyOrganizations from './actions/getMyOrganizations';
import getSessionId from './actions/getSessionId';
import getUserAction from './actions/getUserAction';
import getUserProfileAction from './actions/getUserProfileAction';
import signUpAction from './actions/signUpAction';
import handleOAuthCallbackAction from './actions/handleOAuthCallbackAction';
import {AsgardeoProviderProps} from '@asgardeo/react';
import getCurrentOrganizationAction from './actions/getCurrentOrganizationAction';
import updateUserProfileAction from './actions/updateUserProfileAction';
import getMyOrganizations from './actions/getMyOrganizations';
import getAllOrganizations from './actions/getAllOrganizations';
import getBrandingPreference from './actions/getBrandingPreference';
import isSignedIn from './actions/isSignedIn';
import signInAction from './actions/signInAction';
import signOutAction from './actions/signOutAction';
import signUpAction from './actions/signUpAction';
import switchOrganization from './actions/switchOrganization';
import updateUserProfileAction from './actions/updateUserProfileAction';
import AsgardeoNextClient from '../AsgardeoNextClient';
import AsgardeoClientProvider from '../client/contexts/Asgardeo/AsgardeoProvider';
import {AsgardeoNextConfig} from '../models/config';

/**
* Props interface of {@link AsgardeoServerProvider}
Expand Down Expand Up @@ -150,26 +143,6 @@ const AsgardeoServerProvider: FC<PropsWithChildren<AsgardeoServerProviderProps>>
}
}

const handleGetAllOrganizations = async (
options?: any,
_sessionId?: string,
): Promise<AllOrganizationsApiResponse> => {
'use server';
return await getAllOrganizations(options, sessionId);
};

const handleSwitchOrganization = async (organization: Organization, _sessionId?: string): Promise<void> => {
'use server';
await switchOrganization(organization, sessionId);

// After switching organization, we need to refresh the page to get updated session data
// This is because server components don't maintain state between function calls
const {revalidatePath} = await import('next/cache');

// Revalidate the current path to refresh the component with new data
revalidatePath('/');
};

return (
<AsgardeoClientProvider
organizationHandle={config?.organizationHandle}
Expand All @@ -189,9 +162,10 @@ const AsgardeoServerProvider: FC<PropsWithChildren<AsgardeoServerProviderProps>>
updateProfile={updateUserProfileAction}
isSignedIn={_isSignedIn}
myOrganizations={myOrganizations}
getAllOrganizations={handleGetAllOrganizations}
switchOrganization={handleSwitchOrganization}
getAllOrganizations={getAllOrganizations}
switchOrganization={switchOrganization}
brandingPreference={brandingPreference}
createOrganization={createOrganization}
>
{children}
</AsgardeoClientProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,25 @@

'use server';

import {CreateOrganizationPayload, Organization} from '@asgardeo/node';
import {CreateOrganizationPayload, Organization, AsgardeoAPIError} from '@asgardeo/node';
import getSessionId from './getSessionId';
import AsgardeoNextClient from '../../AsgardeoNextClient';

/**
* Server action to create an organization.
*/
const createOrganizationAction = async (payload: CreateOrganizationPayload, sessionId: string) => {
const createOrganization = async (payload: CreateOrganizationPayload, sessionId: string): Promise<Organization> => {
try {
const client = AsgardeoNextClient.getInstance();
const organization: Organization = await client.createOrganization(payload, sessionId);
return {success: true, data: {organization}, error: null};
const client: AsgardeoNextClient = AsgardeoNextClient.getInstance();
return await client.createOrganization(payload, sessionId ?? ((await getSessionId()) as string));
} catch (error) {
return {
success: false,
data: {
user: {},
},
error: 'Failed to create organization',
};
throw new AsgardeoAPIError(
`Failed to create the organization: ${error instanceof Error ? error.message : String(error)}`,
'createOrganization-ServerActionError-001',
'nextjs',
error instanceof AsgardeoAPIError ? error.statusCode : undefined,
);
}
};

export default createOrganizationAction;
export default createOrganization;
12 changes: 8 additions & 4 deletions packages/nextjs/src/server/actions/getAllOrganizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@

'use server';

import {AllOrganizationsApiResponse, AsgardeoAPIError, Organization} from '@asgardeo/node';
import {AllOrganizationsApiResponse, AsgardeoAPIError} from '@asgardeo/node';
import getSessionId from './getSessionId';
import AsgardeoNextClient from '../../AsgardeoNextClient';

/**
* Server action to get organizations.
*/
const getAllOrganizations = async (options?: any, sessionId?: string | undefined): Promise<AllOrganizationsApiResponse> => {
const getAllOrganizations = async (
options?: any,
sessionId?: string | undefined,
): Promise<AllOrganizationsApiResponse> => {
try {
const client = AsgardeoNextClient.getInstance();
return client.getAllOrganizations(options, sessionId);
const client: AsgardeoNextClient = AsgardeoNextClient.getInstance();
return await client.getAllOrganizations(options, sessionId ?? ((await getSessionId()) as string));
} catch (error) {
throw new AsgardeoAPIError(
`Failed to get all the organizations for the user: ${error instanceof Error ? error.message : String(error)}`,
Expand Down
28 changes: 21 additions & 7 deletions packages/nextjs/src/server/actions/switchOrganization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,35 @@

'use server';

import {Organization, AsgardeoAPIError, AsgardeoRuntimeError, TokenResponse} from '@asgardeo/node';
import {Organization, AsgardeoAPIError, TokenResponse} from '@asgardeo/node';
import getSessionId from './getSessionId';
import AsgardeoNextClient from '../../AsgardeoNextClient';

/**
* Server action to switch organization.
*/
const switchOrganization = async (organization: Organization, sessionId: string): Promise<TokenResponse | Response> => {
const switchOrganization = async (
organization: Organization,
sessionId: string | undefined,
): Promise<TokenResponse | Response> => {
try {
const client = AsgardeoNextClient.getInstance();
return await client.switchOrganization(organization, sessionId);
const client: AsgardeoNextClient = AsgardeoNextClient.getInstance();
const response: TokenResponse | Response = await client.switchOrganization(
organization,
sessionId ?? ((await getSessionId()) as string),
);

// After switching organization, we need to refresh the page to get updated session data
// This is because server components don't maintain state between function calls
const {revalidatePath} = await import('next/cache');

// Revalidate the current path to refresh the component with new data
revalidatePath('/');

return response;
} catch (error) {
throw new AsgardeoAPIError(
`Failed to switch the organizations: ${
error instanceof AsgardeoRuntimeError ? error.message : error instanceof Error ? error.message : String(error)
}`,
`Failed to switch the organizations: ${error instanceof Error ? error.message : String(error)}`,
'switchOrganization-ServerActionError-001',
'nextjs',
error instanceof AsgardeoAPIError ? error.statusCode : undefined,
Expand Down
Loading
Loading