diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java index 6abe58c9245..1f019d7b7ba 100755 --- a/core/src/main/java/org/keycloak/representations/AccessToken.java +++ b/core/src/main/java/org/keycloak/representations/AccessToken.java @@ -22,9 +22,11 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import org.keycloak.OAuth2Constants; import org.keycloak.TokenCategory; import org.keycloak.representations.idm.authorization.Permission; @@ -147,6 +149,9 @@ public class AccessToken extends IDToken { @JsonProperty("scope") protected String scope; + @JsonProperty(OAuth2Constants.AUTHORIZATION_DETAILS) + protected List authorizationDetails; + @JsonIgnore public Map getResourceAccess() { return resourceAccess == null ? Collections.emptyMap() : resourceAccess; @@ -274,6 +279,14 @@ public class AccessToken extends IDToken { this.scope = scope; } + public List getAuthorizationDetails() { + return authorizationDetails; + } + + public void setAuthorizationDetails(List authorizationDetails) { + this.authorizationDetails = authorizationDetails; + } + @Override public TokenCategory getCategory() { return TokenCategory.ACCESS; diff --git a/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java b/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java index 49fac581fe2..6f1714d242c 100755 --- a/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java +++ b/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java @@ -18,8 +18,11 @@ package org.keycloak.representations; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.keycloak.OAuth2Constants; + import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonProperty; @@ -61,6 +64,9 @@ public class AccessTokenResponse { @JsonProperty("scope") protected String scope; + @JsonProperty(OAuth2Constants.AUTHORIZATION_DETAILS) + protected List authorizationDetails; + @JsonProperty("error") protected String error; @@ -78,6 +84,14 @@ public class AccessTokenResponse { this.scope = scope; } + public List getAuthorizationDetails() { + return authorizationDetails; + } + + public void setAuthorizationDetails(List authorizationDetails) { + this.authorizationDetails = authorizationDetails; + } + public String getToken() { return token; } diff --git a/core/src/main/java/org/keycloak/representations/AuthorizationDetailsJSONRepresentation.java b/core/src/main/java/org/keycloak/representations/AuthorizationDetailsJSONRepresentation.java index 1b69fdc5883..35862a0b840 100644 --- a/core/src/main/java/org/keycloak/representations/AuthorizationDetailsJSONRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/AuthorizationDetailsJSONRepresentation.java @@ -30,7 +30,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; * The JSON representation of a Rich Authorization Request's "authorization_details" object. * * @author Daniel Gozalo - * @see {@link Request parameter "authorization_details"} + * @see {@link Request parameter "authorization_details"} */ public class AuthorizationDetailsJSONRepresentation implements Serializable { @@ -156,4 +156,6 @@ public class AuthorizationDetailsJSONRepresentation implements Serializable { public int hashCode() { return Objects.hash(type, locations, actions, datatypes, identifier, privileges, customData); } + + } diff --git a/core/src/main/java/org/keycloak/representations/AuthorizationDetailsResponse.java b/core/src/main/java/org/keycloak/representations/AuthorizationDetailsResponse.java new file mode 100644 index 00000000000..78688c67637 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/AuthorizationDetailsResponse.java @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.representations; + +import java.util.HashMap; +import java.util.Map; + +/** + * Generic response object for authorization details processing. + * This class serves as a base for different types of authorization details responses + * from various RAR (Rich Authorization Requests) implementations. + * + * @author Forkim Akwichek + */ +public class AuthorizationDetailsResponse extends AuthorizationDetailsJSONRepresentation { + + // Map of parsers for specific values of "type" claim of authorizationDetails + private static final Map> PARSERS = new HashMap<>(); + + public static void registerParser(String type, AuthorizationDetailsResponseParser parser) { + PARSERS.put(type, parser); + } + + public T asSubtype(Class clazz) { + AuthorizationDetailsResponseParser parser = (AuthorizationDetailsResponseParser) PARSERS.get(getType()); + if (parser == null) { + throw new IllegalArgumentException("Unsupported to parse response of type '" + getType() + "' to the type '" + clazz + + "'. Please make sure that corresponding parser is registered."); + } + return parser.asSubtype(this); + } + + /** + * Parser, which is able to create specific subtype of {@link AuthorizationDetailsResponse} in performant way + */ + public interface AuthorizationDetailsResponseParser { + + T asSubtype(AuthorizationDetailsResponse response); + + } +} diff --git a/core/src/main/java/org/keycloak/representations/RefreshToken.java b/core/src/main/java/org/keycloak/representations/RefreshToken.java index d73c5675679..55758986964 100755 --- a/core/src/main/java/org/keycloak/representations/RefreshToken.java +++ b/core/src/main/java/org/keycloak/representations/RefreshToken.java @@ -44,6 +44,7 @@ public class RefreshToken extends AccessToken { this.nonce = token.nonce; this.audience = new String[] { token.issuer }; this.scope = token.scope; + this.authorizationDetails = token.authorizationDetails; } /** @@ -54,14 +55,7 @@ public class RefreshToken extends AccessToken { * always be included in the response */ public RefreshToken(AccessToken token, Confirmation confirmation) { - this(); - this.issuer = token.issuer; - this.subject = token.subject; - this.issuedFor = token.issuedFor; - this.sessionId = token.sessionId; - this.nonce = token.nonce; - this.audience = new String[] { token.issuer }; - this.scope = token.scope; + this(token); this.confirmation = confirmation; } diff --git a/integration/client-cli/admin-cli/pom.xml b/integration/client-cli/admin-cli/pom.xml index cbc4683ab18..b4f5451514f 100755 --- a/integration/client-cli/admin-cli/pom.xml +++ b/integration/client-cli/admin-cli/pom.xml @@ -80,6 +80,8 @@ org/keycloak/representations/adapters/config/** org/keycloak/representations/adapters/action/** org/keycloak/representations/AccessTokenResponse.class + org/keycloak/representations/AuthorizationDetailsJSONRepresentation.class + org/keycloak/representations/AuthorizationDetailsResponse.class