From 61cf0e8920425f136ec992c5e11799a4c99ea531 Mon Sep 17 00:00:00 2001 From: Jeramy Soucy Date: Thu, 22 Jan 2026 19:23:53 +0100 Subject: [PATCH 01/10] Update SAML logs and request ID retrieval (#249037) Closes https://github.com/elastic/kibana/issues/246963 ## Summary Adds logging of new "unsolicited InResponseTo" error condition returned from Elasticsearch. This will allow us to identify and filter this specific scenario in serverless logs. Additionally, the request ID is now retrieved directly from the Elasticsearch response metadata rather than parsed from the SAML response. This PR also adds parsing of request ID in our mock SAML IDP plugin. This allows us to use the mock IDP for both SP (service provider) initiated and IDP (identity provider) initiated logins. Lastly, this PR moves the `getSAMLRequestId` utility function to the mock IDP utils package to remove duplication. ### Testing #### Mock IDP - Start ES & KB locally in serverless mode - Navigate to the Kibana URL - Verify the redirect to the mock IDP with a SAML request parameter (http://localhost:5601/mock_idp/login?SAMLRequest=) - Select a role and click Login - Verify logs ``` [INFO ][plugins.mockIdpPlugin] Sending SAML response for request ID: ` _SOME_ID` [INFO ][plugins.security.authentication] Performing login attempt with "saml" provider. [INFO ][plugins.security.saml.cloud-saml-kibana] Removing requestId _SAME_ID from the state. ``` - Log out - Navigate directly to the mock IDP (http://localhost:5601/mock_idp/login) - Select a role and click Login - Verify logs ``` [INFO ][plugins.security.authentication] Performing login attempt with "saml" provider. [INFO ][plugins.security.saml.cloud-saml-kibana] No requestId found in SAML response or state does not contain requestId. ... [INFO ][plugins.security.authentication] Login attempt with "saml" provider succeeded (requires redirect: true). ``` #### Unsolicited InResponseTo - Start ES & KB locally in serverless mode - Navigate to the Kibana URL - Open the browser dev tools and delete the "sid" cookie - Click Login - Verify logs ``` [INFO ][plugins.mockIdpPlugin] Sending SAML response for request ID: _SOME_ID [INFO ][plugins.security.authentication] Performing login attempt with "saml" provider. [ERROR][plugins.security.saml.cloud-saml-kibana] Failed to log in with SAML response, SP-initiated, unsolicited InResponseTo: _SAME_ID, no state - possible delayed login, current requestIds: , error: {...} [ERROR][plugins.security.authentication] Login attempt with "saml" provider cannot be handled. ``` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit f87355ace3f903f609fd7974e32db2474b795725) # Conflicts: # packages/kbn-mock-idp-plugin/server/plugin.ts # src/platform/packages/private/kbn-mock-idp-utils/src/index.ts # src/platform/packages/private/kbn-mock-idp-utils/src/utils.test.ts # src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts # x-pack/platform/plugins/shared/security/server/authentication/providers/saml.test.ts --- .../kbn-mock-idp-plugin/public/login_page.tsx | 1 + .../public/role_switcher.tsx | 1 + packages/kbn-mock-idp-plugin/server/plugin.ts | 17 +++++++++- .../private/kbn-mock-idp-utils/src/index.ts | 7 +++- .../kbn-mock-idp-utils/src/utils.test.ts | 33 ++++++++++++++++++ .../private/kbn-mock-idp-utils/src/utils.ts | 28 +++++++++++++++ .../authentication/providers/saml.test.ts | 5 +++ .../server/authentication/providers/saml.ts | 34 ++++++++++++------- .../plugins/saml_provider/moon.yml | 1 + .../saml_provider/server/saml_tools.ts | 16 +-------- .../plugins/saml_provider/tsconfig.json | 1 + .../packages/helpers/moon.yml | 1 + .../packages/helpers/saml/saml_tools.ts | 16 +-------- .../packages/helpers/tsconfig.json | 1 + .../tests/saml/saml_login.ts | 2 +- 15 files changed, 118 insertions(+), 46 deletions(-) create mode 100644 src/platform/packages/private/kbn-mock-idp-utils/src/utils.test.ts diff --git a/packages/kbn-mock-idp-plugin/public/login_page.tsx b/packages/kbn-mock-idp-plugin/public/login_page.tsx index 92561ca51f584..7900f10507e3b 100644 --- a/packages/kbn-mock-idp-plugin/public/login_page.tsx +++ b/packages/kbn-mock-idp-plugin/public/login_page.tsx @@ -47,6 +47,7 @@ export const LoginPage = () => { full_name: values.full_name, email: sanitizeEmail(values.full_name), roles: [values.role], + url: window.location.href, }); }, }); diff --git a/packages/kbn-mock-idp-plugin/public/role_switcher.tsx b/packages/kbn-mock-idp-plugin/public/role_switcher.tsx index 7a3845b0cc54a..4bcd2f4d4b465 100644 --- a/packages/kbn-mock-idp-plugin/public/role_switcher.tsx +++ b/packages/kbn-mock-idp-plugin/public/role_switcher.tsx @@ -125,6 +125,7 @@ export const RoleSwitcher = () => { full_name: currentUserState.value!.full_name, email: currentUserState.value!.email, roles: [role], + url: window.location.href, }); setIsOpen(false); }, diff --git a/packages/kbn-mock-idp-plugin/server/plugin.ts b/packages/kbn-mock-idp-plugin/server/plugin.ts index fc9043099b197..333a664280b3f 100644 --- a/packages/kbn-mock-idp-plugin/server/plugin.ts +++ b/packages/kbn-mock-idp-plugin/server/plugin.ts @@ -19,6 +19,7 @@ import { STATEFUL_ROLES_ROOT_PATH, } from '@kbn/es'; import { createSAMLResponse, MOCK_IDP_LOGIN_PATH, MOCK_IDP_LOGOUT_PATH } from '@kbn/mock-idp-utils'; +import { getSAMLRequestId } from '@kbn/mock-idp-utils/src/utils'; export interface PluginSetupDependencies { cloud: CloudSetup; @@ -29,6 +30,7 @@ const createSAMLResponseSchema = schema.object({ full_name: schema.maybe(schema.nullable(schema.string())), email: schema.maybe(schema.nullable(schema.string())), roles: schema.arrayOf(schema.string()), + url: schema.string(), }); const projectToAlias = new Map([ @@ -54,12 +56,14 @@ const readStatefulRoles = () => { export type CreateSAMLResponseParams = TypeOf; + export const plugin: PluginInitializer< void, void, PluginSetupDependencies -> = async (): Promise => ({ +> = async (initializerContext): Promise => ({ setup(core, plugins: PluginSetupDependencies) { + const logger = initializerContext.logger.get(); const router = core.http.createRouter(); core.http.resources.register( @@ -111,6 +115,12 @@ export const plugin: PluginInitializer< const { protocol, hostname, port } = core.http.getServerInfo(); const pathname = core.http.basePath.prepend('/api/security/saml/callback'); + try { + const requestId = await getSAMLRequestId(request.body.url); + if (requestId) { + logger.info(`Sending SAML response for request ID: ${requestId}`); + } + return response.ok({ body: { SAMLResponse: await createSAMLResponse({ @@ -119,9 +129,14 @@ export const plugin: PluginInitializer< full_name: request.body.full_name ?? undefined, email: request.body.email ?? undefined, roles: request.body.roles, + ...(requestId ? { authnRequestId: requestId } : {}), }), }, }); + } catch (err) { + logger.error(`Failed to create SAMLResponse: ${err}`, err); + throw err; + } } ); diff --git a/src/platform/packages/private/kbn-mock-idp-utils/src/index.ts b/src/platform/packages/private/kbn-mock-idp-utils/src/index.ts index af8cdcb94ddb5..748f6255cf905 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/src/index.ts +++ b/src/platform/packages/private/kbn-mock-idp-utils/src/index.ts @@ -20,4 +20,9 @@ export { MOCK_IDP_ATTRIBUTE_NAME, } from './constants'; -export { createMockIdpMetadata, createSAMLResponse, ensureSAMLRoleMapping } from './utils'; +export { + createMockIdpMetadata, + createSAMLResponse, + ensureSAMLRoleMapping, + getSAMLRequestId, +} from './utils'; diff --git a/src/platform/packages/private/kbn-mock-idp-utils/src/utils.test.ts b/src/platform/packages/private/kbn-mock-idp-utils/src/utils.test.ts new file mode 100644 index 0000000000000..488f2bdf68693 --- /dev/null +++ b/src/platform/packages/private/kbn-mock-idp-utils/src/utils.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { getSAMLRequestId } from './utils'; + +describe('mock-idp-utils', () => { + describe('getSAMLReuestId', () => { + it('should extract SAMLRequest ID from URL', async () => { + const url = + 'http://localhost:5601/mock_idp/login?SAMLRequest=fZJvT8IwEMa%2FSnPvYVsnExqGQYmRxD8Epi98Q0q5SWPXzl6H8u2dggYT4tvePc9z97sOLz4qw7boSTubQ9KNgaFVbq3tSw6PxXWnDxejIcnK8FqMm7Cxc3xrkAJrhZbEvpJD461wkjQJKyskEZRYjO9uBe%2FGovYuOOUMsDER%2BtBGXTlLTYV%2BgX6rFT7Ob3PYhFCLKDJOSbNxFEQvi5NI1joiVI3XYRd9pUVt2aykegU2aefQVobv2U%2FLK6del3pdt%2B8v2gKbTnJYxhivB6XkPDtL0wT755hgT2a9lA9kkpWDslTIy7TfthM1OLUUpA058JhnnTjpJL0iyQTvi5R3z1P%2BDGx2WPFS2z26%2F3is9k0kbopi1pk9LApgTz8naBvgAFx8p%2Ftj0v8byx%2B8MDpJYxgd%2B%2F6e9b41mk5mzmi1Y2Nj3PuVRxkwh1IaQohGB%2BHfHzD6BA%3D%3D'; + const requestId = await getSAMLRequestId(url); + expect(requestId).toEqual('_0e0d9fa2264331e87e1e5a65329a16f9ffce2f38'); + }); + + it('should return undefined if SAMLRequest parameter is missing', async () => { + const noParamUrl = 'http://localhost:5601/mock_idp/login'; + const requestId = await getSAMLRequestId(noParamUrl); + expect(requestId).toBeUndefined(); + }); + + it('should return undefined if SAMLRequest parameter is invalid', async () => { + const invalidUrl = 'http://localhost:5601/mock_idp/login?SAMLRequest=YmxhaCBibGFoIGJsYWg='; + const requestId = await getSAMLRequestId(invalidUrl); + expect(requestId).toBeUndefined(); + }); + }); +}); diff --git a/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts b/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts index af07d69cfe936..4746fec1a3850 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts +++ b/src/platform/packages/private/kbn-mock-idp-utils/src/utils.ts @@ -10,7 +10,11 @@ import type { Client } from '@elastic/elasticsearch'; import { X509Certificate } from 'crypto'; import { readFile } from 'fs/promises'; +import Url from 'url'; +import { promisify } from 'util'; import { SignedXml } from 'xml-crypto'; +import { parseString } from 'xml2js'; +import zlib from 'zlib'; import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; @@ -205,3 +209,27 @@ export async function ensureSAMLRoleMapping(client: Client) { }, }); } + +const inflateRawAsync = promisify(zlib.inflateRaw); +const parseStringAsync = promisify(parseString); + +export async function getSAMLRequestId(requestUrl: string): Promise { + const samlRequest = Url.parse(requestUrl, true /* parseQueryString */).query.SAMLRequest; + + let requestId: string | undefined; + + if (samlRequest) { + try { + const inflatedSAMLRequest = (await inflateRawAsync( + Buffer.from(samlRequest as string, 'base64') + )) as Buffer; + + const parsedSAMLRequest = (await parseStringAsync(inflatedSAMLRequest.toString())) as any; + requestId = parsedSAMLRequest['saml2p:AuthnRequest'].$.ID as string; + } catch (e) { + return undefined; + } + } + + return requestId; +} diff --git a/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.test.ts b/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.test.ts index 3d09d134b2124..807484cb4299f 100644 --- a/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.test.ts +++ b/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.test.ts @@ -55,6 +55,7 @@ describe('SAMLAuthenticationProvider', () => { refresh_token: 'some-refresh-token', realm: 'test-realm', authentication: mockUser, + in_response_to: mockSAMLSet1.requestId, }); await expect( @@ -104,6 +105,7 @@ describe('SAMLAuthenticationProvider', () => { refresh_token: 'some-refresh-token', realm: 'test-realm', authentication: mockUser, + in_response_to: mockSAMLSet1.requestId, }); provider = new SAMLAuthenticationProvider(mockOptions, { @@ -203,6 +205,7 @@ describe('SAMLAuthenticationProvider', () => { refresh_token: 'user-initiated-login-refresh-token', realm: 'test-realm', authentication: mockUser, + in_response_to: mockSAMLSet1.requestId, }); await expect( @@ -248,6 +251,7 @@ describe('SAMLAuthenticationProvider', () => { refresh_token: 'user-initiated-login-refresh-token', realm: 'test-realm', authentication: mockUser, + in_response_to: mockSAMLSet1.requestId, }); provider = new SAMLAuthenticationProvider(mockOptions, { @@ -367,6 +371,7 @@ describe('SAMLAuthenticationProvider', () => { refresh_token: 'some-refresh-token', realm: 'test-realm', authentication: mockUser, + in_response_to: mockSamlResponses.set25.requestId, }); const requestIdMap: Record = {}; diff --git a/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.ts b/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.ts index c1f23520540f8..e723a1ccefb3a 100644 --- a/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.ts +++ b/x-pack/platform/plugins/shared/security/server/authentication/providers/saml.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { errors } from '@elastic/elasticsearch'; import Boom from '@hapi/boom'; import type { KibanaRequest } from '@kbn/core/server'; @@ -361,6 +362,7 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { refresh_token: string; realm: string; authentication: AuthenticationInfo; + in_response_to?: string; }; try { // This operation should be performed on behalf of the user with a privilege that normal @@ -377,10 +379,24 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { }, })) as any; } catch (err) { + let inResponseToRequestId; + if (err instanceof errors.ResponseError) { + const body = (err as errors.ResponseError).meta.body as + | { error: Record } + | undefined; + inResponseToRequestId = + body?.error?.['security.saml.unsolicited_in_response_to'] ?? undefined; + } + this.logger.error( - `Failed to log in with SAML response, ${ - !isIdPInitiatedLogin ? `current requestIds: ${stateRequestIds}, ` : '' - } error: ${getDetailedErrorMessage(err)}` + [ + 'Failed to log in with SAML response', + inResponseToRequestId + ? `SP-initiated, unsolicited InResponseTo: ${inResponseToRequestId}` + : 'IDP-initiated', + state ? `current requestIds: [${stateRequestIds}]` : 'no state', + getDetailedErrorMessage(err), + ].join(', ') ); // Since we don't know upfront what realm is targeted by the Identity Provider initiated login @@ -418,7 +434,7 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { let remainingRequestIdMap = stateRequestIdMap; if (!isIdPInitiatedLogin) { - const inResponseToRequestId = this.parseRequestIdFromSAMLResponse(samlResponse); + const inResponseToRequestId = result.in_response_to; this.logger.debug(`Login was performed with requestId: ${inResponseToRequestId}`); if (stateRequestIds.length && inResponseToRequestId) { @@ -454,16 +470,8 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { ); } - private parseRequestIdFromSAMLResponse(samlResponse: string): string | null { - const samlResponseBuffer = Buffer.from(samlResponse, 'base64'); - const samlResponseString = samlResponseBuffer.toString('utf-8'); - const inResponseToRequestIdMatch = samlResponseString.match(/InResponseTo="([a-z0-9_]*)"/); - - return inResponseToRequestIdMatch ? inResponseToRequestIdMatch[1] : null; - } - private updateRemainingRequestIds( - requestIdToRemove: string | null, + requestIdToRemove: string | undefined, remainingRequestIds: Record ): [boolean, Record] { if (requestIdToRemove) { diff --git a/x-pack/platform/test/cloud_integration/plugins/saml_provider/moon.yml b/x-pack/platform/test/cloud_integration/plugins/saml_provider/moon.yml index 50f5cb3762bed..22fc832ee1773 100644 --- a/x-pack/platform/test/cloud_integration/plugins/saml_provider/moon.yml +++ b/x-pack/platform/test/cloud_integration/plugins/saml_provider/moon.yml @@ -19,6 +19,7 @@ project: dependsOn: - '@kbn/core' - '@kbn/dev-utils' + - '@kbn/mock-idp-utils' tags: - plugin - prod diff --git a/x-pack/platform/test/cloud_integration/plugins/saml_provider/server/saml_tools.ts b/x-pack/platform/test/cloud_integration/plugins/saml_provider/server/saml_tools.ts index 7d1a6cbfa4255..d2415c025d01f 100644 --- a/x-pack/platform/test/cloud_integration/plugins/saml_provider/server/saml_tools.ts +++ b/x-pack/platform/test/cloud_integration/plugins/saml_provider/server/saml_tools.ts @@ -8,10 +8,8 @@ import crypto from 'crypto'; import fs from 'fs'; import { stringify } from 'query-string'; -import url from 'url'; import zlib from 'zlib'; import { promisify } from 'util'; -import { parseString } from 'xml2js'; import { SignedXml } from 'xml-crypto'; import { KBN_KEY_PATH } from '@kbn/dev-utils'; import { CLOUD_USER_ID } from '../constants'; @@ -23,25 +21,13 @@ import { CLOUD_USER_ID } from '../constants'; * http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf. */ -const inflateRawAsync = promisify(zlib.inflateRaw); const deflateRawAsync = promisify(zlib.deflateRaw); -const parseStringAsync = promisify(parseString); const signingKey = fs.readFileSync(KBN_KEY_PATH); const signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; const canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#'; -export async function getSAMLRequestId(urlWithSAMLRequestId: string) { - const inflatedSAMLRequest = (await inflateRawAsync( - Buffer.from( - url.parse(urlWithSAMLRequestId, true /* parseQueryString */).query.SAMLRequest as string, - 'base64' - ) - )) as Buffer; - - const parsedSAMLRequest = (await parseStringAsync(inflatedSAMLRequest.toString())) as any; - return parsedSAMLRequest['saml2p:AuthnRequest'].$.ID; -} +export { getSAMLRequestId } from '@kbn/mock-idp-utils/src/utils'; export async function getSAMLResponse({ destination, diff --git a/x-pack/platform/test/cloud_integration/plugins/saml_provider/tsconfig.json b/x-pack/platform/test/cloud_integration/plugins/saml_provider/tsconfig.json index d619448106405..65a4f44dcfc53 100644 --- a/x-pack/platform/test/cloud_integration/plugins/saml_provider/tsconfig.json +++ b/x-pack/platform/test/cloud_integration/plugins/saml_provider/tsconfig.json @@ -13,5 +13,6 @@ "kbn_references": [ "@kbn/core", "@kbn/dev-utils", + "@kbn/mock-idp-utils", ] } diff --git a/x-pack/platform/test/security_api_integration/packages/helpers/moon.yml b/x-pack/platform/test/security_api_integration/packages/helpers/moon.yml index d4824649c58ce..67cc2476adde6 100644 --- a/x-pack/platform/test/security_api_integration/packages/helpers/moon.yml +++ b/x-pack/platform/test/security_api_integration/packages/helpers/moon.yml @@ -18,6 +18,7 @@ project: sourceRoot: x-pack/platform/test/security_api_integration/packages/helpers dependsOn: - '@kbn/dev-utils' + - '@kbn/mock-idp-utils' tags: - shared-common - package diff --git a/x-pack/platform/test/security_api_integration/packages/helpers/saml/saml_tools.ts b/x-pack/platform/test/security_api_integration/packages/helpers/saml/saml_tools.ts index b1dd8f851caf0..6ffb14f872662 100644 --- a/x-pack/platform/test/security_api_integration/packages/helpers/saml/saml_tools.ts +++ b/x-pack/platform/test/security_api_integration/packages/helpers/saml/saml_tools.ts @@ -8,10 +8,8 @@ import crypto from 'crypto'; import fs from 'fs'; import { stringify } from 'query-string'; -import url from 'url'; import { promisify } from 'util'; import { SignedXml } from 'xml-crypto'; -import { parseString } from 'xml2js'; import zlib from 'zlib'; import { KBN_KEY_PATH } from '@kbn/dev-utils'; @@ -23,25 +21,13 @@ import { KBN_KEY_PATH } from '@kbn/dev-utils'; * http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf. */ -const inflateRawAsync = promisify(zlib.inflateRaw); const deflateRawAsync = promisify(zlib.deflateRaw); -const parseStringAsync = promisify(parseString); const signingKey = fs.readFileSync(KBN_KEY_PATH); const signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; const canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#'; -export async function getSAMLRequestId(urlWithSAMLRequestId: string) { - const inflatedSAMLRequest = (await inflateRawAsync( - Buffer.from( - url.parse(urlWithSAMLRequestId, true /* parseQueryString */).query.SAMLRequest as string, - 'base64' - ) - )) as Buffer; - - const parsedSAMLRequest = (await parseStringAsync(inflatedSAMLRequest.toString())) as any; - return parsedSAMLRequest['saml2p:AuthnRequest'].$.ID; -} +export { getSAMLRequestId } from '@kbn/mock-idp-utils/src/utils'; export async function getSAMLResponse({ destination, diff --git a/x-pack/platform/test/security_api_integration/packages/helpers/tsconfig.json b/x-pack/platform/test/security_api_integration/packages/helpers/tsconfig.json index f4c287cbeebef..2de864fbd1cc6 100644 --- a/x-pack/platform/test/security_api_integration/packages/helpers/tsconfig.json +++ b/x-pack/platform/test/security_api_integration/packages/helpers/tsconfig.json @@ -15,5 +15,6 @@ ], "kbn_references": [ "@kbn/dev-utils", + "@kbn/mock-idp-utils", ] } diff --git a/x-pack/platform/test/security_api_integration/tests/saml/saml_login.ts b/x-pack/platform/test/security_api_integration/tests/saml/saml_login.ts index 71994bc5512ca..1edaae4981eed 100644 --- a/x-pack/platform/test/security_api_integration/tests/saml/saml_login.ts +++ b/x-pack/platform/test/security_api_integration/tests/saml/saml_login.ts @@ -177,7 +177,7 @@ export default function ({ getService }: FtrProviderContext) { describe('finishing handshake', () => { let handshakeCookie: Cookie; - let samlRequestId: string; + let samlRequestId: string | undefined; beforeEach(async () => { const handshakeResponse = await supertest From bc0d87989c9e2e70a6a6a23ad3275c67e1a34069 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:55:11 +0000 Subject: [PATCH 02/10] Changes from node scripts/eslint_all_files --no-cache --fix --- packages/kbn-mock-idp-plugin/server/plugin.ts | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/kbn-mock-idp-plugin/server/plugin.ts b/packages/kbn-mock-idp-plugin/server/plugin.ts index 333a664280b3f..cabf77f5bb4b4 100644 --- a/packages/kbn-mock-idp-plugin/server/plugin.ts +++ b/packages/kbn-mock-idp-plugin/server/plugin.ts @@ -56,12 +56,9 @@ const readStatefulRoles = () => { export type CreateSAMLResponseParams = TypeOf; - -export const plugin: PluginInitializer< - void, - void, - PluginSetupDependencies -> = async (initializerContext): Promise => ({ +export const plugin: PluginInitializer = async ( + initializerContext +): Promise => ({ setup(core, plugins: PluginSetupDependencies) { const logger = initializerContext.logger.get(); const router = core.http.createRouter(); @@ -116,27 +113,27 @@ export const plugin: PluginInitializer< const pathname = core.http.basePath.prepend('/api/security/saml/callback'); try { - const requestId = await getSAMLRequestId(request.body.url); - if (requestId) { - logger.info(`Sending SAML response for request ID: ${requestId}`); - } - - return response.ok({ - body: { - SAMLResponse: await createSAMLResponse({ - kibanaUrl: `${protocol}://${hostname}:${port}${pathname}`, - username: request.body.username, - full_name: request.body.full_name ?? undefined, - email: request.body.email ?? undefined, - roles: request.body.roles, - ...(requestId ? { authnRequestId: requestId } : {}), - }), - }, - }); - } catch (err) { - logger.error(`Failed to create SAMLResponse: ${err}`, err); - throw err; + const requestId = await getSAMLRequestId(request.body.url); + if (requestId) { + logger.info(`Sending SAML response for request ID: ${requestId}`); } + + return response.ok({ + body: { + SAMLResponse: await createSAMLResponse({ + kibanaUrl: `${protocol}://${hostname}:${port}${pathname}`, + username: request.body.username, + full_name: request.body.full_name ?? undefined, + email: request.body.email ?? undefined, + roles: request.body.roles, + ...(requestId ? { authnRequestId: requestId } : {}), + }), + }, + }); + } catch (err) { + logger.error(`Failed to create SAMLResponse: ${err}`, err); + throw err; + } } ); From c9f4c4880417f78af4d3550209e765a2be914cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Thu, 9 Apr 2026 16:09:18 +0200 Subject: [PATCH 03/10] Fixes type issues --- .../private/kbn-mock-idp-utils/jest.config.ts | 14 ++++++++++++++ .../tests/login_selector/basic_functionality.ts | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/platform/packages/private/kbn-mock-idp-utils/jest.config.ts diff --git a/src/platform/packages/private/kbn-mock-idp-utils/jest.config.ts b/src/platform/packages/private/kbn-mock-idp-utils/jest.config.ts new file mode 100644 index 0000000000000..f6911cb924339 --- /dev/null +++ b/src/platform/packages/private/kbn-mock-idp-utils/jest.config.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../..', + roots: ['/src/platform/packages/private/kbn-mock-idp-utils'], +}; diff --git a/x-pack/platform/test/security_api_integration/tests/login_selector/basic_functionality.ts b/x-pack/platform/test/security_api_integration/tests/login_selector/basic_functionality.ts index fd6df842d0fd0..50f0713713a6a 100644 --- a/x-pack/platform/test/security_api_integration/tests/login_selector/basic_functionality.ts +++ b/x-pack/platform/test/security_api_integration/tests/login_selector/basic_functionality.ts @@ -598,12 +598,14 @@ export default function ({ getService }: FtrProviderContext) { const cookie = parseCookie(samlHandshakeResponse.headers['set-cookie'][0])!; const samlRequestId = await getSAMLRequestId(samlHandshakeResponse.body.location); + expect(samlRequestId).not.to.be(undefined); + const samlResponse = await createSAMLResponse({ issuer: `http://www.elastic.co/saml2`, inResponseTo: samlRequestId, }); - samlResponseMapByRequestId[samlRequestId] = { samlResponse, cookie }; + samlResponseMapByRequestId[samlRequestId!] = { samlResponse, cookie }; } const preparedCallbacks = []; From 2c0a6004bc7f6adbcff002375d90c31a898d5049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Thu, 9 Apr 2026 17:01:37 +0200 Subject: [PATCH 04/10] Update moon.yml --- src/platform/packages/private/kbn-mock-idp-utils/moon.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml index 3d4b0abb24f54..239402079fb42 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml +++ b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml @@ -24,9 +24,12 @@ tags: - dev - group-platform - private + - jest-unit-tests fileGroups: src: - '**/*.ts' - '**/*.tsx' - '!target/**/*' + jest-config: + - jest.config.js tasks: {} From 4db609b03becd24c1b0bbce9382a047150da5d02 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:40:17 +0000 Subject: [PATCH 05/10] Changes from node scripts/regenerate_moon_projects.js --update --- src/platform/packages/private/kbn-mock-idp-utils/moon.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml index 239402079fb42..3d4b0abb24f54 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml +++ b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml @@ -24,12 +24,9 @@ tags: - dev - group-platform - private - - jest-unit-tests fileGroups: src: - '**/*.ts' - '**/*.tsx' - '!target/**/*' - jest-config: - - jest.config.js tasks: {} From 1de1f8c32993d7f0db12d319e5546f42acce3502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Tue, 14 Apr 2026 09:24:21 +0200 Subject: [PATCH 06/10] Update moonn file --- src/platform/packages/private/kbn-mock-idp-utils/moon.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml index 3d4b0abb24f54..239402079fb42 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml +++ b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml @@ -24,9 +24,12 @@ tags: - dev - group-platform - private + - jest-unit-tests fileGroups: src: - '**/*.ts' - '**/*.tsx' - '!target/**/*' + jest-config: + - jest.config.js tasks: {} From cfb23f2493e080c5b39fa951421060a9350d3492 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 14 Apr 2026 07:43:48 +0000 Subject: [PATCH 07/10] Changes from node scripts/regenerate_moon_projects.js --update --- src/platform/packages/private/kbn-mock-idp-utils/moon.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml index 239402079fb42..3d4b0abb24f54 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml +++ b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml @@ -24,12 +24,9 @@ tags: - dev - group-platform - private - - jest-unit-tests fileGroups: src: - '**/*.ts' - '**/*.tsx' - '!target/**/*' - jest-config: - - jest.config.js tasks: {} From 40b0c429f8633f9c9673b42c634a4a0fe8443dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Tue, 14 Apr 2026 10:18:35 +0200 Subject: [PATCH 08/10] Updated moon file via script --- src/platform/packages/private/kbn-mock-idp-utils/moon.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml index 3d4b0abb24f54..239402079fb42 100644 --- a/src/platform/packages/private/kbn-mock-idp-utils/moon.yml +++ b/src/platform/packages/private/kbn-mock-idp-utils/moon.yml @@ -24,9 +24,12 @@ tags: - dev - group-platform - private + - jest-unit-tests fileGroups: src: - '**/*.ts' - '**/*.tsx' - '!target/**/*' + jest-config: + - jest.config.js tasks: {} From c80b9dcc45359997f6d23e09984ad122ec390bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Tue, 14 Apr 2026 10:22:17 +0200 Subject: [PATCH 09/10] Extend moon file --- .../packages/private/kbn-mock-idp-utils/moon.extend.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml new file mode 100644 index 0000000000000..1a7d6ce1e0a04 --- /dev/null +++ b/src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml @@ -0,0 +1,5 @@ +tags: + - jest-unit-tests +fileGroups: + jest-config: + - jest.config.js From dc1873d1d2b09c2111795355455f6d9a71570303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Tue, 14 Apr 2026 12:05:38 +0200 Subject: [PATCH 10/10] Rename jest config to js --- .../kbn-mock-idp-utils/{jest.config.ts => jest.config.js} | 0 .../packages/private/kbn-mock-idp-utils/moon.extend.yml | 5 ----- 2 files changed, 5 deletions(-) rename src/platform/packages/private/kbn-mock-idp-utils/{jest.config.ts => jest.config.js} (100%) delete mode 100644 src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml diff --git a/src/platform/packages/private/kbn-mock-idp-utils/jest.config.ts b/src/platform/packages/private/kbn-mock-idp-utils/jest.config.js similarity index 100% rename from src/platform/packages/private/kbn-mock-idp-utils/jest.config.ts rename to src/platform/packages/private/kbn-mock-idp-utils/jest.config.js diff --git a/src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml b/src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml deleted file mode 100644 index 1a7d6ce1e0a04..0000000000000 --- a/src/platform/packages/private/kbn-mock-idp-utils/moon.extend.yml +++ /dev/null @@ -1,5 +0,0 @@ -tags: - - jest-unit-tests -fileGroups: - jest-config: - - jest.config.js