Skip to content

Commit

Permalink
[KEYCLOAK-992] - Token retrieval from brokered idps.
Browse files Browse the repository at this point in the history
  • Loading branch information
pedroigor committed Feb 7, 2015
1 parent 9b62543 commit 4b1ba70
Show file tree
Hide file tree
Showing 64 changed files with 1,176 additions and 153 deletions.
Expand Up @@ -30,6 +30,7 @@ public class FederatedIdentity {
private String firstName; private String firstName;
private String lastName; private String lastName;
private String email; private String email;
private String token;


public FederatedIdentity(String id) { public FederatedIdentity(String id) {
if (id == null) { if (id == null) {
Expand Down Expand Up @@ -84,4 +85,11 @@ public void setEmail(String email) {
} }




public void setToken(String token) {
this.token = token;
}

public String getToken() {
return this.token;
}
} }
Expand Up @@ -17,9 +17,12 @@
*/ */
package org.keycloak.broker.provider; package org.keycloak.broker.provider;


import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.keycloak.provider.Provider; import org.keycloak.provider.Provider;


import javax.ws.rs.core.Response;

/** /**
* @author Pedro Igor * @author Pedro Igor
*/ */
Expand Down Expand Up @@ -64,4 +67,6 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
* @return * @return
*/ */
AuthenticationResponse handleResponse(AuthenticationRequest request); AuthenticationResponse handleResponse(AuthenticationRequest request);

Response retrieveToken(FederatedIdentityModel identity);
} }
Expand Up @@ -25,7 +25,9 @@
import org.keycloak.broker.provider.AuthenticationRequest; import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse; import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity; import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.models.FederatedIdentityModel;


import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
Expand Down Expand Up @@ -100,7 +102,13 @@ public AuthenticationResponse handleResponse(AuthenticationRequest request) {
.param(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri()) .param(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri())
.param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE).asString(); .param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE).asString();


return doHandleResponse(response); FederatedIdentity federatedIdentity = getFederatedIdentity(response);

if (getConfig().isStoreToken()) {
federatedIdentity.setToken(response);
}

return AuthenticationResponse.end(federatedIdentity);
} }


throw new RuntimeException("No authorization code from identity provider."); throw new RuntimeException("No authorization code from identity provider.");
Expand All @@ -110,23 +118,22 @@ public AuthenticationResponse handleResponse(AuthenticationRequest request) {
} }


@Override @Override
public C getConfig() { public Response retrieveToken(FederatedIdentityModel identity) {
return super.getConfig(); return Response.ok(identity.getToken()).build();
} }


protected AuthenticationResponse doHandleResponse(String response) throws IOException { @Override
String token = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN); public C getConfig() {

return super.getConfig();
if (token == null) {
throw new RuntimeException("No access token from server.");
}

return AuthenticationResponse.end(getFederatedIdentity(token));
} }


protected String extractTokenFromResponse(String response, String tokenName) throws IOException { protected String extractTokenFromResponse(String response, String tokenName) {
if (response.startsWith("{")) { if (response.startsWith("{")) {
return mapper.readTree(response).get(tokenName).getTextValue(); try {
return mapper.readTree(response).get(tokenName).getTextValue();
} catch (IOException e) {
throw new RuntimeException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
}
} else { } else {
Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response); Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);


Expand All @@ -138,9 +145,21 @@ protected String extractTokenFromResponse(String response, String tokenName) thr
return null; return null;
} }


protected FederatedIdentity getFederatedIdentity(String accessToken) { protected FederatedIdentity getFederatedIdentity(String response) {
throw new RuntimeException("Not implemented."); String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
};
if (accessToken == null) {
throw new RuntimeException("No access token from server.");
}

return doGetFederatedIdentity(accessToken);
}

protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
return null;
}

;


protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) { protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
return UriBuilder.fromPath(getConfig().getAuthorizationUrl()) return UriBuilder.fromPath(getConfig().getAuthorizationUrl())
Expand Down
Expand Up @@ -20,7 +20,6 @@
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.keycloak.broker.oidc.util.SimpleHttp; import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.AuthenticationRequest; import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity; import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;


Expand Down Expand Up @@ -59,7 +58,7 @@ protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
} }


@Override @Override
protected AuthenticationResponse doHandleResponse(String response) throws IOException { protected FederatedIdentity getFederatedIdentity(String response) {
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN); String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);


if (accessToken == null) { if (accessToken == null) {
Expand Down Expand Up @@ -96,7 +95,11 @@ protected AuthenticationResponse doHandleResponse(String response) throws IOExce


identity.setUsername(preferredUsername); identity.setUsername(preferredUsername);


return AuthenticationResponse.end(identity); if (getConfig().isStoreToken()) {
identity.setToken(response);
}

return identity;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Could not fetch attributes from userinfo endpoint.", e); throw new RuntimeException("Could not fetch attributes from userinfo endpoint.", e);
} }
Expand Down
Expand Up @@ -22,6 +22,7 @@
import org.keycloak.broker.provider.AuthenticationRequest; import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse; import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity; import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder; import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder; import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
import org.picketlink.common.constants.JBossSAMLConstants; import org.picketlink.common.constants.JBossSAMLConstants;
Expand Down Expand Up @@ -54,6 +55,7 @@
import org.w3c.dom.Node; import org.w3c.dom.Node;


import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
Expand Down Expand Up @@ -140,8 +142,14 @@ public String getRelayState(AuthenticationRequest request) {


@Override @Override
public AuthenticationResponse handleResponse(AuthenticationRequest request) { public AuthenticationResponse handleResponse(AuthenticationRequest request) {
String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);

if (samlResponse == null) {
throw new RuntimeException("No response from SAML identity provider.");
}

try { try {
AssertionType assertion = getAssertion(request); AssertionType assertion = getAssertion(samlResponse, request);
SubjectType subject = assertion.getSubject(); SubjectType subject = assertion.getSubject();
STSubType subType = subject.getSubType(); STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID(); NameIDType subjectNameID = (NameIDType) subType.getBaseID();
Expand All @@ -153,19 +161,22 @@ public AuthenticationResponse handleResponse(AuthenticationRequest request) {
identity.setEmail(subjectNameID.getValue()); identity.setEmail(subjectNameID.getValue());
} }


if (getConfig().isStoreToken()) {
identity.setToken(samlResponse);
}

return AuthenticationResponse.end(identity); return AuthenticationResponse.end(identity);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Could not process response from SAML identity provider.", e); throw new RuntimeException("Could not process response from SAML identity provider.", e);
} }
} }


private AssertionType getAssertion(AuthenticationRequest request) throws Exception { @Override
String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER); public Response retrieveToken(FederatedIdentityModel identity) {

return Response.ok(identity.getToken()).build();
if (samlResponse == null) { }
throw new RuntimeException("No response from SAML identity provider.");
}


private AssertionType getAssertion(String samlResponse, AuthenticationRequest request) throws Exception {
SAML2Request saml2Request = new SAML2Request(); SAML2Request saml2Request = new SAML2Request();
ResponseType responseType; ResponseType responseType;


Expand Down
Expand Up @@ -8,6 +8,7 @@
<column name="REALM_ID" type="VARCHAR(255)"/> <column name="REALM_ID" type="VARCHAR(255)"/>
<column name="FEDERATED_USER_ID" type="VARCHAR(255)"/> <column name="FEDERATED_USER_ID" type="VARCHAR(255)"/>
<column name="FEDERATED_USERNAME" type="VARCHAR(255)"/> <column name="FEDERATED_USERNAME" type="VARCHAR(255)"/>
<column name="TOKEN" type="TEXT"/>
<column name="USER_ID" type="VARCHAR(36)"> <column name="USER_ID" type="VARCHAR(36)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
Expand All @@ -21,6 +22,7 @@
<column name="PROVIDER_NAME" type="VARCHAR(255)"/> <column name="PROVIDER_NAME" type="VARCHAR(255)"/>
<column name="PROVIDER_ID" type="VARCHAR(255)"/> <column name="PROVIDER_ID" type="VARCHAR(255)"/>
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/> <column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/>
<column name="STORE_TOKEN" type="BOOLEAN(1)"/>
<column name="REALM_ID" type="VARCHAR(36)"/> <column name="REALM_ID" type="VARCHAR(36)"/>
</createTable> </createTable>
<createTable tableName="IDENTITY_PROVIDER_CONFIG"> <createTable tableName="IDENTITY_PROVIDER_CONFIG">
Expand All @@ -32,6 +34,14 @@
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
</createTable> </createTable>
<createTable tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER">
<column name="CLIENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="INTERNAL_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<addColumn tableName="CLIENT"> <addColumn tableName="CLIENT">
<column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false"/> <column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false"/>
</addColumn> </addColumn>
Expand All @@ -41,5 +51,8 @@
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/> <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/> <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/> <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="INTERNAL_ID" baseTableName="CLIENT_ALLOWED_IDENTITY_PROVIDER" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
<addUniqueConstraint columnNames="INTERNAL_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER"/>
<addUniqueConstraint columnNames="PROVIDER_NONIMAL_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>
Expand Up @@ -28,6 +28,7 @@ public class ApplicationRepresentation {
protected Boolean fullScopeAllowed; protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout; protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes; protected Map<String, Integer> registeredNodes;
protected List<String> allowedIdentityProviders;


public String getId() { public String getId() {
return id; return id;
Expand Down Expand Up @@ -188,4 +189,12 @@ public Boolean isFrontchannelLogout() {
public void setFrontchannelLogout(Boolean frontchannelLogout) { public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout; this.frontchannelLogout = frontchannelLogout;
} }

public List<String> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
}

public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
this.allowedIdentityProviders = allowedIdentityProviders;
}
} }
Expand Up @@ -30,6 +30,7 @@ public class IdentityProviderRepresentation {
protected String name; protected String name;
protected boolean enabled = true; protected boolean enabled = true;
protected boolean updateProfileFirstLogin = true; protected boolean updateProfileFirstLogin = true;
protected boolean storeToken;
protected String groupName; protected String groupName;
protected Map<String, String> config = new HashMap<String, String>(); protected Map<String, String> config = new HashMap<String, String>();


Expand Down Expand Up @@ -65,14 +66,6 @@ public void setConfig(Map<String, String> config) {
this.config = config; this.config = config;
} }


public String getGroupName() {
return this.groupName;
}

public void setGroupName(String groupName) {
this.groupName = groupName;
}

public boolean isEnabled() { public boolean isEnabled() {
return this.enabled; return this.enabled;
} }
Expand All @@ -88,4 +81,20 @@ public boolean isUpdateProfileFirstLogin() {
public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) { public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
this.updateProfileFirstLogin = updateProfileFirstLogin; this.updateProfileFirstLogin = updateProfileFirstLogin;
} }

public boolean isStoreToken() {
return this.storeToken;
}

public void setStoreToken(boolean storeToken) {
this.storeToken = storeToken;
}

public String getGroupName() {
return this.groupName;
}

public void setGroupName(String groupName) {
this.groupName = groupName;
}
} }
Expand Up @@ -22,6 +22,7 @@ public class OAuthClientRepresentation {
protected Boolean directGrantsOnly; protected Boolean directGrantsOnly;
protected Boolean fullScopeAllowed; protected Boolean fullScopeAllowed;
protected Boolean frontchannelLogout; protected Boolean frontchannelLogout;
protected List<String> allowedIdentityProviders;




public String getId() { public String getId() {
Expand Down Expand Up @@ -135,4 +136,12 @@ public Boolean isFrontchannelLogout() {
public void setFrontchannelLogout(Boolean frontchannelLogout) { public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout; this.frontchannelLogout = frontchannelLogout;
} }

public List<String> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
}

public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
this.allowedIdentityProviders = allowedIdentityProviders;
}
} }

0 comments on commit 4b1ba70

Please sign in to comment.