Support custom start URL search parameters in OIDC provider
Signed-off-by: Chris Kilding <56678532+chriskilding-relx@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend-module-oidc-provider': minor
|
||||
---
|
||||
|
||||
Added support for custom start URL search parameters (with the new `startUrlSearchParams` config property)
|
||||
@@ -241,6 +241,12 @@ These parameters have implicit default values. Don't override them unless you kn
|
||||
- `prompt`: Recommended to use `auto` so the browser will request sign-in to the IDP if the
|
||||
user has no active session.
|
||||
- `sessionDuration`: Lifespan of the user session.
|
||||
- `startUrlSearchParams`: This is a dictionary of search (query) parameters for the OIDC
|
||||
authorization start URL. Don't define it unless you want to change the identity
|
||||
provider's behavior. (For example, you could set the `organization` parameter to guide
|
||||
users towards a particular sign-in option that your organization prefers.) **Note:** the
|
||||
start URL is controlled by the browser, so this feature is only for improving the
|
||||
Backstage user experience.
|
||||
|
||||
:::note Config Reloading
|
||||
Backstage does not yet support hot reloading of auth provider configuration. Any changes to this YAML file require a restart of Backstage.
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface Config {
|
||||
additionalScopes?: string | string[];
|
||||
prompt?: string;
|
||||
timeout?: HumanDuration | string;
|
||||
startUrlSearchParams?: [string: string];
|
||||
signIn?: {
|
||||
resolvers: Array<
|
||||
| {
|
||||
|
||||
@@ -25,6 +25,7 @@ export const oidcAuthenticator: OAuthAuthenticator<
|
||||
client: BaseClient;
|
||||
strategy: Strategy<OidcAuthResult, BaseClient>;
|
||||
}>;
|
||||
searchParams: Record<string, string>;
|
||||
},
|
||||
OidcAuthResult
|
||||
>;
|
||||
|
||||
@@ -279,6 +279,62 @@ describe('oidcAuthenticator', () => {
|
||||
expect(searchParams.get('response_type')).toBe('code');
|
||||
});
|
||||
|
||||
it('passes custom start URL search parameters', async () => {
|
||||
const customImplementation = oidcAuthenticator.initialize({
|
||||
callbackUrl: 'https://backstage.test/callback',
|
||||
config: new ConfigReader({
|
||||
metadataUrl: 'https://oidc.test/.well-known/openid-configuration',
|
||||
clientId: 'clientId123',
|
||||
clientSecret: 'clientSecret',
|
||||
startUrlSearchParams: {
|
||||
foo: '1',
|
||||
bar: '2',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const startResponse = await oidcAuthenticator.start(
|
||||
startRequest,
|
||||
customImplementation,
|
||||
);
|
||||
|
||||
const { searchParams } = new URL(startResponse.url);
|
||||
|
||||
expect(searchParams.get('foo')).toBe('1');
|
||||
expect(searchParams.get('bar')).toBe('2');
|
||||
});
|
||||
|
||||
it('does not override the core start URL search parameters with custom ones', async () => {
|
||||
const customImplementation = oidcAuthenticator.initialize({
|
||||
callbackUrl: 'https://backstage.test/callback',
|
||||
config: new ConfigReader({
|
||||
metadataUrl: 'https://oidc.test/.well-known/openid-configuration',
|
||||
clientId: 'clientId123',
|
||||
clientSecret: 'clientSecret',
|
||||
startUrlSearchParams: {
|
||||
foo: '1',
|
||||
prompt: 'customPrompt',
|
||||
scope: 'customScope',
|
||||
state: 'customState',
|
||||
nonce: 'customNonce',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const startResponse = await oidcAuthenticator.start(
|
||||
startRequest,
|
||||
customImplementation,
|
||||
);
|
||||
|
||||
const { searchParams } = new URL(startResponse.url);
|
||||
|
||||
expect(searchParams.get('foo')).toBe('1');
|
||||
expect(searchParams.get('scope')).not.toBe('customScope');
|
||||
expect(searchParams.get('state')).not.toBe('customState');
|
||||
expect(searchParams.get('nonce')).not.toBe('customNonce');
|
||||
expect(searchParams.get('prompt')).not.toBe('customPrompt');
|
||||
});
|
||||
|
||||
it('passes a nonce', async () => {
|
||||
const startResponse = await oidcAuthenticator.start(
|
||||
startRequest,
|
||||
|
||||
@@ -83,6 +83,9 @@ export const oidcAuthenticator = createOAuthAuthenticator({
|
||||
);
|
||||
const initializedPrompt = config.getOptionalString('prompt');
|
||||
|
||||
const startUrlSearchParams: Record<string, string> =
|
||||
config.getOptional('startUrlSearchParams') || {};
|
||||
|
||||
if (config.has('scope')) {
|
||||
throw new Error(
|
||||
'The oidc provider no longer supports the "scope" configuration option. Please use the "additionalScopes" option instead.',
|
||||
@@ -143,17 +146,21 @@ export const oidcAuthenticator = createOAuthAuthenticator({
|
||||
return { helper, client, strategy };
|
||||
});
|
||||
|
||||
return { initializedPrompt, promise };
|
||||
return { initializedPrompt, promise, searchParams: startUrlSearchParams };
|
||||
},
|
||||
|
||||
async start(input, ctx) {
|
||||
const { initializedPrompt, promise } = ctx;
|
||||
const { initializedPrompt, promise, searchParams } = ctx;
|
||||
const { helper } = await promise;
|
||||
|
||||
// Merge the custom start URL params, but do not override the standard params (scope, state etc)
|
||||
const options: Record<string, string> = {
|
||||
...searchParams,
|
||||
scope: input.scope,
|
||||
state: input.state,
|
||||
nonce: crypto.randomBytes(16).toString('base64'),
|
||||
};
|
||||
|
||||
const prompt = initializedPrompt || 'none';
|
||||
if (prompt !== 'auto') {
|
||||
options.prompt = prompt;
|
||||
|
||||
Reference in New Issue
Block a user