Skip to content

Commit 64f153b

Browse files
authored
Merge pull request #218 from brionmario/thunder
fix: protected route should work without any props
2 parents 16a735d + 2bf7be4 commit 64f153b

File tree

5 files changed

+112
-9
lines changed

5 files changed

+112
-9
lines changed

.changeset/huge-beds-push.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@asgardeo/react-router': patch
3+
'@asgardeo/react': patch
4+
---
5+
6+
`ProtectedRoute` should work without any props

packages/react-router/src/components/ProtectedRoute.tsx

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {AsgardeoRuntimeError} from '@asgardeo/browser';2025, WSO2 LLC. (https://
2121

2222
import {FC, ReactElement, ReactNode} from 'react';
2323
import {Navigate} from 'react-router';
24-
import {useAsgardeo, AsgardeoRuntimeError} from '@asgardeo/react';
24+
import {useAsgardeo, AsgardeoRuntimeError, navigate} from '@asgardeo/react';
2525

2626
/**
2727
* Props for the ProtectedRoute component.
@@ -45,6 +45,37 @@ export interface ProtectedRouteProps {
4545
* Custom loading element to render while authentication status is being determined.
4646
*/
4747
loader?: ReactNode;
48+
/**
49+
* Custom sign-in function to override the default behavior.
50+
* If provided, this function will be called instead of the default signIn method
51+
* when the user is not authenticated and no fallback or redirectTo is specified.
52+
* This allows you to pass additional parameters or implement custom sign-in logic.
53+
*
54+
* @param defaultSignIn - The default signIn method from useAsgardeo hook
55+
* @param signInOptions - Merged sign-in options (context + component props)
56+
*/
57+
onSignIn?: (defaultSignIn: (options?: Record<string, any>) => void, signInOptions?: Record<string, any>) => void;
58+
/**
59+
* Additional parameters to pass to the authorize request.
60+
* These will be merged with the default signInOptions from the Asgardeo context.
61+
* Common options include:
62+
* - prompt: "login" | "none" | "consent" | "select_account"
63+
* - fidp: Federation Identity Provider identifier
64+
* - kc_idp_hint: Keycloak identity provider hint
65+
* - login_hint: Hint to help with the username/identifier in the login form
66+
* - max_age: Maximum authentication age in seconds
67+
* - ui_locales: End-user's preferred languages and scripts for the user interface
68+
*
69+
* @example
70+
* ```tsx
71+
* signInOptions={{
72+
* prompt: "login",
73+
* fidp: "OrganizationSSO",
74+
* login_hint: "user@example.com"
75+
* }}
76+
* ```
77+
*/
78+
signInOptions?: Record<string, any>;
4879
}
4980

5081
/**
@@ -80,9 +111,47 @@ export interface ProtectedRouteProps {
80111
* }
81112
* />
82113
* ```
114+
*
115+
* @example With custom sign-in parameters
116+
* ```tsx
117+
* <Route
118+
* path="/secure"
119+
* element={
120+
* <ProtectedRoute signInOptions={{ prompt: "login", fidp: "OrganizationSSO" }}>
121+
* <SecureContent />
122+
* </ProtectedRoute>
123+
* }
124+
* />
125+
* ```
126+
*
127+
* @example With custom sign-in handler
128+
* ```tsx
129+
* <Route
130+
* path="/custom"
131+
* element={
132+
* <ProtectedRoute
133+
* onSignIn={(defaultSignIn, options) => {
134+
* // Custom logic before sign-in
135+
* console.log('Initiating custom sign-in');
136+
* defaultSignIn({ ...options, prompt: "login" });
137+
* }}
138+
* signInOptions={{ fidp: "CustomIDP" }}
139+
* >
140+
* <CustomContent />
141+
* </ProtectedRoute>
142+
* }
143+
* />
144+
* ```
83145
*/
84-
const ProtectedRoute: FC<ProtectedRouteProps> = ({children, fallback, redirectTo, loader = null}) => {
85-
const {isSignedIn, isLoading} = useAsgardeo();
146+
const ProtectedRoute: FC<ProtectedRouteProps> = ({
147+
children,
148+
fallback,
149+
redirectTo,
150+
loader = null,
151+
onSignIn,
152+
signInOptions: overriddenSignInOptions = {},
153+
}) => {
154+
const {isSignedIn, isLoading, signIn, signInOptions, signInUrl} = useAsgardeo();
86155

87156
// Always wait for loading to finish before making authentication decisions
88157
if (isLoading) {
@@ -101,11 +170,34 @@ const ProtectedRoute: FC<ProtectedRouteProps> = ({children, fallback, redirectTo
101170
return <Navigate to={redirectTo} replace />;
102171
}
103172

173+
if (!isSignedIn) {
174+
if (signInUrl) {
175+
navigate(signInUrl);
176+
} else {
177+
if (onSignIn) {
178+
onSignIn(signIn, overriddenSignInOptions);
179+
} else {
180+
(async () => {
181+
try {
182+
await signIn(overriddenSignInOptions ?? signInOptions);
183+
} catch (error) {
184+
throw new AsgardeoRuntimeError(
185+
'Sign-in failed in ProtectedRoute.',
186+
'ProtectedRoute-SignInError-001',
187+
'react-router',
188+
`An error occurred during sign-in: ${(error as Error).message}`,
189+
);
190+
}
191+
})();
192+
}
193+
}
194+
}
195+
104196
throw new AsgardeoRuntimeError(
105-
'"fallback" or "redirectTo" prop is required.',
106-
'ProtectedRoute-ValidationError-001',
197+
'ProtectedRoute misconfiguration.',
198+
'ProtectedRoute-Misconfiguration-001',
107199
'react-router',
108-
'Either "fallback" or "redirectTo" prop must be provided to handle unauthenticated users.',
200+
'The internal handler failed to process the state. Please try with a fallback or redirectTo prop.',
109201
);
110202
};
111203

packages/react/src/AsgardeoReactClient.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,12 @@ class AsgardeoReactClient<T extends AsgardeoReactConfig = AsgardeoReactConfig> e
335335

336336
const config: AsgardeoReactConfig = (await this.asgardeo.getConfigData()) as AsgardeoReactConfig;
337337

338-
if (config.platform === Platform.AsgardeoV2 && typeof arg1 === 'object' && !isEmpty(arg1)) {
338+
if (
339+
config.platform === Platform.AsgardeoV2 &&
340+
typeof arg1 === 'object' &&
341+
!isEmpty(arg1) &&
342+
('flowId' in arg1 || 'applicationId' in arg1)
343+
) {
339344
const sessionDataKey: string = new URL(window.location.href).searchParams.get('sessionDataKey');
340345

341346
return executeEmbeddedSignInFlowV2({

packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ const AsgardeoProvider: FC<PropsWithChildren<AsgardeoProviderProps>> = ({
257257

258258
// TEMPORARY: Asgardeo V2 platform does not support SCIM2, Organizations endpoints yet.
259259
// Tracker: https://github.com/asgardeo/javascript/issues/212
260-
if (config.platform !== Platform.AsgardeoV2) {
260+
if (config.platform === Platform.AsgardeoV2) {
261261
setUser(extractUserClaimsFromIdToken(decodedToken));
262262
} else {
263263
try {

packages/react/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,4 @@ export {default as updateMeProfile, UpdateMeProfileConfig} from './api/updateMeP
273273
export {default as getMeProfile} from './api/getScim2Me';
274274
export * from './api/getScim2Me';
275275

276-
export {AsgardeoRuntimeError, http, getActiveTheme} from '@asgardeo/browser';
276+
export {AsgardeoRuntimeError, http, getActiveTheme, navigate} from '@asgardeo/browser';

0 commit comments

Comments
 (0)