[KEYCLOAK-18807] - Fixing claims in JARM responses

This commit is contained in:
Pedro Igor 2021-07-19 14:42:29 -03:00 committed by Marek Posolda
parent 13a08362d4
commit 730d4e8ac9
3 changed files with 52 additions and 7 deletions

View file

@ -24,6 +24,8 @@ public interface OAuth2Constants {
String CODE = "code"; String CODE = "code";
String TOKEN = "token";
String CLIENT_ID = "client_id"; String CLIENT_ID = "client_id";
String CLIENT_SECRET = "client_secret"; String CLIENT_SECRET = "client_secret";

View file

@ -17,14 +17,19 @@
package org.keycloak.protocol.oidc.utils; package org.keycloak.protocol.oidc.utils;
import org.keycloak.OAuth2Constants;
import org.keycloak.common.util.Encode; import org.keycloak.common.util.Encode;
import org.keycloak.common.util.HtmlUtils; import org.keycloak.common.util.HtmlUtils;
import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AuthorizationResponseToken; import org.keycloak.representations.AuthorizationResponseToken;
import org.keycloak.services.Urls;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -183,10 +188,10 @@ public abstract class OIDCRedirectUriBuilder {
// https://openid.net/specs/openid-financial-api-jarm-ID1.html // https://openid.net/specs/openid-financial-api-jarm-ID1.html
private static class JWTRedirectUriBuilder extends OIDCRedirectUriBuilder { private static class JWTRedirectUriBuilder extends OIDCRedirectUriBuilder {
private OIDCResponseMode responseMode; private final OIDCResponseMode responseMode;
private AuthorizationResponseToken responseJWT; private final AuthorizationResponseToken responseJWT;
private KeycloakSession session; private final KeycloakSession session;
private AuthenticatedClientSessionModel clientSession; private final AuthenticatedClientSessionModel clientSession;
public JWTRedirectUriBuilder(KeycloakUriBuilder uriBuilder, OIDCResponseMode responseMode, KeycloakSession session, AuthenticatedClientSessionModel clientSession) { public JWTRedirectUriBuilder(KeycloakUriBuilder uriBuilder, OIDCResponseMode responseMode, KeycloakSession session, AuthenticatedClientSessionModel clientSession) {
super(uriBuilder); super(uriBuilder);
@ -204,12 +209,23 @@ public abstract class OIDCRedirectUriBuilder {
@Override @Override
public Response build() { public Response build() {
KeycloakContext context = session.getContext();
ClientModel client = context.getClient();
RealmModel realm = client.getRealm();
responseJWT.issuer(Urls.realmIssuer(context.getUri().getBaseUri(), realm.getName()));
responseJWT.audience(client.getClientId());
responseJWT.exp((long) (Time.currentTime() + realm.getAccessCodeLifespan()));
if(clientSession != null) { if(clientSession != null) {
responseJWT.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER)); responseJWT.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER));
responseJWT.audience(clientSession.getClient().getClientId()); String responseType = clientSession.getNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
responseJWT.setOtherClaims("scope", clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM));
responseJWT.exp((long) (Time.currentTime() + clientSession.getRealm().getAccessCodeLifespan())); if (OAuth2Constants.TOKEN.equals(responseType)) {
responseJWT.setOtherClaims(OAuth2Constants.SCOPE, clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM));
}
} }
switch (responseMode) { switch (responseMode) {
case QUERY_JWT: case QUERY_JWT:
return buildQueryResponse(); return buildQueryResponse();

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.oidc;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
@ -38,6 +39,9 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class AuthorizationTokenResponseModeTest extends AbstractTestRealmKeycloakTest { public class AuthorizationTokenResponseModeTest extends AbstractTestRealmKeycloakTest {
@ -76,6 +80,8 @@ public class AuthorizationTokenResponseModeTest extends AbstractTestRealmKeycloa
assertEquals("test-app", responseToken.getAudience()[0]); assertEquals("test-app", responseToken.getAudience()[0]);
Assert.assertNotNull(responseToken.getOtherClaims().get("code")); Assert.assertNotNull(responseToken.getOtherClaims().get("code"));
// should not return code when response_type not 'token'
assertFalse(responseToken.getOtherClaims().containsKey(OAuth2Constants.SCOPE));
assertEquals("OpenIdConnect.AuthenticationProperties=2302984sdlk", responseToken.getOtherClaims().get("state")); assertEquals("OpenIdConnect.AuthenticationProperties=2302984sdlk", responseToken.getOtherClaims().get("state"));
Assert.assertNull(responseToken.getOtherClaims().get("error")); Assert.assertNull(responseToken.getOtherClaims().get("error"));
@ -212,6 +218,27 @@ public class AuthorizationTokenResponseModeTest extends AbstractTestRealmKeycloa
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().assertEvent(); events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().assertEvent();
} }
@Test
public void testErrorObjectExpectedClaims() throws Exception {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").implicitFlow(true);
oauth.responseMode("query.jwt");
oauth.responseType("code id_token");
oauth.stateParamHardcoded("OpenIdConnect.AuthenticationProperties=2302984sdlk");
oauth.nonce("123456");
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
AuthorizationResponseToken responseToken = oauth.verifyAuthorizationResponseToken(errorResponse.getResponse());
assertNotNull(responseToken.getIssuer());
assertNotNull(responseToken.getExp());
assertNotNull(responseToken.getAudience());
assertNotEquals(0, responseToken.getAudience().length);
assertTrue(responseToken.getOtherClaims().containsKey("error"));
assertTrue(responseToken.getOtherClaims().containsKey("error_description"));
}
@Override @Override
public void configureTestRealm(RealmRepresentation testRealm) { public void configureTestRealm(RealmRepresentation testRealm) {
} }