Skip to content

Commit d7615d6

Browse files
committed
KEYCLOAK-2122 Configuration of AssertionConsumerServiceUrl in SAML adapter
1 parent f1f2040 commit d7615d6

File tree

16 files changed

+688
-41
lines changed

16 files changed

+688
-41
lines changed

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.keycloak.adapters.saml;
1919

20+
import org.keycloak.adapters.saml.SamlDeployment.IDP.SingleSignOnService;
2021
import org.jboss.logging.Logger;
2122
import org.keycloak.adapters.spi.AuthChallenge;
2223
import org.keycloak.adapters.spi.HttpFacade;
@@ -95,21 +96,22 @@ public static SAML2AuthnRequestBuilder buildSaml2AuthnRequestBuilder(SamlDeploym
9596
nameIDPolicyFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
9697
}
9798

99+
SingleSignOnService sso = deployment.getIDP().getSingleSignOnService();
98100
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
99-
.destination(deployment.getIDP().getSingleSignOnService().getRequestBindingUrl())
101+
.destination(sso.getRequestBindingUrl())
100102
.issuer(issuerURL)
101103
.forceAuthn(deployment.isForceAuthentication()).isPassive(deployment.isIsPassive())
102104
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
103-
if (deployment.getIDP().getSingleSignOnService().getResponseBinding() != null) {
105+
if (sso.getResponseBinding() != null) {
104106
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
105-
if (deployment.getIDP().getSingleSignOnService().getResponseBinding() == SamlDeployment.Binding.POST) {
107+
if (sso.getResponseBinding() == SamlDeployment.Binding.POST) {
106108
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
107109
}
108110
authnRequestBuilder.protocolBinding(protocolBinding);
109111

110112
}
111-
if (deployment.getAssertionConsumerServiceUrl() != null) {
112-
authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
113+
if (sso.getAssertionConsumerServiceUrl() != null) {
114+
authnRequestBuilder.assertionConsumerUrl(sso.getAssertionConsumerServiceUrl());
113115
}
114116
return authnRequestBuilder;
115117
}

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.keycloak.rotation.CompositeKeyLocator;
3232
import org.keycloak.rotation.HardcodedKeyLocator;
3333
import org.keycloak.rotation.KeyLocator;
34+
import java.net.URI;
3435

3536
/**
3637
* @author <a href="mailto:[email protected]">Bill Burke</a>
@@ -45,6 +46,7 @@ public static class DefaultSingleSignOnService implements IDP.SingleSignOnServic
4546
private Binding requestBinding;
4647
private Binding responseBinding;
4748
private String requestBindingUrl;
49+
private URI assertionConsumerServiceUrl;
4850

4951
@Override
5052
public boolean signRequest() {
@@ -76,6 +78,15 @@ public String getRequestBindingUrl() {
7678
return requestBindingUrl;
7779
}
7880

81+
@Override
82+
public URI getAssertionConsumerServiceUrl() {
83+
return assertionConsumerServiceUrl;
84+
}
85+
86+
public void setAssertionConsumerServiceUrl(URI assertionConsumerServiceUrl) {
87+
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
88+
}
89+
7990
public void setSignRequest(boolean signRequest) {
8091
this.signRequest = signRequest;
8192
}
@@ -277,7 +288,6 @@ public void setClient(HttpClient client) {
277288
private boolean turnOffChangeSessionIdOnLogin;
278289
private PrivateKey decryptionKey;
279290
private KeyPair signingKeyPair;
280-
private String assertionConsumerServiceUrl;
281291
private Set<String> roleAttributeNames;
282292
private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
283293
private String principalAttributeName;
@@ -340,11 +350,6 @@ public KeyPair getSigningKeyPair() {
340350
return signingKeyPair;
341351
}
342352

343-
@Override
344-
public String getAssertionConsumerServiceUrl() {
345-
return assertionConsumerServiceUrl;
346-
}
347-
348353
@Override
349354
public Set<String> getRoleAttributeNames() {
350355
return roleAttributeNames;
@@ -396,10 +401,6 @@ public void setSigningKeyPair(KeyPair signingKeyPair) {
396401
this.signingKeyPair = signingKeyPair;
397402
}
398403

399-
public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
400-
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
401-
}
402-
403404
public void setRoleAttributeNames(Set<String> roleAttributeNames) {
404405
this.roleAttributeNames = roleAttributeNames;
405406
}

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Set;
2626
import org.apache.http.client.HttpClient;
2727
import org.keycloak.rotation.KeyLocator;
28+
import java.net.URI;
2829

2930
/**
3031
* Represents SAML deployment configuration.
@@ -103,8 +104,24 @@ public interface SingleSignOnService {
103104
*/
104105
boolean validateAssertionSignature();
105106
Binding getRequestBinding();
107+
/**
108+
* SAML allows the client to request what binding type it wants authn responses to use. The default is
109+
* that the client will not request a specific binding type for responses.
110+
* @return
111+
*/
106112
Binding getResponseBinding();
113+
/**
114+
* Returns URL for the IDP login service that the client will send requests to.
115+
* @return
116+
*/
107117
String getRequestBindingUrl();
118+
/**
119+
* Returns URI where the IdP should send the responses to. The default is
120+
* that the client will not request a specific assertion consumer service URL.
121+
* This property is typically accompanied by the ProtocolBinding attribute.
122+
* @return
123+
*/
124+
URI getAssertionConsumerServiceUrl();
108125
}
109126

110127
public interface SingleLogoutService {
@@ -141,7 +158,6 @@ public interface SingleLogoutService {
141158
KeyPair getSigningKeyPair();
142159
String getSignatureCanonicalizationMethod();
143160
SignatureAlgorithm getSignatureAlgorithm();
144-
String getAssertionConsumerServiceUrl();
145161
String getLogoutPage();
146162

147163
Set<String> getRoleAttributeNames();

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public static class SingleSignOnService implements Serializable {
3232
private String requestBinding;
3333
private String responseBinding;
3434
private String bindingUrl;
35+
private String assertionConsumerServiceUrl;
3536
private boolean validateAssertionSignature;
3637

3738
public boolean isSignRequest() {
@@ -81,6 +82,14 @@ public String getBindingUrl() {
8182
public void setBindingUrl(String bindingUrl) {
8283
this.bindingUrl = bindingUrl;
8384
}
85+
86+
public String getAssertionConsumerServiceUrl() {
87+
return assertionConsumerServiceUrl;
88+
}
89+
90+
public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
91+
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
92+
}
8493
}
8594

8695
public static class SingleLogoutService implements Serializable {

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public class ConfigXmlConstants {
7272
public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
7373
public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
7474
public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
75+
public static final String ASSERTION_CONSUMER_SERVICE_URL_ATTR = "assertionConsumerServiceUrl";
7576

7677
public static final String HTTP_CLIENT_ELEMENT = "HttpClient";
7778
public static final String ALLOW_ANY_HOSTNAME_ATTR = "allowAnyHostname";

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.util.HashSet;
4242
import java.util.Set;
4343
import org.keycloak.adapters.cloned.HttpClientBuilder;
44+
import java.net.URI;
4445

4546
/**
4647
* @author <a href="mailto:[email protected]">Bill Burke</a>
@@ -156,6 +157,12 @@ public SamlDeployment build(InputStream xml, ResourceLoader resourceLoader) thro
156157
if (sp.getIdp().getSingleSignOnService().getResponseBinding() != null) {
157158
sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
158159
}
160+
if (sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl() != null) {
161+
if (! sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl().endsWith("/saml")) {
162+
throw new RuntimeException("AssertionConsumerServiceUrl must end with \"/saml\".");
163+
}
164+
sso.setAssertionConsumerServiceUrl(URI.create(sp.getIdp().getSingleSignOnService().getAssertionConsumerServiceUrl()));
165+
}
159166
sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest());
160167
sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature());
161168
sso.setValidateAssertionSignature(sp.getIdp().getSingleSignOnService().isValidateAssertionSignature());

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEve
118118
sso.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
119119
sso.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
120120
sso.setBindingUrl(getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
121+
sso.setAssertionConsumerServiceUrl(getAttributeValue(element, ConfigXmlConstants.ASSERTION_CONSUMER_SERVICE_URL_ATTR));
121122
return sso;
122123
}
123124

adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,15 @@ private void createPaosRequestHeader(SOAPEnvelope envelope) throws SOAPException
156156
paosRequestHeader.setMustUnderstand(true);
157157
paosRequestHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next");
158158
paosRequestHeader.addAttribute(envelope.createName("service"), JBossSAMLURIConstants.ECP_PROFILE.get());
159-
paosRequestHeader.addAttribute(envelope.createName("responseConsumerURL"), deployment.getAssertionConsumerServiceUrl());
159+
paosRequestHeader.addAttribute(envelope.createName("responseConsumerURL"), getResponseConsumerUrl());
160+
}
161+
162+
private String getResponseConsumerUrl() {
163+
return (deployment.getIDP() == null
164+
|| deployment.getIDP().getSingleSignOnService() == null
165+
|| deployment.getIDP().getSingleSignOnService().getAssertionConsumerServiceUrl() == null
166+
) ? null
167+
: deployment.getIDP().getSingleSignOnService().getAssertionConsumerServiceUrl().toString();
160168
}
161169
};
162170
}

0 commit comments

Comments
 (0)