KEYCLOAK-1350 client_session_state should be updated when refreshing a token

This commit is contained in:
Sebastian Rose 2015-06-02 15:01:47 +02:00
parent ea544c639e
commit 17affe576f
4 changed files with 155 additions and 10 deletions

View file

@ -24,5 +24,7 @@ public interface Details {
String NODE_HOST = "node_host";
String REASON = "reason";
String REVOKED_CLIENT = "revoked_client";
String CLIENT_SESSION_STATE = "client_session_state";
String CLIENT_SESSION_HOST = "client_session_host";
}

View file

@ -37,6 +37,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.List;
import java.util.Map;
/**
@ -227,16 +228,7 @@ public class TokenEndpoint {
throw new ErrorResponseException("invalid_grant", "Session not active", Response.Status.BAD_REQUEST);
}
String adapterSessionId = formParams.getFirst(AdapterConstants.CLIENT_SESSION_STATE);
if (adapterSessionId != null) {
String adapterSessionHost = formParams.getFirst(AdapterConstants.CLIENT_SESSION_HOST);
logger.debugf("Adapter Session '%s' saved in ClientSession for client '%s'. Host is '%s'", adapterSessionId, client.getClientId(), adapterSessionHost);
event.detail(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
clientSession.setNote(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
event.detail(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
clientSession.setNote(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
}
updateClientSession(clientSession);
AccessToken token = tokenManager.createClientAccessToken(session, accessCode.getRequestedRoles(), realm, client, user, userSession, clientSession);
@ -259,6 +251,10 @@ public class TokenEndpoint {
AccessTokenResponse res;
try {
res = tokenManager.refreshAccessToken(session, uriInfo, clientConnection, realm, client, refreshToken, event, headers);
UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
updateClientSessions(userSession.getClientSessions());
} catch (OAuthErrorException e) {
event.error(Errors.INVALID_TOKEN);
throw new ErrorResponseException(e.getError(), e.getDescription(), Response.Status.BAD_REQUEST);
@ -269,6 +265,45 @@ public class TokenEndpoint {
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
}
private void updateClientSession(ClientSessionModel clientSession) {
if(clientSession == null) {
logger.error("client session is null");
return;
}
String adapterSessionId = formParams.getFirst(AdapterConstants.CLIENT_SESSION_STATE);
if (adapterSessionId != null) {
String adapterSessionHost = formParams.getFirst(AdapterConstants.CLIENT_SESSION_HOST);
logger.debugf("Adapter Session '%s' saved in ClientSession for client '%s'. Host is '%s'", adapterSessionId, client.getClientId(), adapterSessionHost);
event.detail(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
clientSession.setNote(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
event.detail(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
clientSession.setNote(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
}
}
private void updateClientSessions(List<ClientSessionModel> clientSessions) {
if(clientSessions == null) {
logger.error("client sessions is null");
return;
}
for (ClientSessionModel clientSession : clientSessions) {
if(clientSession == null) {
logger.error("client session is null");
continue;
}
if(clientSession.getClient() == null) {
logger.error("client model in client session is null");
continue;
}
if(client.getId().equals(clientSession.getClient().getId())) {
updateClientSession(clientSession);
}
}
}
public Response buildResourceOwnerPasswordCredentialsGrant() {
if (!realm.isPasswordCredentialGrantAllowed()) {
throw new ErrorResponseException("not_enabled", "Direct Grant REST API not enabled", Response.Status.FORBIDDEN);

View file

@ -35,6 +35,7 @@ import org.junit.Assert;
import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.freemarker.LocaleHelper;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
@ -78,6 +79,10 @@ public class OAuthClient {
private PublicKey realmPublicKey;
private String clientSessionState;
private String clientSessionHost;
public OAuthClient(WebDriver driver) {
this.driver = driver;
@ -128,6 +133,14 @@ public class OAuthClient {
parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
}
if(clientSessionState != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
}
if(clientSessionHost != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
}
UrlEncodedFormEntity formEntity = null;
try {
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@ -155,6 +168,13 @@ public class OAuthClient {
parameters.add(new BasicNameValuePair("username", username));
parameters.add(new BasicNameValuePair("password", password));
if(clientSessionState != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
}
if(clientSessionHost != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
}
UrlEncodedFormEntity formEntity;
try {
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@ -211,6 +231,13 @@ public class OAuthClient {
parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
}
if(clientSessionState != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
}
if(clientSessionHost != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
}
UrlEncodedFormEntity formEntity;
try {
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@ -360,6 +387,16 @@ public class OAuthClient {
return this;
}
public OAuthClient clientSessionState(String client_session_state) {
this.clientSessionState = client_session_state;
return this;
}
public OAuthClient clientSessionHost(String client_session_host) {
this.clientSessionHost = client_session_host;
return this;
}
public String getRealm() {
return realm;
}

View file

@ -0,0 +1,71 @@
package org.keycloak.testsuite.oauth;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details;
import org.keycloak.events.Event;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
/**
* @author Sebastian Rose, AOE on 02.06.15.
*/
public class OAuthDanceClientSessionExtensionTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule();
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected OAuthClient oauth;
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Test
public void doOauthDanceWithClientSessionStateAndHost() throws Exception {
oauth.doLogin("test-user@localhost", "password");
Event loginEvent = events.expectLogin().assertEvent();
String sessionId = loginEvent.getSessionId();
String codeId = loginEvent.getDetails().get(Details.CODE_ID);
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
String clientSessionState = "1234";
String clientSessionHost = "test-client-host";
OAuthClient.AccessTokenResponse tokenResponse = oauth.clientSessionState(clientSessionState)
.clientSessionHost(clientSessionHost)
.doAccessTokenRequest(code, "password");
String refreshTokenString = tokenResponse.getRefreshToken();
Event tokenEvent = events.expectCodeToToken(codeId, sessionId)
.detail(Details.CLIENT_SESSION_STATE, clientSessionState)
.detail(Details.CLIENT_SESSION_HOST, clientSessionHost)
.assertEvent();
String updatedClientSessionState = "5678";
oauth.clientSessionState(updatedClientSessionState)
.clientSessionHost(clientSessionHost)
.doRefreshTokenRequest(refreshTokenString, "password");
events.expectRefresh(tokenEvent.getDetails().get(Details.REFRESH_TOKEN_ID), sessionId)
.detail(Details.CLIENT_SESSION_STATE, updatedClientSessionState)
.detail(Details.CLIENT_SESSION_HOST, clientSessionHost)
.assertEvent();
}
}