From 23081edfb401cb53a3a700cc3051f98f48146df2 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Thu, 22 Aug 2024 09:48:35 +0200 Subject: [PATCH] fix(security, http): expose authentication headers in the authentication result when HTTP authentication is used (#190998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary When Kibana tries to authenticate a request that already has an `Authorization` header (not a cookie, client certificate, or Kerberos ticket), the authentication is performed by the [HTTP authentication provider](https://www.elastic.co/guide/en/kibana/current/kibana-authentication.html#http-authentication). Unlike session/Kerberos/PKI providers, this provider returns an authentication result that doesn't explicitly tell Core which authorization headers should be used (e.g., `t.authenticated({ state: authenticationResult.user, --> requestHeaders: authenticationResult.authHeaders <-- ... });`), assuming that Core will just use the headers from the request. The `Authorization` header is forwarded to Elasticsearch by default, no additional configuration is required. This worked well previously, but I think with the introduction of the the [`getSecondaryAuthHeaders`](https://github.com/elastic/kibana/pull/184901) method this is the first time where this assumption doesn't hold. Internally, this method tries to reuse authentication headers that were provided to Core by the authentication provider during the request authentication stage — headers that the HTTP authentication provider never provided before. This PR makes the HTTP authentication provider behave consistently with the rest of the providers we support today. (cherry picked from commit 3ac931fb796b2053c68b454804e643788dc598d9) --- .../authentication/providers/http.test.ts | 32 +++++++++---------- .../server/authentication/providers/http.ts | 6 +++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/security/server/authentication/providers/http.test.ts b/x-pack/plugins/security/server/authentication/providers/http.test.ts index 90ff62294ff3fd..d599b6be2d9c38 100644 --- a/x-pack/plugins/security/server/authentication/providers/http.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/http.test.ts @@ -133,10 +133,10 @@ describe('HTTPAuthenticationProvider', () => { }); await expect(provider.authenticate(request)).resolves.toEqual( - AuthenticationResult.succeeded({ - ...user, - authentication_provider: { type: 'http', name: 'http' }, - }) + AuthenticationResult.succeeded( + { ...user, authentication_provider: { type: 'http', name: 'http' } }, + { authHeaders: { authorization: header } } + ) ); expectAuthenticateCall(mockOptions.client, { headers: { authorization: header } }); @@ -160,10 +160,10 @@ describe('HTTPAuthenticationProvider', () => { }); await expect(provider.authenticate(request)).resolves.toEqual( - AuthenticationResult.succeeded({ - ...user, - authentication_provider: { type: 'http', name: 'http' }, - }) + AuthenticationResult.succeeded( + { ...user, authentication_provider: { type: 'http', name: 'http' } }, + { authHeaders: { authorization: header } } + ) ); expectAuthenticateCall(mockOptions.client, { headers: { authorization: header } }); @@ -187,10 +187,10 @@ describe('HTTPAuthenticationProvider', () => { }); await expect(provider.authenticate(request)).resolves.toEqual( - AuthenticationResult.succeeded({ - ...user, - authentication_provider: { type: 'http', name: 'http' }, - }) + AuthenticationResult.succeeded( + { ...user, authentication_provider: { type: 'http', name: 'http' } }, + { authHeaders: { authorization: header } } + ) ); expectAuthenticateCall(mockOptions.client, { headers: { authorization: header } }); @@ -217,10 +217,10 @@ describe('HTTPAuthenticationProvider', () => { }); await expect(provider.authenticate(request)).resolves.toEqual( - AuthenticationResult.succeeded({ - ...user, - authentication_provider: { type: 'http', name: 'http' }, - }) + AuthenticationResult.succeeded( + { ...user, authentication_provider: { type: 'http', name: 'http' } }, + { authHeaders: { authorization: header } } + ) ); expectAuthenticateCall(mockOptions.client, { headers: { authorization: header } }); diff --git a/x-pack/plugins/security/server/authentication/providers/http.ts b/x-pack/plugins/security/server/authentication/providers/http.ts index 630ae7ea59de78..8619d942d3e1d6 100644 --- a/x-pack/plugins/security/server/authentication/providers/http.ts +++ b/x-pack/plugins/security/server/authentication/providers/http.ts @@ -113,7 +113,11 @@ export class HTTPAuthenticationProvider extends BaseAuthenticationProvider { return AuthenticationResult.notHandled(); } - return AuthenticationResult.succeeded(user); + return AuthenticationResult.succeeded(user, { + // Even though the `Authorization` header is already present in the HTTP headers of the original request, + // we still need to expose it to the Core authentication service for consistency. + authHeaders: { authorization: authorizationHeader.toString() }, + }); } catch (err) { this.logger.debug( `Failed to authenticate request to ${request.url.pathname} via authorization header with "${