diff --git a/broker/oidc/pom.xml b/broker/oidc/pom.xml index 478cb4738c..5bd4d543e6 100755 --- a/broker/oidc/pom.xml +++ b/broker/oidc/pom.xml @@ -15,6 +15,11 @@ jar + + org.keycloak + keycloak-core + ${project.version} + org.keycloak keycloak-broker-core diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java index f2245e3c22..274171c326 100755 --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java @@ -33,9 +33,11 @@ import org.keycloak.events.EventType; import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.representations.AccessTokenResponse; import org.keycloak.services.managers.EventsManager; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.flows.Flows; +import org.keycloak.util.JsonSerialization; import javax.ws.rs.GET; import javax.ws.rs.QueryParam; @@ -47,6 +49,7 @@ import javax.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URI; import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,6 +60,9 @@ public abstract class AbstractOAuth2IdentityProvider notes, String response) { + AccessTokenResponse tokenResponse = null; + try { + tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class); + } catch (IOException e) { + throw new IdentityBrokerException("Could not decode access token response.", e); + } + String accessToken = tokenResponse.getToken(); + notes.put(FEDERATED_ACCESS_TOKEN, accessToken); + notes.put(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken()); + notes.put(FEDERATED_TOKEN_EXPIRATION, Long.toString(tokenResponse.getExpiresIn())); if (accessToken == null) { throw new IdentityBrokerException("No access token from server."); @@ -164,6 +177,7 @@ public abstract class AbstractOAuth2IdentityProvider userNotes = new HashMap(); + FederatedIdentity federatedIdentity = getFederatedIdentity(userNotes, response); if (getConfig().isStoreToken()) { federatedIdentity.setToken(response); } - return callback.authenticated(new HashMap(), getConfig(), federatedIdentity, state); + return callback.authenticated(userNotes, getConfig(), federatedIdentity, state); } } catch (Exception e) { logger.error("Failed to make identity provider oauth callback", e); diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java old mode 100644 new mode 100755 index 2c43d61d3d..3e052728cc --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java @@ -22,10 +22,32 @@ import org.keycloak.broker.oidc.util.SimpleHttp; import org.keycloak.broker.provider.AuthenticationRequest; import org.keycloak.broker.provider.FederatedIdentity; import org.keycloak.broker.provider.IdentityBrokerException; +import org.keycloak.broker.provider.IdentityProvider; +import org.keycloak.events.Errors; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.IDToken; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.managers.EventsManager; +import org.keycloak.services.messages.Messages; +import org.keycloak.services.resources.IdentityBrokerService; +import org.keycloak.services.resources.RealmsResource; +import org.keycloak.services.resources.flows.Flows; +import org.keycloak.util.JsonSerialization; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; import java.io.IOException; +import java.util.Map; /** * @author Pedro Igor @@ -33,8 +55,8 @@ import java.io.IOException; public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider { public static final String OAUTH2_PARAMETER_PROMPT = "prompt"; - public static final String OIDC_PARAMETER_ID_TOKEN = "id_token"; public static final String SCOPE_OPENID = "openid"; + public static final String FEDERATED_ID_TOKEN = "FEDERATED_ID_TOKEN"; public OIDCIdentityProvider(OIDCIdentityProviderConfig config) { super(config); @@ -46,6 +68,54 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider notes, String response) { + AccessTokenResponse tokenResponse = null; + try { + tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class); + } catch (IOException e) { + throw new IdentityBrokerException("Could not decode access token response.", e); + } + String accessToken = tokenResponse.getToken(); if (accessToken == null) { throw new IdentityBrokerException("No access_token from server."); } - String idToken = extractTokenFromResponse(response, OIDC_PARAMETER_ID_TOKEN); + String encodedIdToken = tokenResponse.getIdToken(); - validateIdToken(idToken); + notes.put(FEDERATED_ACCESS_TOKEN, accessToken); + notes.put(FEDERATED_ID_TOKEN, encodedIdToken); + notes.put(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken()); + notes.put(FEDERATED_TOKEN_EXPIRATION, Long.toString(tokenResponse.getExpiresIn())); + + + IDToken idToken = validateIdToken(encodedIdToken); try { - JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl()) - .header("Authorization", "Bearer " + accessToken) - .asJson(); + String id = idToken.getSubject(); + String name = idToken.getName(); + String preferredUsername = idToken.getPreferredUsername(); + String email = idToken.getEmail(); - String id = getJsonProperty(userInfo, "sub"); - String name = getJsonProperty(userInfo, "name"); - String preferredUsername = getJsonProperty(userInfo, "preferred_username"); - String email = getJsonProperty(userInfo, "email"); + if (id == null || name == null || preferredUsername == null || email == null && getConfig().getUserInfoUrl() != null) { + JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl()) + .header("Authorization", "Bearer " + accessToken) + .asJson(); + + id = getJsonProperty(userInfo, "sub"); + name = getJsonProperty(userInfo, "name"); + preferredUsername = getJsonProperty(userInfo, "preferred_username"); + email = getJsonProperty(userInfo, "email"); + } FederatedIdentity identity = new FederatedIdentity(id); @@ -106,16 +195,16 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider otherClaims = new HashMap(); + + + public String getToken() { return token; } @@ -96,4 +105,15 @@ public class AccessTokenResponse { public void setSessionState(String sessionState) { this.sessionState = sessionState; } + + @JsonAnyGetter + public Map getOtherClaims() { + return otherClaims; + } + + @JsonAnySetter + public void setOtherClaims(String name, Object value) { + otherClaims.put(name, value); + } + } diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index a835b66441..4d4f41b9fe 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -76,7 +76,6 @@ import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE; * * @author Pedro Igor */ -@Path("/broker") public class IdentityBrokerService implements IdentityProvider.AuthenticationCallback { private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class); diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index 0feccb47ec..1f43e9af0e 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -79,6 +79,10 @@ public class RealmsResource { return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getProtocol"); } + public static UriBuilder brokerUrl(UriInfo uriInfo) { + return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getBrokerService"); + } + @Path("{realm}/login-status-iframe.html") @Deprecated public Object getLoginStatusIframe(final @PathParam("realm") String name,