From 4931737827f3d5d1a3ca48502d0085bc10775098 Mon Sep 17 00:00:00 2001 From: Samil Abud Date: Fri, 23 May 2025 17:47:00 -0400 Subject: [PATCH 1/2] feat(provider): add prop to KindeProvider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Expose an optional in KindeProviderProps - Use override to swap out the default path for custom endpoints (e.g. ) - Preserve all query parameters when building the redirect URL - Include in login() callback’s dependency array - Closes #78 --- src/state/KindeProvider.tsx | 78 ++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/src/state/KindeProvider.tsx b/src/state/KindeProvider.tsx index 3138535..d7eb21b 100644 --- a/src/state/KindeProvider.tsx +++ b/src/state/KindeProvider.tsx @@ -57,12 +57,12 @@ type EventTypes = { ( event: AuthEvent.tokenRefreshed, state: RefreshTokenResult, - context: KindeContextProps, + context: KindeContextProps ): void; ( event: AuthEvent, state: Record, - context: KindeContextProps, + context: KindeContextProps ): void; }; @@ -70,12 +70,12 @@ type KindeCallbacks = { onSuccess?: ( user: UserProfile, state: Record, - context: KindeContextProps, + context: KindeContextProps ) => void; onError?: ( props: ErrorProps, state: Record, - context: KindeContextProps, + context: KindeContextProps ) => void; onEvent?: EventTypes; }; @@ -85,6 +85,7 @@ type KindeProviderProps = { children: React.ReactNode; clientId: string; domain: string; + authorizationEndpoint?: string; /** * Use localstorage for refresh token. * @@ -124,6 +125,7 @@ export const KindeProvider = ({ clientId, children, domain, + authorizationEndpoint, useInsecureForRefreshToken = false, redirectUri, callbacks = {}, @@ -158,7 +160,7 @@ export const KindeProvider = ({ const login = useCallback( async ( - options: LoginMethodParams & { state?: Record } = {}, + options: LoginMethodParams & { state?: Record } = {} ) => { const optionsState: Record = options.state || {}; @@ -173,28 +175,40 @@ export const KindeProvider = ({ JSON.stringify({ kinde: { event: AuthEvent.login }, ...optionsState, - }), + }) ), redirectURL: getRedirectUrl(options.redirectURL || redirectUri), }; const domain = (await storeState.memoryStorage.getSessionItem( - storeState.LocalKeys.domain, + storeState.LocalKeys.domain )) as string; const authUrl = await generateAuthUrl( domain, IssuerRouteTypes.login, - authProps, + authProps ); - document.location = authUrl.url.toString(); + let redirectUrl = ""; + if (authorizationEndpoint) { + // override just the path, keep the query string + const custom = new URL(authUrl.url.toString()); + // ensure it’s a path, not a full URL + custom.pathname = authorizationEndpoint.startsWith("/") + ? authorizationEndpoint + : `/${authorizationEndpoint}`; + redirectUrl = custom.toString(); + } else { + redirectUrl = authUrl.url.toString(); + } + document.location = redirectUrl; }, - [audience, clientId, redirectUri], + [audience, clientId, redirectUri] ); const register = useCallback( async ( - options: LoginMethodParams & { state?: Record } = {}, + options: LoginMethodParams & { state?: Record } = {} ) => { const optionsState: Record = options.state || {}; @@ -206,14 +220,14 @@ export const KindeProvider = ({ JSON.stringify({ kinde: { event: AuthEvent.register }, ...optionsState, - }), + }) ), supportsReauth: true, audience: (await storeState.memoryStorage.getSessionItem( - storeState.LocalKeys.audience, + storeState.LocalKeys.audience )) as string, clientId: (await storeState.memoryStorage.getSessionItem( - storeState.LocalKeys.clientId, + storeState.LocalKeys.clientId )) as string, redirectURL: getRedirectUrl(options?.redirectURL || redirectUri), prompt: PromptTypes.create, @@ -221,13 +235,13 @@ export const KindeProvider = ({ try { const domain = (await storeState.memoryStorage.getSessionItem( - storeState.LocalKeys.domain, + storeState.LocalKeys.domain )) as string; const authUrl = await generateAuthUrl( domain, IssuerRouteTypes.register, - authProps, + authProps ); document.location = authUrl.url.toString(); } catch (error) { @@ -238,17 +252,17 @@ export const KindeProvider = ({ errorDescription: String(error), }, {}, - contextValue, + contextValue ); } }, - [redirectUri], + [redirectUri] ); const logout = useCallback(async (options?: string | LogoutOptions) => { try { const domain = (await storeState.memoryStorage.getSessionItem( - storeState.LocalKeys.domain, + storeState.LocalKeys.domain )) as string; const params = new URLSearchParams(); @@ -286,7 +300,7 @@ export const KindeProvider = ({ errorDescription: String(error), }, {}, - contextValue, + contextValue ); } }, []); @@ -305,19 +319,19 @@ export const KindeProvider = ({ getAccessToken: async (): Promise => { const storage = getActiveStorage(); return (await storage?.getSessionItem( - StorageKeys.accessToken, + StorageKeys.accessToken )) as string; }, /** @deprecated use `getAccessToken` instead */ getToken: async (): Promise => { const storage = getActiveStorage(); return (await storage?.getSessionItem( - StorageKeys.accessToken, + StorageKeys.accessToken )) as string; }, getClaim: async ( - keyName: keyof T, + keyName: keyof T ): Promise<{ name: keyof T; value: V } | null> => { return getClaim(keyName); }, @@ -334,7 +348,7 @@ export const KindeProvider = ({ return await getCurrentOrganization(); }, getFlag: async ( - name: string, + name: string ): Promise => { return await getFlag(name); }, @@ -346,7 +360,7 @@ export const KindeProvider = ({ }, getPermission: async ( - permissionKey: T, + permissionKey: T ): Promise => { return await getPermission(permissionKey); }, @@ -377,7 +391,7 @@ export const KindeProvider = ({ mergedCallbacks.onEvent(AuthEvent.tokenRefreshed, data, contextValue); } }, - [mergedCallbacks, contextValue], + [mergedCallbacks, contextValue] ); const handleFocus = useCallback(() => { @@ -442,7 +456,7 @@ export const KindeProvider = ({ try { returnedState = JSON.parse(decoded); kindeState = Object.assign( - returnedState.kinde || { event: PromptTypes.login }, + returnedState.kinde || { event: PromptTypes.login } ); } catch (error) { console.error("Error parsing state:", error); @@ -452,14 +466,14 @@ export const KindeProvider = ({ errorDescription: String(error), }, {}, - contextValue, + contextValue ); returnedState = {} as StateWithKinde; kindeState = { event: AuthEvent.login }; } try { const redirectURL = (await storeState.memoryStorage.getSessionItem( - storeState.LocalKeys.redirectUri, + storeState.LocalKeys.redirectUri )) as string; const codeResponse = await exchangeAuthCode({ @@ -481,7 +495,7 @@ export const KindeProvider = ({ ...returnedState, kinde: undefined, }, - contextValue, + contextValue ); if (mergedCallbacks.onEvent) { mergedCallbacks.onEvent( @@ -490,7 +504,7 @@ export const KindeProvider = ({ ...returnedState, kinde: undefined, }, - contextValue, + contextValue ); } } @@ -501,7 +515,7 @@ export const KindeProvider = ({ errorDescription: codeResponse.error, }, returnedState, - contextValue, + contextValue ); } } finally { From 2028d3839db5ecfcc96d20f9d829293b6e92b305 Mon Sep 17 00:00:00 2001 From: Samil Abud Date: Fri, 23 May 2025 18:04:01 -0400 Subject: [PATCH 2/2] fix: fixes useEffect dependency regarding to authorizationEndPoint --- src/state/KindeProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/KindeProvider.tsx b/src/state/KindeProvider.tsx index d7eb21b..53b31f7 100644 --- a/src/state/KindeProvider.tsx +++ b/src/state/KindeProvider.tsx @@ -203,7 +203,7 @@ export const KindeProvider = ({ } document.location = redirectUrl; }, - [audience, clientId, redirectUri] + [audience, clientId, redirectUri, authorizationEndpoint] ); const register = useCallback(