[KEYCLOAK-13071] - AuthorizationTokenService swallows Exceptions thrown by KeycloakIdentity
This commit is contained in:
parent
098ec91dd2
commit
44c49d69a7
3 changed files with 119 additions and 46 deletions
|
@ -58,6 +58,8 @@ import org.keycloak.authorization.store.StoreFactory;
|
|||
import org.keycloak.authorization.util.Permissions;
|
||||
import org.keycloak.authorization.util.Tokens;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -125,6 +127,7 @@ public class AuthorizationTokenService {
|
|||
identity = new KeycloakIdentity(authorization.getKeycloakSession(),
|
||||
Tokens.getAccessToken(request.getSubjectToken(), authorization.getKeycloakSession()));
|
||||
} catch (Exception cause) {
|
||||
fireErrorEvent(request.getEvent(), Errors.INVALID_TOKEN, cause);
|
||||
throw new CorsErrorResponseException(request.getCors(), "unauthorized_client", "Invalid identity", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
@ -144,6 +147,7 @@ public class AuthorizationTokenService {
|
|||
try {
|
||||
idToken = new TokenManager().verifyIDTokenSignature(keycloakSession, subjectToken);
|
||||
} catch (Exception cause) {
|
||||
fireErrorEvent(request.getEvent(), Errors.INVALID_SIGNATURE, cause);
|
||||
throw new CorsErrorResponseException(request.getCors(), "unauthorized_client", "Invalid signature", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
@ -152,6 +156,7 @@ public class AuthorizationTokenService {
|
|||
try {
|
||||
identity = new KeycloakIdentity(keycloakSession, idToken);
|
||||
} catch (Exception cause) {
|
||||
fireErrorEvent(request.getEvent(), Errors.INVALID_TOKEN, cause);
|
||||
throw new CorsErrorResponseException(request.getCors(), "unauthorized_client", "Invalid identity", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
@ -165,6 +170,12 @@ public class AuthorizationTokenService {
|
|||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static void fireErrorEvent(EventBuilder event, String error, Exception cause) {
|
||||
event.detail(Details.REASON, cause == null || cause.getMessage() == null ? "<unknown>" : cause.getMessage())
|
||||
.error(error);
|
||||
logger.debug(event.getEvent().getType(), cause);
|
||||
}
|
||||
|
||||
public Response authorize(KeycloakAuthorizationRequest request) {
|
||||
if (request == null) {
|
||||
throw new CorsErrorResponseException(request.getCors(), OAuthErrorException.INVALID_GRANT, "Invalid authorization request.", Status.BAD_REQUEST);
|
||||
|
|
|
@ -110,56 +110,60 @@ public class KeycloakIdentity implements Identity {
|
|||
attributes.put(fieldName, values);
|
||||
}
|
||||
}
|
||||
|
||||
if (token instanceof AccessToken) {
|
||||
this.accessToken = AccessToken.class.cast(token);
|
||||
} else {
|
||||
UserSessionProvider sessions = keycloakSession.sessions();
|
||||
UserSessionModel userSession = sessions.getUserSession(realm, token.getSessionState());
|
||||
|
||||
if (userSession == null) {
|
||||
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
||||
AuthenticatedClientSessionModel clientSessionModel = userSession.getAuthenticatedClientSessions().get(client.getId());
|
||||
|
||||
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSessionModel, keycloakSession);
|
||||
this.accessToken = new TokenManager().createClientAccessToken(keycloakSession, realm, client, userSession.getUser(), userSession, clientSessionCtx);
|
||||
}
|
||||
|
||||
AccessToken.Access realmAccess = this.accessToken.getRealmAccess();
|
||||
|
||||
if (realmAccess != null) {
|
||||
attributes.put("kc.realm.roles", realmAccess.getRoles());
|
||||
}
|
||||
|
||||
Map<String, AccessToken.Access> resourceAccess = this.accessToken.getResourceAccess();
|
||||
|
||||
if (resourceAccess != null) {
|
||||
resourceAccess.forEach((clientId, access) -> attributes.put("kc.client." + clientId + ".roles", access.getRoles()));
|
||||
}
|
||||
|
||||
ClientModel clientModel = getTargetClient();
|
||||
UserModel clientUser = null;
|
||||
|
||||
if (clientModel != null) {
|
||||
clientUser = this.keycloakSession.users().getServiceAccount(clientModel);
|
||||
}
|
||||
|
||||
UserModel userSession = getUserFromSessionState();
|
||||
|
||||
this.resourceServer = clientUser != null && userSession.getId().equals(clientUser.getId());
|
||||
|
||||
if (resourceServer) {
|
||||
this.id = clientModel.getId();
|
||||
} else {
|
||||
this.id = userSession.getId();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error while reading attributes from security token.", e);
|
||||
}
|
||||
|
||||
if (token instanceof AccessToken) {
|
||||
this.accessToken = AccessToken.class.cast(token);
|
||||
} else {
|
||||
UserSessionProvider sessions = keycloakSession.sessions();
|
||||
UserSessionModel userSession = sessions.getUserSession(realm, token.getSessionState());
|
||||
|
||||
if (userSession == null) {
|
||||
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
||||
}
|
||||
|
||||
if (userSession == null) {
|
||||
throw new RuntimeException("No active session associated with the token");
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
||||
AuthenticatedClientSessionModel clientSessionModel = userSession.getAuthenticatedClientSessions().get(client.getId());
|
||||
|
||||
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSessionModel, keycloakSession);
|
||||
this.accessToken = new TokenManager().createClientAccessToken(keycloakSession, realm, client, userSession.getUser(), userSession, clientSessionCtx);
|
||||
}
|
||||
|
||||
AccessToken.Access realmAccess = this.accessToken.getRealmAccess();
|
||||
|
||||
if (realmAccess != null) {
|
||||
attributes.put("kc.realm.roles", realmAccess.getRoles());
|
||||
}
|
||||
|
||||
Map<String, AccessToken.Access> resourceAccess = this.accessToken.getResourceAccess();
|
||||
|
||||
if (resourceAccess != null) {
|
||||
resourceAccess.forEach((clientId, access) -> attributes.put("kc.client." + clientId + ".roles", access.getRoles()));
|
||||
}
|
||||
|
||||
ClientModel clientModel = getTargetClient();
|
||||
UserModel clientUser = null;
|
||||
|
||||
if (clientModel != null) {
|
||||
clientUser = this.keycloakSession.users().getServiceAccount(clientModel);
|
||||
}
|
||||
|
||||
UserModel userSession = getUserFromSessionState();
|
||||
|
||||
this.resourceServer = clientUser != null && userSession.getId().equals(clientUser.getId());
|
||||
|
||||
if (resourceServer) {
|
||||
this.id = clientModel.getId();
|
||||
} else {
|
||||
this.id = userSession.getId();
|
||||
}
|
||||
|
||||
this.attributes = Attributes.from(attributes);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ import org.keycloak.authorization.client.Configuration;
|
|||
import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
|
||||
import org.keycloak.authorization.client.util.HttpResponseException;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
||||
|
@ -70,7 +71,9 @@ import org.keycloak.representations.AccessToken;
|
|||
import org.keycloak.representations.AccessToken.Authorization;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
|
||||
|
@ -1930,6 +1933,61 @@ public class EntitlementAPITest extends AbstractAuthzTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidTokenSignature() throws Exception {
|
||||
RealmEventsConfigRepresentation eventConfig = getRealm().getRealmEventsConfig();
|
||||
|
||||
eventConfig.setEventsEnabled(true);
|
||||
eventConfig.setEnabledEventTypes(Arrays.asList(EventType.PERMISSION_TOKEN_ERROR.name()));
|
||||
|
||||
getRealm().updateRealmEventsConfig(eventConfig);
|
||||
|
||||
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
|
||||
AuthorizationResource authorization = client.authorization();
|
||||
|
||||
JSPolicyRepresentation policy = new JSPolicyRepresentation();
|
||||
|
||||
policy.setName(KeycloakModelUtils.generateId());
|
||||
policy.setCode("$evaluation.grant();");
|
||||
|
||||
authorization.policies().js().create(policy).close();
|
||||
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Sensors");
|
||||
|
||||
try (Response response = authorization.resources().create(resource)) {
|
||||
response.readEntity(ResourceRepresentation.class);
|
||||
}
|
||||
|
||||
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
||||
|
||||
permission.setName("View Sensor");
|
||||
permission.addPolicy(policy.getName());
|
||||
|
||||
authorization.permissions().resource().create(permission).close();
|
||||
|
||||
String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
|
||||
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
|
||||
request.addPermission("Sensors");
|
||||
request.setSubjectToken(accessToken + "i");
|
||||
|
||||
try {
|
||||
authzClient.authorization().authorize(request);
|
||||
fail("should fail, session invalidated");
|
||||
} catch (Exception e) {
|
||||
Throwable expected = e.getCause();
|
||||
assertEquals(400, HttpResponseException.class.cast(expected).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected).toString().contains("unauthorized_client"));
|
||||
}
|
||||
|
||||
List<EventRepresentation> events = getRealm()
|
||||
.getEvents(Arrays.asList(EventType.PERMISSION_TOKEN_ERROR.name()), null, null, null, null, null, null, null);
|
||||
assertEquals(1, events.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDenyScopeNotManagedByScopePolicy() throws Exception {
|
||||
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
|
||||
|
|
Loading…
Reference in a new issue