Skip to content

Commit

Permalink
Do not handle simultaneous logins
Browse files Browse the repository at this point in the history
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
 elastic#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.
  • Loading branch information
jkakavas committed May 7, 2019
1 parent b12f0da commit 4315f8c
Showing 1 changed file with 1 addition and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
}

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -356,7 +270,6 @@ export class OpenIdConnectAuthenticationProvider extends BaseAuthenticationProvi
}
}


/**
* Validates whether request contains `Bearer ***` Authorization header and just passes it
* forward to Elasticsearch backend.
Expand Down

0 comments on commit 4315f8c

Please sign in to comment.