Diving Into SSO With Spring SAML and SSOCircle
How to configure Spring SAML on Spring Boot + Thymeleaf project via java configuration and connecting it to SSOCircle Identity Provider.
Join the DZone community and get the full member experience.Join For Free
Recently I've been working on integrating SSO in an old Spring project and for that very reason, I created a small PoC to get my hands on Spring SAML and all its subtleties. In this article, I am going to share my experience of adding Spring SAML to a Spring MVC project and integrating it with SSOCircle. Spring did a wonderful job of documenting everything SAML related, setting up the
.xml configuration, and even SSOCircle integration, but there are places where you could still fall. So before reading this article make sure to read the whole Spring SAML documentation and SAML documentation itself.
I would assume that you already are familiar with the concept of SSO and its details, but for the sake of a better understanding of the article and the keywords that I'm going to use I will briefly describe the basics of SSO.
What Is SSO?
So there are several related, yet independent, software systems – service providers and an entity that checks the user's identity – identity provider. Then there is a secure way for these 2 to communicate and one of the most-used security languages that define the relationship between identity providers and service providers is Security Assertion Markup Language (SAML).
The Security Assertion Markup Language (SAML), developed by the Security Services Technical Committee of OASIS, is an XML-based framework for communicating user authentication, entitlement, and attribute information. As its name suggests, SAML allows business entities to make assertions regarding the identity, attributes, and entitlements of a subject (an entity that is often a human user) to other entities, such as a partner company or another enterprise application. - OASIS
- Service Provider (SP) – the application providing services
- Identity Provider (IdP) – the entity providing identities
- SAML – the language of communication between SP and IdP
- SAML Request – authentication request (
- SAML Response – assertion of the authenticated user (
- Assertion - a package of information that supplies zero or more statements made by a SAML authority
- SAML Request – authentication request (
Let's see a brief overview of how everything assembles
Having a user named John Doe:
- John Doe via the browser tries to access the SP
- SP redirects to the IdP for the identity check/authentication
- IdP returns the SAML response to the SP via the browser's redirection
- Based on the response SP sends the requested resource or handles the error
Now for this communication to work IdP and SP need to know about each other, and this is done via the SAML metadata. SAML Metadata is an XML document containing various important information about IdP/SP. Typically the SP will generate one metadata containing URLs, IDs, a public key, and other SAML-related information for IdP to import it and the same goes for the IdP – SP will import IdP's metadata.
Now that we have some understanding of what is going on, let's take a quick look at the application. The application is a simple task manager (things to do manager) named taskr on Spring Boot with Thymeleaf. taskr enables its user to create/delete and view tasks, you can check it on GitHub. And then there is SSOCircle which is the IdP for taskr.
Let's take a look at the SAML configuration of the taskr
Basically what you see is what the Spring documentation already offers with some small adjustments. Now I want to talk about some things that in my opinion are tricky.
First of all, is the
SAML communication uses cryptography for signing and encryption of the data and everything this related is done through the
keyManager which relies on a single JKS key store containing all the private and public keys. To generate a key store I've used the following command:
keytool -genkeypair -alias taskrSaml -storepass 123456
-dname "CN=John Doe, OU=Taskr, O=EvilInc, L=Cupertino, S=California, C=US"
-keypass 123456 -keyalg RSA -keysize 2048 -sigalg SHA256withRSA
And then you can place it under your resources and provide the path. Also, note that there is KeyStore Explorer which facilitates easy handling of the key stores and it comes in very handy when you have to deal with importing the IdP certificates. Another thing to mention here is that Maven might corrupt your
.jks file during the build process and it becomes unusable, so to deal with that I added the following plugin in
pom.xml under the build plugins.
Since I've already mentioned the IdP signing/encryption certificates let's discuss them. IdP certificates may be provided by the person handling the IdP or you can grab them from the IdP's metadata. SSOCircle's metadata URL is https://idp.ssocircle.com/idp-meta.xml (please note this is the same URL used for the
HTTPMetadataProvider bean initialization) and opening that you want to search for the following 2 tags
These are client certificates provided by SSOCircle as a mean of strong authentication, but the integration works without adding them into your key store too. Some IdPs might require a signature trust establishment, so for that reason, I'm going to explain this. Under them, in the
<ds:X509Certificate> tag you'll find IdP's certificates. I created 2 files with the
.p12 extension, one for the signing certificate, and the other for the encryption certificate and I pasted the content of the X509 Certificates between these 2 lines.
After that, I imported them into my key store using KeyStore Explorer – Import Certificate.
Being on the cryptography topic, let's discuss another interesting thing -
samlBootstrap bean. Though integrating with SSOCircle most probably you won't encounter this problem, but dealing with other IdPs you might get an error saying that the message is not signed with the expected signature algorithm, and that message is signed with the signature algorithm SHA1 whilst the expected one is SHA256. That is because SAML by default signs its messages with SHA1 and to overcome that, we need to explicitly specify the signature algorithm and digest method via a custom SAML bootstrap.
Let's talk about communication, how IdP is going to know where to redirect, or where to address its requests. It is already mentioned in the Spring SAML documentation.
In case you use automatically generated metadata make sure to configure entityBaseURL matching the front-end URL in your metadataGeneratorFilter bean - Spring Docs
This usually is enough to establish the communication between the SP and IdP, but if you're facing a situation where multiple back-end servers process SAML requests forwarded by a reverse-proxy/load balancer or you're trying to expose your local URL using ngrok, then you might need to configure another
As it is already well-documented I want to mention just one thing, make sure to set
samlContextProviderLB.setIncludeServerPortInRequestURL(true); if you're dealing with your server port and you need it in your URL. Also pay attention that if the server port is less or equal to 0 the port won't be included in the URL, might come in handy when you are dealing with multiple environments with different URLs.
Since we are talking about URLs, I want to tell you another interesting thing that I encountered, I call it “Cyclic SAML Authentication”, but first a little preface. When you are dealing with SAML you can configure 2 kinds of logouts: local logout and global logout.
- Local logout logs you out just out of the current SP's session, but keeps your IdP session and allows the user to use other SPs still being authenticated. Local logout can be invoked at
saml/logoutby specifying the
localrequest parameter equal to
- Global logout logs you out of IdP terminating all the SPs sessions forcing the user to enter his credentials once again at the IdP to access any of the SP and can be invoked at
saml/logoutwithout specifying any parameters.
Another thing to mention related to the user's session is that you might get a message stating that the authentication statement is too old to be used if you don't get in sync the maximum authentication age of your SP and IdP. You can fix this problem by setting the property on the
webSSOprofileConsumer bean. You might be tempted to use the
Integer.MAX_VALUE as a maximum authentication age, but this will result in not checking the authentication statement at all or it won't even work as it happened to me.
Now, what happens after you log out? What I have experienced is that either you log out locally or globally you get redirected to
"/" and therefore not being authenticated you automatically get redirected to the login page at IdP in case of a global logout or you're getting a new session which redirects you to the requested page in case of a local logout.
So from a UX perspective, the local logout doesn't make any sense you try to logout from the current SP and then you get to use it again after some redirection as if you never logged out. A solution would be to create an unsecured login or logout page on the SP side and whenever the user locally logs out you redirect him there and let him decide if he wants to login again or not (
saml/login). So to configure that you need to specify the logout success URL
And for it to work I registered the authentication entry point like this
IdP Configuration — SSOCircle
And last but not least I wanted to share my experience on SSOCircle configuration. First what you want to do is to create an account on SSOCircle. The user that you'll be creating here will be the user of your SP.
Once you have your user you need to add the service provider to SSOCircle.
On the next page, you need to enter the SP's id, metadata, and check the needed assertions that you're going to validate via your custom
The SP's id is the same id used in the
metadataGenerator as the
entityId and the SP's metadata can be grabbed at http://localhost:8080/saml/metadata
That's it, I hope, you've learned something.
Opinions expressed by DZone contributors are their own.