Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow all oauth2 authorize params to be passed while using login. #171

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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 packages/core/src/SDKConfig/SDKConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export interface SDKConfig {
*/
scope?: string;

/**
* Additional params passed to loginPath typically `/app/login/`, which redirects to `/oauth2/authorize`. Example of this might be loginParams = [{idp_hint:'44449786-3dff-42a6-aac6-1f1ceecb6c46'}] or any params found at https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/endpoints
*/
authParams?: { [key: string]: any }[];

/**
* The redirect URI for post-logout. Defaults the provided `redirectUri`.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/SDKCore/SDKCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class SDKCore {
clientId: config.clientId,
redirectUri: config.redirectUri,
scope: config.scope,
authParams: config.authParams,
mePath: config.mePath,
loginPath: config.loginPath,
registerPath: config.registerPath,
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/UrlHelper/UrlHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('UrlHelper', () => {
clientId: 'abc123',
redirectUri: 'http://my-client',
scope: 'openid email profile offline_access',
authParams: [{ idp_hint: '44449786-3dff-42a6-aac6-1f1ceecb6c46' }],
postLogoutRedirectUri: 'http://example.com',
};

Expand All @@ -31,6 +32,16 @@ describe('UrlHelper', () => {
expect(loginUrl.searchParams.get('state')).toBe(stateValue);
});

it('login url authParams', () => {
const stateValue = 'login-state-value';
const loginUrl = urlHelper.getLoginUrl(stateValue);
expect(loginUrl.origin).toBe(config.serverUrl);
expect(loginUrl.pathname).toBe('/app/login/');
expect(loginUrl.searchParams.get('idp_hint')).toBe(
config.authParams?.at(0)?.idp_hint,
);
});

it('register url', () => {
const stateValue = 'register-state-value';
const registerUrl = urlHelper.getRegisterUrl(stateValue);
Expand Down
21 changes: 17 additions & 4 deletions packages/core/src/UrlHelper/UrlHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export class UrlHelper {
clientId: string;
redirectUri: string;
scope?: string;
authParams?: { [key: string]: any }[];

mePath: string;
loginPath: string;
Expand All @@ -19,6 +20,7 @@ export class UrlHelper {
this.clientId = config.clientId;
this.redirectUri = config.redirectUri;
this.scope = config.scope;
this.authParams = config.authParams;
this.postLogoutRedirectUri = config.postLogoutRedirectUri;

this.mePath = config.mePath ?? '/app/me/';
Expand All @@ -37,6 +39,7 @@ export class UrlHelper {
client_id: this.clientId,
redirect_uri: this.redirectUri,
scope: this.scope,
authParams: this.authParams,
state,
});
}
Expand All @@ -46,6 +49,7 @@ export class UrlHelper {
client_id: this.clientId,
redirect_uri: this.redirectUri,
scope: this.scope,
authParams: this.authParams,
state,
});
}
Expand Down Expand Up @@ -85,13 +89,22 @@ export class UrlHelper {
params: UrlHelperQueryParams,
): URLSearchParams {
const urlSearchParams = new URLSearchParams();
this.appendParams(params, urlSearchParams);
return urlSearchParams;
}

private appendParams(
params: Record<string, any>, // or a more specific type if known
urlSearchParams: URLSearchParams,
): void {
Object.entries(params).forEach(([key, value]) => {
if (value) {
urlSearchParams.append(key, value);
if ((value && typeof value === 'object') || Array.isArray(value)) {
// Recursively handle nested objects
this.appendParams(value, urlSearchParams);
} else if (value !== undefined && value !== null) {
// Append primitive values
urlSearchParams.append(key, String(value));
}
});

return urlSearchParams;
}
}
2 changes: 2 additions & 0 deletions packages/core/src/UrlHelper/UrlHelperTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type UrlHelperConfig = Pick<
| 'logoutPath'
| 'tokenRefreshPath'
| 'scope'
| 'authParams'
| 'postLogoutRedirectUri'
>;

Expand All @@ -21,5 +22,6 @@ export type UrlHelperQueryParams = {
redirect_uri?: string;
post_logout_redirect_uri?: string;
scope?: string;
authParams?: { [key: string]: any }[];
state?: string;
};
4 changes: 4 additions & 0 deletions packages/sdk-react/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
fusionauth-react-sdk Changes

Changes in 2.5.0

- Allows for available paramaters to be passed for the `/oauth2/authorize` redirect that occurs after the hosted backend login from `/app/login`. Addresses issue [164](https://github.com/FusionAuth/fusionauth-javascript-sdk/issues/164)

Changes in 2.4.3

- Correctly address issues with config and `isLoggedIn` in [169](https://github.com/FusionAuth/fusionauth-javascript-sdk/pull/169)
Expand Down
4 changes: 2 additions & 2 deletions packages/sdk-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fusionauth/react-sdk",
"version": "2.4.3",
"version": "2.5.0",
"private": false,
"description": "FusionAuth solves the problem of building essential security without adding risk or distracting from your primary application",
"type": "module",
Expand Down Expand Up @@ -63,4 +63,4 @@
"vite-plugin-dts": "^3.7.3",
"vitest": "^1.3.1"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
removeAt_expCookie,
mockWindowLocation,
} from '@fusionauth-sdk/core';
import { TEST_CONFIG } from '#testing-tools/mocks/testConfig';
import {
TEST_CONFIG,
TEST_AUTHPARAM_CONFIG,
} from '#testing-tools/mocks/testConfig';

function renderWithWrapper<T = UserInfo>(config: FusionAuthProviderConfig) {
return renderHook(() => useFusionAuth<T>(), {
Expand Down Expand Up @@ -49,6 +52,31 @@ describe('FusionAuthProvider', () => {
expect(mockedLocation.assign).toHaveBeenCalledWith(expectedUrl);
});

test('Redirects to the correct login url with idp_hint', () => {
const mockedLocation = mockWindowLocation(vi);

const { result } = renderWithWrapper(TEST_AUTHPARAM_CONFIG);

const stateValue = 'state-value';
result.current.startLogin(stateValue);

const expectedUrl = new URL(TEST_AUTHPARAM_CONFIG.serverUrl);
expectedUrl.pathname = '/app/login/';
expectedUrl.searchParams.set('client_id', TEST_AUTHPARAM_CONFIG.clientId);
expectedUrl.searchParams.set(
'redirect_uri',
TEST_AUTHPARAM_CONFIG.redirectUri,
);
expectedUrl.searchParams.set('scope', TEST_AUTHPARAM_CONFIG.scope!);
expectedUrl.searchParams.set(
'idp_hint',
TEST_AUTHPARAM_CONFIG?.authParams?.at(0)?.idp_hint,
);
expectedUrl.searchParams.set('state', stateValue);

expect(mockedLocation.assign).toHaveBeenCalledWith(expectedUrl);
});

test('Redirects to the correct logout url', () => {
const mockedLocation = mockWindowLocation(vi);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function FusionAuthProvider<T = DefaultUserInfo>(
clientId: props.clientId,
redirectUri: props.redirectUri,
scope: props.scope,
authParams: props.authParams,
postLogoutRedirectUri: props.postLogoutRedirectUri,
shouldAutoRefresh: props.shouldAutoRefresh,
shouldAutoFetchUserInfo: props.shouldAutoFetchUserInfo,
Expand All @@ -46,6 +47,7 @@ function FusionAuthProvider<T = DefaultUserInfo>(
props.clientId,
props.redirectUri,
props.scope,
props.authParams,
props.postLogoutRedirectUri,
props.shouldAutoRefresh,
props.shouldAutoFetchUserInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export interface FusionAuthProviderConfig {
*/
scope?: string;

/**
* Additional params passed to loginPath typically `/app/login/`, which redirects to `/oauth2/authorize`. Example of this might be loginParams = [{idp_hint:'44449786-3dff-42a6-aac6-1f1ceecb6c46'}] or any params found at https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/endpoints
*/
authParams?: { [key: string]: any }[];

/**
* The redirect URI for post-logout. Defaults the provided `redirectUri`.
*/
Expand Down
9 changes: 9 additions & 0 deletions packages/sdk-react/src/testing-tools/mocks/testConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ export const TEST_CONFIG: FusionAuthProviderConfig = {
scope: 'openid email profile offline_access',
postLogoutRedirectUri: 'http://localhost',
};

export const TEST_AUTHPARAM_CONFIG: FusionAuthProviderConfig = {
clientId: '85a03867-dccf-4882-adde-1a79aeec50df',
serverUrl: 'http://localhost:9000',
redirectUri: 'http://localhost',
scope: 'openid email profile offline_access',
authParams: [{ idp_hint: '44449786-3dff-42a6-aac6-1f1ceecb6c46' }],
postLogoutRedirectUri: 'http://localhost',
};
Loading