From 4315f8c2ac7fc2eb1a5813b0526d7466ccaebcaf Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Mon, 6 May 2019 15:35:54 +0300 Subject: [PATCH] Do not handle simultaneous logins Contrary to what happens in the SAML realm, we do not support consuming unsolicited Responses from OpenID Connect Providers. This means that the only way that a behavior such as the one described in #33644 can be observed is when - a user initiates a login in a tab but stalls in the OP authentication form - user opens a new tab in the same browser and completes authentication - user returns to original tab, submits the OP form and lands in Kibana's oidc endpoint while actually already having a session. Not sure if this is a corner case we need to cater for, so removing this for now. --- .../lib/authentication/providers/oidc.ts | 89 +------------------ 1 file changed, 1 insertion(+), 88 deletions(-) diff --git a/x-pack/plugins/security/server/lib/authentication/providers/oidc.ts b/x-pack/plugins/security/server/lib/authentication/providers/oidc.ts index 2e285650022365..758dea3cf167e5 100644 --- a/x-pack/plugins/security/server/lib/authentication/providers/oidc.ts +++ b/x-pack/plugins/security/server/lib/authentication/providers/oidc.ts @@ -136,15 +136,6 @@ export class OpenIdConnectAuthenticationProvider extends BaseAuthenticationProvi // This might be the OpenID Connect Provider redirecting the user to `redirect_uri` after authentication or // a third party initiating an authentication authenticationResult = await this.authenticateViaResponseUrl(request, state); - } else if (authenticationResult.succeeded()){ - // If user has been authenticated via session, but request also includes an OpenIDConnect response - // we should check whether this response is for the exactly same user and if not - // we'll re-authenticate user and forward to a page with the respective warning. - authenticationResult = await this.authenticateViaNewResponseUrl( - request, - (authenticationResult.state || state) as ProviderState, - authenticationResult.user as AuthenticatedUser, - ); } } @@ -182,6 +173,7 @@ export class OpenIdConnectAuthenticationProvider extends BaseAuthenticationProvi } else if (request.payload) { iss = request.payload.iss; } + if (iss) { this.debug('Authentication has been initiated by a Third Party.'); // We might already have a state and nonce generated by Elasticsearch (from an unfinished authentication in @@ -194,7 +186,6 @@ export class OpenIdConnectAuthenticationProvider extends BaseAuthenticationProvi this.debug('OpenID Connect Authentication response is not found.'); return AuthenticationResult.notHandled(); } - // If it is an authentication response and the users' session state doesn't contain all the necessary information, // then something unexpected happened and we should fail because Elasticsearch won't be able to validate the // response. @@ -238,83 +229,6 @@ export class OpenIdConnectAuthenticationProvider extends BaseAuthenticationProvi } } - - /** - * Validates whether user retrieved using session is the same as the user for which we received an - * OpenID Connect authentication response. - * If we can successfully exchange this OIDC response with access and refresh tokens, then we'll - * invalidate tokens from the existing session and use the new ones instead. - * - * The tokens are stored in the state and user is redirected to the default Kibana location, unless - * we detect that user from existing session isn't the same as defined in OIDC response. In this case - * we'll forward user to a page with the respective warning. - * @param request Request instance. - * @param existingState State existing user session is based on. - * @param user User returned for the existing session. - */ - private async authenticateViaNewResponseUrl( - request: OidcIncomingRequest, - existingState: ProviderState, - user: AuthenticatedUser, - ) { - this.debug('Trying to authenticate via OpenID Connect response with existing valid session.'); - - // First let's try to authenticate via OpenID Connect response URL. - const payloadAuthenticationResult = await this.authenticateViaResponseUrl(request); - if (payloadAuthenticationResult.failed()) { - return payloadAuthenticationResult; - } else if (!payloadAuthenticationResult.shouldUpdateState()) { - // Should never happen, but if it does - it's a bug. - return AuthenticationResult.failed( - new Error('Authentication via OpenID Connect response URL did not produce access and refresh tokens.'), - ); - } - - const newState = payloadAuthenticationResult.state as ProviderState; - - // Then use received tokens to retrieve user information. We need just username and authentication - // realm, once ES starts returning this info from `oidc/authenticate` we can get rid of this call. - const newUserAuthenticationResult = await this.authenticateViaState(request, newState); - if (newUserAuthenticationResult.failed()) { - return newUserAuthenticationResult; - } else if (newUserAuthenticationResult.user === undefined) { - // Should never happen, but if it does - it's a bug. - return AuthenticationResult.failed( - new Error('Could not retrieve user information using tokens produced for the OpenID Connect response URL.'), - ); - } - - // Now let's invalidate tokens from the existing session. - try { - await this.invalidateTokens( - existingState.accessToken!, - existingState.refreshToken!, - ); - } catch (err) { - this.debug(`Failed to invalidate existing tokens on reauthentication: ${err.message}`); - return AuthenticationResult.failed(err); - } - - if ( - newUserAuthenticationResult.user.username !== user.username || - newUserAuthenticationResult.user.authentication_realm.name !== user.authentication_realm.name - ) { - this.debug( - 'Authentication response is for a different user than currently authenticated.', - ); - - return AuthenticationResult.redirectTo( - `${this._options.basePath}/overwritten_session`, - newState, - ); - } - - this.debug( - 'Authentication response is for currently authenticated user.', - ); - return payloadAuthenticationResult; - } - /** * Initiates an authentication attempt by either providing the realm name or the issuer to Elasticsearch * @@ -356,7 +270,6 @@ export class OpenIdConnectAuthenticationProvider extends BaseAuthenticationProvi } } - /** * Validates whether request contains `Bearer ***` Authorization header and just passes it * forward to Elasticsearch backend.