Skip to content

Commit d949bf5

Browse files
committed
Merge branch 'connectors-auth-code-grant' of github.com:elastic/kibana into issue-250979-user-token-so
2 parents e744ef1 + f1e5cf2 commit d949bf5

8 files changed

Lines changed: 35 additions & 27 deletions

File tree

src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
150150
"ml-module": "cb77705b41ea0a35d8ba79b19014a30069e0e93a2cfb7ae8c6f20a79207d5daa",
151151
"ml-trained-model": "133305438dc0b60a6660c44f0d8183ad5ba079db8fdd4e4f4b5ab3a09d2f29b8",
152152
"monitoring-telemetry": "fa7c4f2a099b4f0539e571372a598601c2a0c65ba50f6c34df23b4d6925cdc53",
153-
"oauth_state": "dba837d6453b71f02f3aedc40a85bb161267e50203058ecab83a6b918376065f",
153+
"oauth_state": "8902d67a5fb68ccea1f3a63100dc45b84a764c786dd13d77b75ca5a901f42335",
154154
"observability-onboarding-state": "b656db675800bfee8a2ddb5bf73b543542c7a7db64ed268ab5adcae6910773d2",
155155
"osquery-manager-usage-metric": "8833b9f812e9179897444c395761f9911945cfb77de9869c4e9b6ee6eeb0f573",
156156
"osquery-pack": "7ee940ea04c9c562406977efaa213050b2079c5bcc4e06ec56c8be6a85eb5ccd",
@@ -996,7 +996,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
996996
"oauth_state|global: a665293b001c9e7b6e92c0a50a553b8163dbcd41",
997997
"oauth_state|mappings: 04721e2fa836fed1f3f2e9c343d96ec5304f8f09",
998998
"oauth_state|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709",
999-
"oauth_state|10.1.0: 6e761629347d967babaf76618a126851715a8156e27de8198f27248bd894edb9",
999+
"oauth_state|10.1.0: 8324da0f847d48f9b0de638008110b966275a95f53d3efc9242fe25f5a2f1f13",
10001000
"====================================================================================",
10011001
"observability-onboarding-state|global: c226ba4dd0412c2d7fd7a01976461e9da00b78bf",
10021002
"observability-onboarding-state|mappings: d6efe91e6efcc5e1b41fac37b731b715182939ce",

x-pack/platform/plugins/shared/actions/server/lib/oauth_authorization_service.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ interface ConnectorWithOAuth {
6565
interface ConstructorOptions {
6666
actionsClient: ActionsClient;
6767
encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
68-
kibanaBaseUrl: string;
6968
}
7069

7170
/**
@@ -79,12 +78,10 @@ interface ConstructorOptions {
7978
export class OAuthAuthorizationService {
8079
private readonly actionsClient: ActionsClient;
8180
private readonly encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
82-
private readonly kibanaBaseUrl: string;
8381

84-
constructor({ actionsClient, encryptedSavedObjectsClient, kibanaBaseUrl }: ConstructorOptions) {
82+
constructor({ actionsClient, encryptedSavedObjectsClient }: ConstructorOptions) {
8583
this.actionsClient = actionsClient;
8684
this.encryptedSavedObjectsClient = encryptedSavedObjectsClient;
87-
this.kibanaBaseUrl = kibanaBaseUrl;
8885
}
8986

9087
/**
@@ -152,16 +149,17 @@ export class OAuthAuthorizationService {
152149
* The redirect URI is where the OAuth provider will send the user after authorization.
153150
* It points to the oauth_callback route in this Kibana instance.
154151
*
152+
* @param publicBaseUrl - The Kibana public base URL (`server.publicBaseUrl`)
155153
* @returns The complete redirect URI
156154
* @throws Error if Kibana public base URL is not configured
157155
*/
158-
getRedirectUri(): string {
159-
if (!this.kibanaBaseUrl) {
156+
static getRedirectUri(publicBaseUrl: string | undefined): string {
157+
if (!publicBaseUrl) {
160158
throw new Error(
161159
'Kibana public URL not configured. Please set server.publicBaseUrl in kibana.yml'
162160
);
163161
}
164-
return `${this.kibanaBaseUrl}${BASE_ACTION_API_PATH}/connector/_oauth_callback`;
162+
return `${publicBaseUrl}${BASE_ACTION_API_PATH}/connector/_oauth_callback`;
165163
}
166164

167165
/**

x-pack/platform/plugins/shared/actions/server/lib/oauth_state_client.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ interface OAuthStateAttributes {
1919
state: string;
2020
codeVerifier: string;
2121
connectorId: string;
22-
redirectUri: string;
2322
kibanaReturnUrl: string;
2423
spaceId: string;
2524
createdAt: string;
@@ -39,7 +38,6 @@ interface ConstructorOptions {
3938

4039
interface CreateStateOptions {
4140
connectorId: string;
42-
redirectUri: string;
4341
kibanaReturnUrl: string;
4442
spaceId: string;
4543
createdBy?: string;
@@ -79,7 +77,6 @@ export class OAuthStateClient {
7977
*/
8078
public async create({
8179
connectorId,
82-
redirectUri,
8380
kibanaReturnUrl,
8481
spaceId,
8582
createdBy,
@@ -103,7 +100,6 @@ export class OAuthStateClient {
103100
state,
104101
codeVerifier,
105102
connectorId,
106-
redirectUri,
107103
kibanaReturnUrl,
108104
spaceId,
109105
createdAt: now.toISOString(),

x-pack/platform/plugins/shared/actions/server/routes/oauth_authorize.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ export const oauthAuthorizeRoute = (
9292
encryptedSavedObjectsClient: encryptedSavedObjects.getClient({
9393
includedHiddenTypes: ['action'],
9494
}),
95-
kibanaBaseUrl: kibanaUrl,
9695
});
9796

9897
const spaceId = spaces ? spaces.spacesService.getSpaceId(req) : 'default';
@@ -101,7 +100,7 @@ export const oauthAuthorizeRoute = (
101100
namespace = spaces.spacesService.spaceIdToNamespace(spaceId);
102101
}
103102
const oauthConfig = await oauthService.getOAuthConfig(connectorId, namespace);
104-
const redirectUri = oauthService.getRedirectUri();
103+
const redirectUri = OAuthAuthorizationService.getRedirectUri(kibanaUrl);
105104

106105
// Validate and build return URL for post-OAuth redirect
107106
const requestedReturnUrl = req.body?.returnUrl;
@@ -137,7 +136,6 @@ export const oauthAuthorizeRoute = (
137136
});
138137
const { state, codeChallenge } = await oauthStateClient.create({
139138
connectorId,
140-
redirectUri,
141139
kibanaReturnUrl,
142140
spaceId,
143141
});

x-pack/platform/plugins/shared/actions/server/routes/oauth_callback.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { schema } from '@kbn/config-schema';
99
import type { CoreSetup, IRouter, Logger } from '@kbn/core/server';
1010
import { i18n } from '@kbn/i18n';
11+
import { escape } from 'lodash';
1112
import type { ActionsPluginsStart } from '../plugin';
1213
import type { ILicenseState } from '../lib';
1314
import { BASE_ACTION_API_PATH } from '../../common';
@@ -16,6 +17,7 @@ import type { ActionsConfigurationUtilities } from '../actions_config';
1617
import { DEFAULT_ACTION_ROUTE_SECURITY } from './constants';
1718
import { verifyAccessAndContext } from './verify_access_and_context';
1819
import { OAuthStateClient } from '../lib/oauth_state_client';
20+
import { OAuthAuthorizationService } from '../lib/oauth_authorization_service';
1921
import { requestOAuthAuthorizationCodeToken } from '../lib/request_oauth_authorization_code_token';
2022
import { ConnectorTokenClient } from '../lib/connector_token_client';
2123
import type { OAuthRateLimiter } from '../lib/oauth_rate_limiter';
@@ -104,14 +106,18 @@ function generateOAuthCallbackPage({
104106
}): string {
105107
const iconColor = isSuccess ? '#00BFB3' : '#BD271E';
106108
const icon = isSuccess ? '✓' : '✕';
109+
const sanitisedTitle = escape(title);
110+
const sanitisedHeading = escape(heading);
111+
const sanitisedMessage = escape(message);
112+
const sanitisedDetails = details ? escape(details) : '';
107113

108114
return `
109115
<!DOCTYPE html>
110116
<html lang="en">
111117
<head>
112118
<meta charset="utf-8">
113119
<meta name="viewport" content="width=device-width, initial-scale=1">
114-
<title>${title}</title>
120+
<title>${sanitisedTitle}</title>
115121
<style>
116122
* {
117123
margin: 0;
@@ -191,9 +197,9 @@ function generateOAuthCallbackPage({
191197
<body>
192198
<div class="container">
193199
<div class="icon">${icon}</div>
194-
<h1>${heading}</h1>
195-
<p>${message}</p>
196-
${details ? `<div class="details">${details}</div>` : ''}
200+
<h1>${sanitisedHeading}</h1>
201+
<p>${sanitisedMessage}</p>
202+
${sanitisedDetails ? `<div class="details">${sanitisedDetails}</div>` : ''}
197203
${
198204
autoClose
199205
? '<p style="display: none; margin-top: 16px;" class="auto-close-message">This window will close automatically, or you can close it manually.</p>'
@@ -312,7 +318,7 @@ export const oauthCallbackRoute = (
312318
}
313319

314320
try {
315-
const [, { encryptedSavedObjects }] = await coreSetup.getStartServices();
321+
const [coreStart, { encryptedSavedObjects, spaces }] = await coreSetup.getStartServices();
316322

317323
// Retrieve and validate state
318324
const oauthStateClient = new OAuthStateClient({
@@ -339,16 +345,20 @@ export const oauthCallbackRoute = (
339345
});
340346
}
341347

342-
// Get connector with decrypted secrets
348+
// Get connector with decrypted secrets using the spaceId from the OAuth state
343349
const connectorEncryptedClient = encryptedSavedObjects.getClient({
344350
includedHiddenTypes: ['action'],
345351
});
352+
const namespace =
353+
spaces && oauthState.spaceId
354+
? spaces.spacesService.spaceIdToNamespace(oauthState.spaceId)
355+
: undefined;
346356
const rawAction = await connectorEncryptedClient.getDecryptedAsInternalUser<{
347357
actionTypeId: string;
348358
name: string;
349359
config: OAuthConnectorConfig;
350360
secrets: OAuthConnectorSecrets;
351-
}>('action', oauthState.connectorId);
361+
}>('action', oauthState.connectorId, { namespace });
352362

353363
const config = rawAction.attributes.config;
354364
const secrets = rawAction.attributes.secrets;
@@ -363,13 +373,18 @@ export const oauthCallbackRoute = (
363373
);
364374
}
365375

376+
// Build the redirect URI (must match the one sent to the authorization endpoint)
377+
const redirectUri = OAuthAuthorizationService.getRedirectUri(
378+
coreStart.http.basePath.publicBaseUrl
379+
);
380+
366381
// Exchange authorization code for tokens
367382
const tokenResult = await requestOAuthAuthorizationCodeToken(
368383
tokenUrl,
369384
logger,
370385
{
371386
code,
372-
redirectUri: oauthState.redirectUri,
387+
redirectUri,
373388
codeVerifier: oauthState.codeVerifier,
374389
clientId,
375390
clientSecret,

x-pack/platform/plugins/shared/actions/server/saved_objects/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,12 @@ export function setupSavedObjects(
196196
};
197197
},
198198
});
199-
200199
encryptedSavedObjects.registerType({
201200
type: OAUTH_STATE_SAVED_OBJECT_TYPE,
202201
attributesToEncrypt: new Set(['codeVerifier']),
203202
attributesToIncludeInAAD: new Set([
204203
'state',
205204
'connectorId',
206-
'redirectUri',
207205
'authorizationUrl',
208206
'scope',
209207
'spaceId',

x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_oauth_state/v1.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export const rawOAuthStateSchema = schema.object({
1111
state: schema.string(),
1212
codeVerifier: schema.string(),
1313
connectorId: schema.string(),
14-
redirectUri: schema.string(),
1514
scope: schema.maybe(schema.string()),
1615
kibanaReturnUrl: schema.string(), // in case of OAuth success, redirect to this URL
1716
spaceId: schema.string(), // the space where the connector exists

x-pack/platform/plugins/shared/encrypted_saved_objects/integration_tests/ci_checks/check_registered_types.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ describe('checking changes on all registered encrypted SO types', () => {
7777
"fleet-uninstall-tokens": "6e7d75921dcce46e566f175eab1b0e3825fe565f20cdb3c984e7037934d61e23",
7878
"ingest-download-sources": "23eb3cf789fe13b4899215c6f919705b8a44b89f8feba7181e1f5db3c7699d40",
7979
"ingest-outputs": "d66716d5333484a25c57f7917bead5ac2576ec57a4b9eb61701b573f35ab62ad",
80+
<<<<<<< HEAD
8081
"oauth_state": "192a6c868fd863d8d14c33cc3357a3c61de3acca48b4a8ccb3a5e29ba465f4d7",
82+
=======
83+
"oauth_state": "90050be54da9ef0e0059b14eb634af1165374618055289365f8cfebba24ddcdb",
84+
>>>>>>> f1e5cf2909709f35cf539b848b087a775364bbb8
8185
"privmon-api-key": "7d7b76b3bc5287a784518731ba66d4f761052177fc04b1a85e5605846ab9de42",
8286
"synthetics-monitor": "f1c060b7be3b30187c4adcb35d74f1fa8a4290bd7faf04fec869de2aa387e21b",
8387
"synthetics-monitor-multi-space": "39c4c6abd28c4173f77c1c89306e92b6b92492c0029274e10620a170be4d4a67",

0 commit comments

Comments
 (0)