From 7495edfc17c2a6ecd1bff8efcea0e310df1ef033 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Wed, 16 Oct 2024 15:19:45 +0500 Subject: [PATCH 01/10] feat: Added custom timeout for oidc provider Signed-off-by: Ruslan Nasyrov --- .changeset/early-feet-lay.md | 5 +++++ .../src/authenticator.ts | 15 ++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 .changeset/early-feet-lay.md diff --git a/.changeset/early-feet-lay.md b/.changeset/early-feet-lay.md new file mode 100644 index 0000000000..d6f62d1129 --- /dev/null +++ b/.changeset/early-feet-lay.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-oidc-provider': minor +--- + +Added custom timeout setting for oidc provider diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index c396ea7034..ece4776bf8 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -34,12 +34,14 @@ import { } from '@backstage/plugin-auth-node'; const HTTP_OPTION_TIMEOUT = 10000; -const httpOptionsProvider: CustomHttpOptionsProvider = (_url, options) => { - return { - ...options, - timeout: HTTP_OPTION_TIMEOUT, +const createHttpOptionsProvider = + ({ timeout }: { timeout?: number }): CustomHttpOptionsProvider => + (_url, options) => { + return { + ...options, + timeout: timeout ?? HTTP_OPTION_TIMEOUT, + }; }; -}; /** * authentication result for the OIDC which includes the token set and user @@ -84,6 +86,9 @@ export const oidcAuthenticator = createOAuthAuthenticator({ 'The oidc provider no longer supports the "scope" configuration option. Please use the "additionalScopes" option instead.', ); } + const httpOptionsProvider = createHttpOptionsProvider({ + timeout: config.getOptionalNumber('timeout'), + }); Issuer[custom.http_options] = httpOptionsProvider; const promise = Issuer.discover(metadataUrl).then(issuer => { From 7ee07a12481fa47a20fac60ab1a37c52f915ef90 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Thu, 17 Oct 2024 12:16:29 +0500 Subject: [PATCH 02/10] changed timeout type Signed-off-by: Ruslan Nasyrov --- plugins/auth-backend-module-oidc-provider/config.d.ts | 2 ++ plugins/auth-backend-module-oidc-provider/package.json | 1 + .../src/authenticator.ts | 10 +++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index 409c658c23..4c7c435e54 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { HumanDuration } from '@backstage/types'; import { HumanDuration } from '@backstage/types'; @@ -33,6 +34,7 @@ export interface Config { tokenSignedResponseAlg?: string; additionalScopes?: string | string[]; prompt?: string; + timeout?: HumanDuration; signIn?: { resolvers: Array< | { diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 3de2277163..3e82e6183e 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -37,6 +37,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", + "@backstage/types": "workspace:^", "express": "^4.18.2", "openid-client": "^5.5.0", "passport": "^0.7.0" diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index ece4776bf8..9c4589a73e 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -32,6 +32,7 @@ import { PassportOAuthAuthenticatorHelper, PassportOAuthPrivateInfo, } from '@backstage/plugin-auth-node'; +import { durationToMilliseconds, HumanDuration } from '@backstage/types'; const HTTP_OPTION_TIMEOUT = 10000; const createHttpOptionsProvider = @@ -86,8 +87,15 @@ export const oidcAuthenticator = createOAuthAuthenticator({ 'The oidc provider no longer supports the "scope" configuration option. Please use the "additionalScopes" option instead.', ); } + + const timeoutHumanDuration = config.getOptional('timeout'); + const timeoutMilliseconds = + typeof timeoutHumanDuration === 'object' + ? durationToMilliseconds(timeoutHumanDuration) + : undefined; + const httpOptionsProvider = createHttpOptionsProvider({ - timeout: config.getOptionalNumber('timeout'), + timeout: timeoutMilliseconds, }); Issuer[custom.http_options] = httpOptionsProvider; From 5b423cff583e662f9080d173cb27d2ebf84cecb3 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Fri, 25 Oct 2024 12:47:57 +0500 Subject: [PATCH 03/10] review changes #2 Signed-off-by: Ruslan Nasyrov --- .../auth-backend-module-oidc-provider/package.json | 1 + .../src/authenticator.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 3e82e6183e..7d6dc841ab 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -35,6 +35,7 @@ }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", + "@backstage/config": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/types": "workspace:^", diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index 9c4589a73e..b98bec3cc4 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -33,6 +33,7 @@ import { PassportOAuthPrivateInfo, } from '@backstage/plugin-auth-node'; import { durationToMilliseconds, HumanDuration } from '@backstage/types'; +import { readDurationFromConfig } from '@backstage/config'; const HTTP_OPTION_TIMEOUT = 10000; const createHttpOptionsProvider = @@ -88,12 +89,11 @@ export const oidcAuthenticator = createOAuthAuthenticator({ ); } - const timeoutHumanDuration = config.getOptional('timeout'); - const timeoutMilliseconds = - typeof timeoutHumanDuration === 'object' - ? durationToMilliseconds(timeoutHumanDuration) - : undefined; - + const timeoutMilliseconds = config.has('timeout') + ? durationToMilliseconds( + readDurationFromConfig(config, { key: 'timeout' }), + ) + : undefined; const httpOptionsProvider = createHttpOptionsProvider({ timeout: timeoutMilliseconds, }); From 06a58f67976f482d8bc2330a8d91565ec33e0228 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Fri, 25 Oct 2024 13:28:07 +0500 Subject: [PATCH 04/10] review changes #2 (fix) Signed-off-by: Ruslan Nasyrov --- plugins/auth-backend-module-oidc-provider/src/authenticator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index b98bec3cc4..6e7022f7cf 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -32,7 +32,7 @@ import { PassportOAuthAuthenticatorHelper, PassportOAuthPrivateInfo, } from '@backstage/plugin-auth-node'; -import { durationToMilliseconds, HumanDuration } from '@backstage/types'; +import { durationToMilliseconds } from '@backstage/types'; import { readDurationFromConfig } from '@backstage/config'; const HTTP_OPTION_TIMEOUT = 10000; From f9f72525a94bfbf0f1a60729228532c0fa4e48e7 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Fri, 8 Nov 2024 15:48:32 +0500 Subject: [PATCH 05/10] added an example of usage and configuration tests Signed-off-by: Ruslan Nasyrov --- .changeset/early-feet-lay.md | 12 +++ .../src/authenticator.test.ts | 76 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/.changeset/early-feet-lay.md b/.changeset/early-feet-lay.md index d6f62d1129..ee6fd89730 100644 --- a/.changeset/early-feet-lay.md +++ b/.changeset/early-feet-lay.md @@ -3,3 +3,15 @@ --- Added custom timeout setting for oidc provider + +Here is an example of how to use a custom timeout with the configuration: + +```yaml +auth: + oidc: + production: + clientId: ${AUTH_GOOGLE_CLIENT_ID} + clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET} + timeout: + seconds: 30 +``` diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index 38e134bcd0..d12427f127 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -28,6 +28,7 @@ import { ConfigReader } from '@backstage/config'; import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; import { rest } from 'msw'; import express from 'express'; +import { custom } from 'openid-client'; describe('oidcAuthenticator', () => { let implementation: any; @@ -169,6 +170,81 @@ describe('oidcAuthenticator', () => { jest.clearAllMocks(); }); + describe('timeout configuration', () => { + const TEST_URL = new URL('https://test.com'); + + it('should use default timeout when no timeout is configured', async () => { + const { promise } = oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + }), + }); + const { client } = await promise; + + const timeout = client[custom.http_options](TEST_URL, {}).timeout; + + // Check if the HTTP timeout is set to the default value + expect(timeout).toBeDefined(); + expect(timeout).toBe(10000); + }); + + it('should use configured timeout when provided in the config', async () => { + const { promise } = oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + timeout: { + seconds: 30, + }, + }), + }); + const { client } = await promise; + + const timeout = client[custom.http_options](TEST_URL, {}).timeout; + + // Check if the HTTP timeout is set to the configured value (30 seconds) + expect(timeout).toBeDefined(); + expect(timeout).toBe(30000); + }); + + it('should handle invalid timeout configuration gracefully', async () => { + expect(() => { + oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + timeout: '30s', + }), + }); + }).toThrow( + "Failed to read duration from config, TypeError: Invalid type in config for key 'timeout' in 'mock-config', got string, wanted object", + ); + + expect(() => { + oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + timeout: { + seconds: '30s', + }, + }), + }); + }).toThrow( + "Failed to read duration from config, Error: Unable to convert config value for key 'timeout.seconds' in 'mock-config' to a number", + ); + }); + }); + describe('#start', () => { let fakeSession: Record; let startRequest: OAuthAuthenticatorStartInput; From 98ff86af2e98f31f7b23795c0a51c2386284fcc3 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Thu, 13 Mar 2025 14:04:58 +0500 Subject: [PATCH 06/10] fixed config imports Signed-off-by: Ruslan Nasyrov --- plugins/auth-backend-module-oidc-provider/config.d.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index 4c7c435e54..907db35612 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -15,8 +15,6 @@ */ import { HumanDuration } from '@backstage/types'; -import { HumanDuration } from '@backstage/types'; - export interface Config { auth?: { providers?: { @@ -38,9 +36,9 @@ export interface Config { signIn?: { resolvers: Array< | { - resolver: 'emailLocalPartMatchingUserEntityName'; - allowedDomains?: string[]; - } + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; From c197bf80dc76d4db7ffe10db9f348fd9f000fc3a Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Fri, 14 Mar 2025 16:18:08 +0500 Subject: [PATCH 07/10] prettier fix Signed-off-by: Ruslan Nasyrov --- plugins/auth-backend-module-oidc-provider/config.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index 907db35612..65716a46b6 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -36,9 +36,9 @@ export interface Config { signIn?: { resolvers: Array< | { - resolver: 'emailLocalPartMatchingUserEntityName'; - allowedDomains?: string[]; - } + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; From 8f68415a7365ef5af79ee28c501cd48621dba811 Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Mon, 17 Mar 2025 11:12:46 +0500 Subject: [PATCH 08/10] fix test Signed-off-by: Ruslan Nasyrov --- .../src/authenticator.test.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index d12427f127..a7a80da9ab 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -117,12 +117,12 @@ describe('oidcAuthenticator', () => { return res( req.headers.get('Authorization') ? ctx.json({ - access_token: 'accessToken', - id_token: idToken, - refresh_token: 'refreshToken', - scope: 'testScope', - expires_in: 3600, - }) + access_token: 'accessToken', + id_token: idToken, + refresh_token: 'refreshToken', + scope: 'testScope', + expires_in: 3600, + }) : ctx.status(401), ); }), @@ -220,12 +220,10 @@ describe('oidcAuthenticator', () => { metadataUrl: 'https://oidc.test/.well-known/openid-configuration', clientId: 'clientId', clientSecret: 'clientSecret', - timeout: '30s', + timeout: 123, // Invalid: should be a duration object }), }); - }).toThrow( - "Failed to read duration from config, TypeError: Invalid type in config for key 'timeout' in 'mock-config', got string, wanted object", - ); + }).toThrow(); expect(() => { oidcAuthenticator.initialize({ @@ -235,13 +233,11 @@ describe('oidcAuthenticator', () => { clientId: 'clientId', clientSecret: 'clientSecret', timeout: { - seconds: '30s', + invalid: 'value', }, }), }); - }).toThrow( - "Failed to read duration from config, Error: Unable to convert config value for key 'timeout.seconds' in 'mock-config' to a number", - ); + }).toThrow(); }); }); From 5803ea239b48adfce4fd99be0b82ff3b89a50dfb Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Mon, 17 Mar 2025 12:45:07 +0500 Subject: [PATCH 09/10] prettier fix Signed-off-by: Ruslan Nasyrov --- .../src/authenticator.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index a7a80da9ab..c327d4ecc5 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -117,12 +117,12 @@ describe('oidcAuthenticator', () => { return res( req.headers.get('Authorization') ? ctx.json({ - access_token: 'accessToken', - id_token: idToken, - refresh_token: 'refreshToken', - scope: 'testScope', - expires_in: 3600, - }) + access_token: 'accessToken', + id_token: idToken, + refresh_token: 'refreshToken', + scope: 'testScope', + expires_in: 3600, + }) : ctx.status(401), ); }), From 6e72e879d702d2e38214ccb05e00ee77b986b09c Mon Sep 17 00:00:00 2001 From: Ruslan Nasyrov Date: Mon, 24 Mar 2025 12:27:07 +0500 Subject: [PATCH 10/10] fixed changeset Signed-off-by: Ruslan Nasyrov --- .changeset/early-feet-lay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/early-feet-lay.md b/.changeset/early-feet-lay.md index ee6fd89730..71a557e5e5 100644 --- a/.changeset/early-feet-lay.md +++ b/.changeset/early-feet-lay.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-auth-backend-module-oidc-provider': minor +'@backstage/plugin-auth-backend-module-oidc-provider': patch --- Added custom timeout setting for oidc provider