keycloak-developers

[KEYCLOAK-883] - More SAML configuration. Using SAML builders

1/21/2015 11:40:20 PM

Details

diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java b/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java
index 641c7da..5e54093 100644
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java
@@ -54,4 +54,7 @@ public class AuthenticationResponse {
         return new AuthenticationResponse(Response.temporaryRedirect(url).build());
     }
 
+    public static AuthenticationResponse fromResponse(Response response) {
+        return new AuthenticationResponse(response);
+    }
 }
diff --git a/broker/saml/pom.xml b/broker/saml/pom.xml
index a0e6706..f518b3c 100755
--- a/broker/saml/pom.xml
+++ b/broker/saml/pom.xml
@@ -22,6 +22,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-saml-protocol</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.picketlink</groupId>
             <artifactId>picketlink-federation</artifactId>
         </dependency>
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 59863f8..05db2c2 100644
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -22,6 +22,8 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.AuthenticationResponse;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
+import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
 import org.picketlink.common.constants.JBossSAMLConstants;
 import org.picketlink.common.constants.JBossSAMLURIConstants;
 import org.picketlink.common.exceptions.ProcessingException;
@@ -31,7 +33,6 @@ import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
 import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
 import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
 import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
-import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
 import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
 import org.picketlink.identity.federation.core.util.JAXPValidationUtil;
 import org.picketlink.identity.federation.core.util.XMLEncryptionUtil;
@@ -41,7 +42,6 @@ import org.picketlink.identity.federation.saml.v2.assertion.EncryptedAssertionTy
 import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
 import org.picketlink.identity.federation.saml.v2.assertion.SubjectType;
 import org.picketlink.identity.federation.saml.v2.assertion.SubjectType.STSubType;
-import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
 import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
 import org.picketlink.identity.federation.saml.v2.protocol.ResponseType.RTChoiceType;
 import org.picketlink.identity.federation.saml.v2.protocol.StatusCodeType;
@@ -53,10 +53,10 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.xml.namespace.QName;
-import java.net.URI;
 import java.net.URLDecoder;
 import java.security.KeyPair;
 import java.security.PrivateKey;
@@ -85,22 +85,26 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
             UriInfo uriInfo = request.getUriInfo();
             String issuerURL = UriBuilder.fromUri(uriInfo.getBaseUri()).build().toString();
             String destinationUrl = getConfig().getSingleSignOnServiceUrl();
-            SAML2Request samlRequest = new SAML2Request();
             String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
 
             if (nameIDPolicyFormat == null) {
                 nameIDPolicyFormat =  JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
             }
 
-            samlRequest.setNameIDFormat(nameIDPolicyFormat);
+            String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
 
-            AuthnRequestType authn = samlRequest
-                    .createAuthnRequestType(IDGenerator.create("ID_"), request.getRedirectUri(), destinationUrl, issuerURL);
-
-            authn.setProtocolBinding(URI.create(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()));
-            authn.setForceAuthn(getConfig().isForceAuthn());
+            if (getConfig().isPostBindingResponse()) {
+                protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
+            }
 
-            Document authnDoc = samlRequest.convert(authn);
+            SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
+                    .assertionConsumerUrl(request.getRedirectUri())
+                    .destination(destinationUrl)
+                    .issuer(issuerURL)
+                    .forceAuthn(getConfig().isForceAuthn())
+                    .protocolBinding(protocolBinding)
+                    .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat))
+                    .relayState(request.getState());
 
             if (getConfig().isWantAuthnRequestsSigned()) {
                 PrivateKey privateKey = request.getRealm().getPrivateKey();
@@ -116,16 +120,14 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
                 KeyPair keypair = new KeyPair(publicKey, privateKey);
 
-                this.saml2Signature.signSAMLDocument(authnDoc, keypair);
+                authnRequestBuilder.signWith(keypair);
             }
 
-            byte[] responseBytes = DocumentUtil.getDocumentAsString(authnDoc).getBytes("UTF-8");
-            String urlEncodedResponse = RedirectBindingUtil.deflateBase64URLEncode(responseBytes);
-            URI redirectUri = UriBuilder.fromPath(destinationUrl)
-                    .queryParam(SAML_REQUEST_PARAMETER, urlEncodedResponse)
-                    .queryParam(RELAY_STATE_PARAMETER, request.getState()).build();
-
-            return AuthenticationResponse.temporaryRedirect(redirectUri);
+            if (getConfig().isPostBindingAuthnRequest()) {
+                return AuthenticationResponse.fromResponse(authnRequestBuilder.postBinding().request());
+            } else {
+                return AuthenticationResponse.fromResponse(authnRequestBuilder.redirectBinding().request());
+            }
         } catch (Exception e) {
             throw new RuntimeException("Could not create authentication request.", e);
         }
@@ -133,44 +135,48 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
     @Override
     public String getRelayState(AuthenticationRequest request) {
-        HttpRequest httpRequest = request.getHttpRequest();
-        return httpRequest.getFormParameters().getFirst(RELAY_STATE_PARAMETER);
+        return getRequestParameter(request, RELAY_STATE_PARAMETER);
     }
 
     @Override
     public AuthenticationResponse handleResponse(AuthenticationRequest request) {
-        HttpRequest httpRequest = request.getHttpRequest();
-        String samlResponse = httpRequest.getFormParameters().getFirst(SAML_RESPONSE_PARAMETER);
-
-        if (samlResponse == null) {
-            throw new RuntimeException("No response from SAML identity provider.");
-        }
-
         try {
-            SAML2Request saml2Request = new SAML2Request();
-            ResponseType responseType = (ResponseType) saml2Request
-                    .getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(samlResponse, "UTF-8")));
-            AssertionType assertion = getAssertion(request, saml2Request, responseType);
-
+            AssertionType assertion = getAssertion(request);
             SubjectType subject = assertion.getSubject();
             STSubType subType = subject.getSubType();
             NameIDType subjectNameID = (NameIDType) subType.getBaseID();
+            FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
 
-            FederatedIdentity user = new FederatedIdentity(subjectNameID.getValue());
-
-            user.setUsername(subjectNameID.getValue());
+            identity.setUsername(subjectNameID.getValue());
 
             if (subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
-                user.setEmail(subjectNameID.getValue());
+                identity.setEmail(subjectNameID.getValue());
             }
 
-            return AuthenticationResponse.end(user);
+            return AuthenticationResponse.end(identity);
         } catch (Exception e) {
             throw new RuntimeException("Could not process response from SAML identity provider.", e);
         }
     }
 
-    private AssertionType getAssertion(AuthenticationRequest request, SAML2Request saml2Request, ResponseType responseType) throws ProcessingException {
+    private AssertionType getAssertion(AuthenticationRequest request) throws Exception {
+        String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);
+
+        if (samlResponse == null) {
+            throw new RuntimeException("No response from SAML identity provider.");
+        }
+
+        SAML2Request saml2Request = new SAML2Request();
+        ResponseType responseType;
+
+        if (getConfig().isPostBindingResponse()) {
+            responseType = (ResponseType) saml2Request
+                    .getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(samlResponse, "UTF-8")));
+        } else {
+            responseType = (ResponseType) saml2Request
+                    .getSAML2ObjectFromStream(RedirectBindingUtil.base64DeflateDecode((samlResponse)));
+        }
+
         validateStatusResponse(responseType);
         validateSignature(saml2Request);
 
@@ -252,4 +258,17 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
         }
     }
 
+    private String getRequestParameter(AuthenticationRequest request, String parameterName) {
+        MultivaluedMap<String, String> requestParameters;
+
+        if (getConfig().isPostBindingResponse()) {
+            HttpRequest httpRequest = request.getHttpRequest();
+            requestParameters = httpRequest.getFormParameters();
+        } else {
+            UriInfo uriInfo = request.getUriInfo();
+            requestParameters = uriInfo.getQueryParameters();
+        }
+
+        return requestParameters.getFirst(parameterName);
+    }
 }
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
index b0b8724..e6efe16 100644
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
@@ -89,4 +89,20 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
     public void setEncryptionPublicKey(String encryptionPublicKey) {
         getConfig().put("encryptionPublicKey", encryptionPublicKey);
     }
+
+    public boolean isPostBindingAuthnRequest() {
+        return Boolean.valueOf(getConfig().get("postBindingAuthnRequest"));
+    }
+
+    public void setPostBindingAuthnRequest(boolean postBindingAuthnRequest) {
+        getConfig().put("postBindingAuthnRequest", String.valueOf(postBindingAuthnRequest));
+    }
+
+    public boolean isPostBindingResponse() {
+        return Boolean.valueOf(getConfig().get("postBindingResponse"));
+    }
+
+    public void setPostBindingResponse(boolean postBindingResponse) {
+        getConfig().put("postBindingResponse", String.valueOf(postBindingResponse));
+    }
 }
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
index 6d4ea91..5624e54 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
@@ -73,6 +73,20 @@
                         <span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span>
                     </div>
                     <div class="form-group">
+                        <label class="col-sm-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label>
+                        <div class="col-sm-4">
+                            <input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" onoffswitch />
+                        </div>
+                        <span tooltip-placement="right" tooltip="Indicates whether the identity provider must respond to the AuthnRequest using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
+                    </div>
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label>
+                        <div class="col-sm-4">
+                            <input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" onoffswitch />
+                        </div>
+                        <span tooltip-placement="right" tooltip="Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
+                    </div>
+                    <div class="form-group">
                         <label class="col-sm-2 control-label" for="enabled">Enabled</label>
                         <div class="col-sm-4">
                             <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java
index 2bf82ae..288e2bd 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java
@@ -127,7 +127,7 @@ public class SALM2LoginResponseBuilder extends SAML2BindingBuilder<SALM2LoginRes
         // Create a response type
         String id = IDGenerator.create("ID_");
 
-        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(responseIssuer);
+        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(issuer);
         issuerHolder.setStatusCode(JBossSAMLURIConstants.STATUS_SUCCESS.get());
 
         IDPInfoHolder idp = new IDPInfoHolder();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2AuthnRequestBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2AuthnRequestBuilder.java
new file mode 100644
index 0000000..4dedd01
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2AuthnRequestBuilder.java
@@ -0,0 +1,98 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.protocol.saml;
+
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
+import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
+import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
+import org.w3c.dom.Document;
+
+import java.net.URI;
+
+/**
+ * @author pedroigor
+ */
+public class SAML2AuthnRequestBuilder extends SAML2BindingBuilder<SAML2AuthnRequestBuilder> {
+
+    private final AuthnRequestType authnRequestType;
+
+    public SAML2AuthnRequestBuilder() {
+        try {
+            this.authnRequestType = new AuthnRequestType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant());
+        } catch (ConfigurationException e) {
+            throw new RuntimeException("Could not create SAML AuthnRequest builder.", e);
+        }
+    }
+
+    public SAML2AuthnRequestBuilder assertionConsumerUrl(String assertionConsumerUrl) {
+        this.authnRequestType.setAssertionConsumerServiceURL(URI.create(assertionConsumerUrl));
+        return this;
+    }
+
+    public SAML2AuthnRequestBuilder forceAuthn(boolean forceAuthn) {
+        this.authnRequestType.setForceAuthn(forceAuthn);
+        return this;
+    }
+
+    public SAML2AuthnRequestBuilder nameIdPolicy(SAML2NameIDPolicyBuilder nameIDPolicy) {
+        this.authnRequestType.setNameIDPolicy(nameIDPolicy.build());
+        return this;
+    }
+
+    public SAML2AuthnRequestBuilder protocolBinding(String protocolBinding) {
+        this.authnRequestType.setProtocolBinding(URI.create(protocolBinding));
+        return this;
+    }
+
+    public RedirectBindingBuilder redirectBinding() {
+        try {
+            return new RedirectBindingBuilder(toDocument());
+        } catch (Exception e) {
+            throw new RuntimeException("Could not build authn request for post binding.", e);
+        }
+    }
+
+    public PostBindingBuilder postBinding() {
+        try {
+            return new PostBindingBuilder(toDocument());
+        } catch (Exception e) {
+            throw new RuntimeException("Could not build authn request for post binding.", e);
+        }
+    }
+
+    private Document toDocument()  {
+        try {
+            AuthnRequestType authnRequestType = this.authnRequestType;
+
+            NameIDType nameIDType = new NameIDType();
+
+            nameIDType.setValue(this.issuer);
+
+            authnRequestType.setIssuer(nameIDType);
+
+            authnRequestType.setDestination(URI.create(this.destination));
+
+            return new SAML2Request().convert(authnRequestType);
+        } catch (Exception e) {
+            throw new RuntimeException("Could not convert " + authnRequestType + " to a document.", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
index 294f210..dfefa05 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
@@ -44,7 +44,7 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
     protected SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSA_SHA1;
     protected String relayState;
     protected String destination;
-    protected String responseIssuer;
+    protected String issuer;
     protected int encryptionKeySize = 128;
     protected PublicKey encryptionPublicKey;
     protected String encryptionAlgorithm = "AES";
@@ -108,8 +108,8 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
         return (T)this;
     }
 
-    public T responseIssuer(String issuer) {
-        this.responseIssuer = issuer;
+    public T issuer(String issuer) {
+        this.issuer = issuer;
         return (T)this;
     }
 
@@ -140,14 +140,17 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
         }
 
         public String htmlResponse() throws ProcessingException, ConfigurationException, IOException {
-            return buildHtml(encoded(), destination);
+            return buildHtml(encoded(), destination, false);
 
         }
+        public Response request() throws ConfigurationException, ProcessingException, IOException {
+            return buildResponse(document, destination, true);
+        }
         public Response response() throws ConfigurationException, ProcessingException, IOException {
-            return buildResponse(document, destination);
+            return buildResponse(document, destination, false);
         }
         public Response response(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
-            return buildResponse(document, actionUrl);
+            return buildResponse(document, actionUrl, false);
         }
     }
 
@@ -165,15 +168,28 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
         public Document getDocument() {
             return document;
         }
-        public URI responseUri(String redirectUri) throws ConfigurationException, ProcessingException, IOException {
-            return generateRedirectUri("SAMLResponse", redirectUri, document);
+        public URI responseUri(String redirectUri, boolean asRequest) throws ConfigurationException, ProcessingException, IOException {
+            String samlParameterName = GeneralConstants.SAML_RESPONSE_KEY;
+
+            if (asRequest) {
+                samlParameterName = GeneralConstants.SAML_REQUEST_KEY;
+            }
+
+            return generateRedirectUri(samlParameterName, redirectUri, document);
         }
         public Response response() throws ProcessingException, ConfigurationException, IOException {
-            return response(destination);
+            return response(destination, false);
         }
-
         public Response response(String redirectUri) throws ProcessingException, ConfigurationException, IOException {
-            URI uri = responseUri(redirectUri);
+            return response(destination, false);
+        }
+
+        public Response request() throws ProcessingException, ConfigurationException, IOException {
+            return response(destination, true);
+        }
+
+        private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
+            URI uri = responseUri(redirectUri, asRequest);
 
             CacheControl cacheControl = new CacheControl();
             cacheControl.setNoCache(true);
@@ -266,8 +282,8 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
     }
 
 
-    protected Response buildResponse(Document responseDoc, String actionUrl) throws ProcessingException, ConfigurationException, IOException {
-        String str = buildHtmlPostResponse(responseDoc, actionUrl);
+    protected Response buildResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
+        String str = buildHtmlPostResponse(responseDoc, actionUrl, asRequest);
 
         CacheControl cacheControl = new CacheControl();
         cacheControl.setNoCache(true);
@@ -276,14 +292,14 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
                        .header("Cache-Control", "no-cache, no-store").build();
     }
 
-    protected String buildHtmlPostResponse(Document responseDoc, String actionUrl) throws ProcessingException, ConfigurationException, IOException {
+    protected String buildHtmlPostResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
         byte[] responseBytes = DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
         String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
 
-        return buildHtml(samlResponse, actionUrl);
+        return buildHtml(samlResponse, actionUrl, asRequest);
     }
 
-    protected String buildHtml(String samlResponse, String actionUrl) {
+    protected String buildHtml(String samlResponse, String actionUrl, boolean asRequest) {
         if (destination == null) {
             throw SALM2LoginResponseBuilder.logger.nullValueError("Destination is null");
         }
@@ -291,6 +307,11 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
         StringBuilder builder = new StringBuilder();
 
         String key = GeneralConstants.SAML_RESPONSE_KEY;
+
+        if (asRequest) {
+            key = GeneralConstants.SAML_REQUEST_KEY;
+        }
+
         builder.append("<HTML>");
         builder.append("<HEAD>");
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2ErrorResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2ErrorResponseBuilder.java
index c4a2c2b..7059d68 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2ErrorResponseBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2ErrorResponseBuilder.java
@@ -47,7 +47,7 @@ public class SAML2ErrorResponseBuilder extends SAML2BindingBuilder<SAML2ErrorRes
         // Create a response type
         String id = IDGenerator.create("ID_");
 
-        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(responseIssuer);
+        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(issuer);
         issuerHolder.setStatusCode(status);
 
         IDPInfoHolder idp = new IDPInfoHolder();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutRequestBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutRequestBuilder.java
index 9e885ea..65c902c 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutRequestBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutRequestBuilder.java
@@ -1,19 +1,15 @@
 package org.keycloak.protocol.saml;
 
-import org.picketlink.common.constants.JBossSAMLURIConstants;
 import org.picketlink.common.exceptions.ConfigurationException;
 import org.picketlink.common.exceptions.ParsingException;
 import org.picketlink.common.exceptions.ProcessingException;
 import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
-import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil;
 import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
 import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS;
 import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
 import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType;
-import org.picketlink.identity.federation.web.util.PostBindingUtil;
 import org.w3c.dom.Document;
 
-import java.io.IOException;
 import java.net.URI;
 
 /**
@@ -48,7 +44,7 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
     }
 
     private LogoutRequestType createLogoutRequest() throws ConfigurationException {
-        LogoutRequestType lort = new SAML2Request().createLogoutRequest(responseIssuer);
+        LogoutRequestType lort = new SAML2Request().createLogoutRequest(issuer);
 
         NameIDType nameID = new NameIDType();
         nameID.setValue(userPrincipal);
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutResponseBuilder.java
index 9815e61..c9c63b2 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutResponseBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LogoutResponseBuilder.java
@@ -6,13 +6,8 @@ import org.picketlink.common.exceptions.ParsingException;
 import org.picketlink.common.exceptions.ProcessingException;
 import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
 import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
-import org.picketlink.identity.federation.core.saml.v2.factories.JBossSAMLAuthnResponseFactory;
-import org.picketlink.identity.federation.core.saml.v2.holders.IDPInfoHolder;
-import org.picketlink.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
-import org.picketlink.identity.federation.core.saml.v2.holders.SPInfoHolder;
 import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
 import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
-import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
 import org.picketlink.identity.federation.saml.v2.protocol.StatusCodeType;
 import org.picketlink.identity.federation.saml.v2.protocol.StatusResponseType;
 import org.picketlink.identity.federation.saml.v2.protocol.StatusType;
@@ -60,7 +55,7 @@ public class SAML2LogoutResponseBuilder extends SAML2BindingBuilder<SAML2LogoutR
             statusResponse.setStatus(statusType);
             statusResponse.setInResponseTo(logoutRequestID);
             NameIDType issuer = new NameIDType();
-            issuer.setValue(responseIssuer);
+            issuer.setValue(this.issuer);
 
             statusResponse.setIssuer(issuer);
             statusResponse.setDestination(destination);
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2NameIDPolicyBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2NameIDPolicyBuilder.java
new file mode 100644
index 0000000..71f4186
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2NameIDPolicyBuilder.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.protocol.saml;
+
+import org.picketlink.identity.federation.saml.v2.protocol.NameIDPolicyType;
+
+import java.net.URI;
+
+/**
+ * @author pedroigor
+ */
+public class SAML2NameIDPolicyBuilder {
+
+    private final NameIDPolicyType policyType;
+
+    private SAML2NameIDPolicyBuilder(String format) {
+        this.policyType = new NameIDPolicyType();
+        this.policyType.setFormat(URI.create(format));
+    }
+
+    public static SAML2NameIDPolicyBuilder format(String format) {
+        return new SAML2NameIDPolicyBuilder(format);
+    }
+
+    public NameIDPolicyType build() {
+        this.policyType.setAllowCreate(Boolean.TRUE);
+        return this.policyType;
+    }
+}
\ No newline at end of file
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 0e68eb3..f3bc382 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -114,7 +114,7 @@ public class SamlProtocol implements LoginProtocol {
         SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
                 .relayState(clientSession.getNote(GeneralConstants.RELAY_STATE))
                 .destination(clientSession.getRedirectUri())
-                .responseIssuer(getResponseIssuer(realm))
+                .issuer(getResponseIssuer(realm))
                 .status(status);
       try {
           if (isPostBinding(clientSession)) {
@@ -191,7 +191,7 @@ public class SamlProtocol implements LoginProtocol {
         builder.requestID(requestID)
                .relayState(relayState)
                .destination(redirectUri)
-               .responseIssuer(responseIssuer)
+               .issuer(responseIssuer)
                .requestIssuer(clientSession.getClient().getClientId())
                .nameIdentifier(nameIdFormat, nameId)
                .authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
diff --git a/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java b/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java
index 550deda..19ff363 100644
--- a/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java
@@ -156,7 +156,7 @@ public class AuthenticationBrokerResource {
             String relayState = provider.getRelayState(createAuthenticationRequest(providerId, null, realm, null));
 
             if (relayState == null) {
-                return redirectToErrorPage(realm, "No authorization code provided.");
+                return redirectToErrorPage(realm, "No relay state from identity provider.");
             }
 
             ClientSessionCode clientCode = isValidAuthorizationCode(relayState, realm);