Merge pull request #795 from patriot1burke/master
force post binding switch and saml docs
This commit is contained in:
commit
4417f28c5a
8 changed files with 152 additions and 11 deletions
|
@ -13,6 +13,7 @@
|
|||
<!ENTITY JavascriptAdapter SYSTEM "modules/javascript-adapter.xml">
|
||||
<!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
|
||||
<!ENTITY Logout SYSTEM "modules/logout.xml">
|
||||
<!ENTITY SAML SYSTEM "modules/saml.xml">
|
||||
<!ENTITY SocialConfig SYSTEM "modules/social-config.xml">
|
||||
<!ENTITY SocialFacebook SYSTEM "modules/social-facebook.xml">
|
||||
<!ENTITY SocialGitHub SYSTEM "modules/social-github.xml">
|
||||
|
@ -121,6 +122,7 @@ This one is short
|
|||
&UserFederation;
|
||||
&ExportImport;
|
||||
&ServerCache;
|
||||
&SAML;
|
||||
&SecurityVulnerabilities;
|
||||
&Clustering;
|
||||
&Migration;
|
||||
|
|
|
@ -73,6 +73,9 @@
|
|||
<listitem>
|
||||
OpenID Connect Support.
|
||||
</listitem>
|
||||
<listitem>
|
||||
SAML Support.
|
||||
</listitem>
|
||||
<listitem>
|
||||
CORS Support
|
||||
</listitem>
|
||||
|
@ -89,13 +92,13 @@
|
|||
Account Management console that allows users to manage their own account, view their open sessions, reset passwords, etc.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Deployable as a WAR, appliance, or on Openshift.
|
||||
Deployable as a WAR, appliance, or on Openshift. Completely clusterable.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Multitenancy support. You can host and manage multiple realms for multiple organizations.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Supports JBoss AS7, EAP 6.x, Wildfly and JavaScript applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments
|
||||
Supports JBoss AS7, EAP 6.x, Wildfly and Pure JavaScript applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
|
97
docbook/reference/en/en-US/modules/saml.xml
Executable file
97
docbook/reference/en/en-US/modules/saml.xml
Executable file
|
@ -0,0 +1,97 @@
|
|||
<chapter id="saml">
|
||||
<title>SAML SSO</title>
|
||||
<para>
|
||||
Keycloak supports SAML 2.0 for registered applications. Both POST and Redirect bindings are supported. You can choose
|
||||
to require client signature validation and can have the server sign and/or encrypt responses as well. We do not
|
||||
yet support logout via redirects. All logouts happen via a background POST binding request to the application
|
||||
that will be logged out. We do not support SAML 1.1 either. If you want support for either of those, please
|
||||
log a JIRA request and we'll schedule it.
|
||||
</para>
|
||||
<para>
|
||||
When you create an application in the admin console, you can choose which protocol the application will log in with.
|
||||
In the application create screen, choose <literal>saml</literal> from the protocol list. After that there
|
||||
are a bunch of configuration options. Here is a description of each item:
|
||||
</para>
|
||||
<para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Include AuthnStatement</term>
|
||||
<listitem>
|
||||
<para>
|
||||
SAML login responses may specify the authenticaiton method used (password, etc.) as well as
|
||||
a timestamp of the login. Setting this to on will include that statement in the response document.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Multi-valued Roles</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If this switch is off, any user role mapings will have a corresponding attribute created for it.
|
||||
If this switch is turn on, only one role attribute will be created, but it will have
|
||||
multiple values within in.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Sign Documents</term>
|
||||
<listitem>
|
||||
<para>
|
||||
When turned on, Keycloak will sign the document using the realm's private key.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Sign Assertions</term>
|
||||
<listitem>
|
||||
<para>
|
||||
With the <literal>Sign Documents</literal> switch signs the whole document. With this setting
|
||||
you just assign the assertions of the document.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Signature Algorithm</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Choose between a variety of algorithms for signing SAML documents.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Encrypt Assertions</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Encrypt assertions in SAML documents with the realm's private key. The AES algorithm is used
|
||||
with a key size of 128 bits.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Client Signature Required</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Expect that documents coming from a client are signed. Keycloak will validate this signature
|
||||
using the client keys set up in the <literal>Application Keys</literal> submenu item.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Force POST Binding</term>
|
||||
<listitem>
|
||||
<para>
|
||||
By default, Keycloak will respond using the initial SAML binding of the original request. By turning
|
||||
on this switch, you will force Keycloak to always respond using the SAML POST Binding even if the
|
||||
original request was a the Redirect binding.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
<para>
|
||||
One thing to note is that roles are not treated as a hierarchy. So, any role mappings will just be added
|
||||
to the role attributes in the SAML document using their basic name. So, if you have multiple applicaiton roles
|
||||
you might have name collisions. You can use the Scope Mapping menu item to control which role mappings are set
|
||||
in the response.
|
||||
</para>
|
||||
</chapter>
|
|
@ -152,4 +152,13 @@
|
|||
At this point in time, there is no knowledge of any SQL injection vulnerabilities in Keycloak
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Limiting Scope</title>
|
||||
<para>
|
||||
Using the <literal>Scope</literal> menu in the admin console for oauth clients or applications, you can control
|
||||
exactly which role mappings will be included within the token sent back to the client or application. This
|
||||
allows you to limit the scope of permissions given to the application or client which is great if the client isn't
|
||||
very trusted and is known to not being very careful with its tokens.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
|
@ -363,6 +363,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
$scope.samlAssertionSignature = false;
|
||||
$scope.samlClientSignature = false;
|
||||
$scope.samlEncrypt = false;
|
||||
$scope.samlForcePostBinding = false;
|
||||
if (!$scope.create) {
|
||||
if (!application.attributes) {
|
||||
application.attributes = {};
|
||||
|
@ -442,6 +443,13 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
$scope.samlMultiValuedRoles = false;
|
||||
}
|
||||
}
|
||||
if ($scope.application.attributes["saml.force.post.binding"]) {
|
||||
if ($scope.application.attributes["saml.force.post.binding"] == "true") {
|
||||
$scope.samlForcePostBinding = true;
|
||||
} else {
|
||||
$scope.samlForcePostBinding = false;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.switchChange = function() {
|
||||
$scope.changed = true;
|
||||
|
@ -534,6 +542,12 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
} else {
|
||||
$scope.application.attributes["saml.multivalued.roles"] = "false";
|
||||
|
||||
}
|
||||
if ($scope.samlForcePostBinding == true) {
|
||||
$scope.application.attributes["saml.force.post.binding"] = "true";
|
||||
} else {
|
||||
$scope.application.attributes["saml.force.post.binding"] = "false";
|
||||
|
||||
}
|
||||
|
||||
$scope.application.protocol = $scope.protocol;
|
||||
|
|
|
@ -111,6 +111,13 @@
|
|||
</div>
|
||||
<span tooltip-placement="right" tooltip="Will the client sign their saml requests and responses? And should they be validated?" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
|
||||
<label class="col-sm-2 control-label" for="samlForcePostBinding">Force POST Binding</label>
|
||||
<div class="col-sm-6">
|
||||
<input ng-model="samlForcePostBinding" ng-click="switchChange()" name="samlForcePostBinding" id="samlForcePostBinding" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Always use POST binding for responses." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="!application.bearerOnly">
|
||||
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
|
||||
|
|
|
@ -39,6 +39,14 @@ public class SamlProtocol implements LoginProtocol {
|
|||
public static final String SAML_BINDING = "saml_binding";
|
||||
public static final String SAML_POST_BINDING = "post";
|
||||
public static final String SAML_GET_BINDING = "get";
|
||||
public static final String SAML_SERVER_SIGNATURE = "saml.server.signature";
|
||||
public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
|
||||
public static final String SAML_AUTHNSTATEMENT = "saml.authnstatement";
|
||||
public static final String SAML_MULTIVALUED_ROLES = "saml.multivalued.roles";
|
||||
public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
|
||||
public static final String SAML_ENCRYPT = "saml.encrypt";
|
||||
public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
|
||||
public static final String REQUEST_ID = "REQUEST_ID";
|
||||
|
||||
protected KeycloakSession session;
|
||||
|
||||
|
@ -98,14 +106,15 @@ public class SamlProtocol implements LoginProtocol {
|
|||
}
|
||||
|
||||
protected boolean isPostBinding(ClientSessionModel clientSession) {
|
||||
return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING));
|
||||
ClientModel client = clientSession.getClient();
|
||||
return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || "true".equals(client.getAttribute(SAML_FORCE_POST_BINDING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
|
||||
ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
ClientModel client = clientSession.getClient();
|
||||
String requestID = clientSession.getNote("REQUEST_ID");
|
||||
String requestID = clientSession.getNote(REQUEST_ID);
|
||||
String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
|
||||
String redirectUri = clientSession.getRedirectUri();
|
||||
String responseIssuer = getResponseIssuer(realm);
|
||||
|
@ -166,23 +175,23 @@ public class SamlProtocol implements LoginProtocol {
|
|||
}
|
||||
|
||||
private boolean requiresRealmSignature(ClientModel client) {
|
||||
return "true".equals(client.getAttribute("saml.server.signature"));
|
||||
return "true".equals(client.getAttribute(SAML_SERVER_SIGNATURE));
|
||||
}
|
||||
|
||||
private boolean requiresAssertionSignature(ClientModel client) {
|
||||
return "true".equals(client.getAttribute("saml.assertion.signature"));
|
||||
return "true".equals(client.getAttribute(SAML_ASSERTION_SIGNATURE));
|
||||
}
|
||||
|
||||
private boolean includeAuthnStatement(ClientModel client) {
|
||||
return "true".equals(client.getAttribute("saml.authnstatement"));
|
||||
return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT));
|
||||
}
|
||||
|
||||
private boolean multivaluedRoles(ClientModel client) {
|
||||
return "true".equals(client.getAttribute("saml.multivalued.roles"));
|
||||
return "true".equals(client.getAttribute(SAML_MULTIVALUED_ROLES));
|
||||
}
|
||||
|
||||
public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) {
|
||||
String alg = client.getAttribute("saml.signature.algorithm");
|
||||
String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
|
||||
if (alg != null) {
|
||||
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
|
||||
if (algorithm != null) return algorithm;
|
||||
|
@ -191,7 +200,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
}
|
||||
|
||||
private boolean requiresEncryption(ClientModel client) {
|
||||
return "true".equals(client.getAttribute("saml.encrypt"));
|
||||
return "true".equals(client.getAttribute(SAML_ENCRYPT));
|
||||
}
|
||||
|
||||
public void initClaims(SALM2LoginResponseBuilder builder, ClientModel model, UserModel user) {
|
||||
|
|
|
@ -198,7 +198,7 @@ public class SamlService {
|
|||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
clientSession.setNote(SamlProtocol.SAML_BINDING, getBindingType());
|
||||
clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
|
||||
clientSession.setNote("REQUEST_ID", requestAbstractType.getID());
|
||||
clientSession.setNote(SamlProtocol.REQUEST_ID, requestAbstractType.getID());
|
||||
|
||||
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
|
||||
if (response != null) return response;
|
||||
|
|
Loading…
Reference in a new issue