How to connect Okta SAML and Spring Boot using saml2Login, part 2
This is the second article in the series. For the first article, visit here.
Multiple Okta SAML Identity Providers
What’s the best way to register multiple Okta instances in a Spring Boot application as potential sources of identity and authorization?
spring:
security:
saml2:
relyingparty:
registration:
okta1:
identityprovider:
metadata-uri: https://dev-#######.okta.com/app/XXXXXXXXXXXXXXXXXXXX/sso/saml/metadata
okta2:
identityprovider:
metadata-uri: https://dev-#######.okta.com/app/XXXXXXXXXXXXXXXXXXXX/sso/saml/metadata
It’s that simple. The properties are briefly introduced in the Spring Security documentation (5.4.2), and I haven’t been able to find a more detailed description. As typical for Spring Boot, there’s some magic and defaults that kick in when explicit configuration isn’t given, so if it’s not behaving as expected, check out the Saml2RelyingPartyProperties source code.
Note that okta1
and okta2
are the Relying Party registrationIds
, which for our example are used primarily for the Okta SSO URL and Audience Restriction URI which are configured in the Okta User Interface.
SSO URL: http://localhost:8080/login/saml2/sso/okta1
Audience Restriction URI: http://localhost:8080/saml2/service-provider-metadata/okta1
Now when you launch your app, you will see the following login page — without having to do any extra work!
Simply choose which Okta instance to use, sign in, and you will be returned to the application. I’ve created a sample application at https://github.com/joshuatcasey/okta-boot-and-saml2login, all you need to do is substitute the correct Okta metadata URLs in application.yaml
.
Map SAML attributes to Identity and Authorization
SAML has limited support for identity / authorization concepts. The Subject
attribute of an Assertion
will provide some form of identifier, but it’s usually not the only identification attributes that we want to know. What about given name, last name, email address, and so on? And how do we know what this user is allowed to do within our application? Let’s say that the user belongs to some Groups in Okta, such as Users
, Admins
, or Customers
. We can configure Okta to provide these Groups in an attribute in the Assertion
. For more information, see part 1.
How is this information represented in Spring Security? Identity is contained in the Principal
and authorities are contained in the Authorities
. For more information, see the links below. But how do we link a SAML Subject
into a Spring Security Principal
? How do we translate SAML Groups
Attribute into Spring Security Authorities
?
First, let’s pick a terminology for Identity. Should my name “Joshua” be my first name, given name, or forename? Should my name “Casey” be my last name, family name, or surname? I’ll select SCIM as the common labeling methodology. For today, all we will use is userName
, name.givenName
, name.familyName
, and emails
from the SCIM specification.
We need a way to map the attributes from SAML to our SCIM attributes. This will depend on the SAML provider and how it is implemented, so we’ll have to link it to the registrationId
. As an example, let’s specify this mapping in our properties:
spring:
security:
saml2:
relyingparty:
authorityMapping:
okta1: Groups
okta2: Groups
identityMapping:
okta1:
givenName: FirstName
familyName: LastName
email: Email
okta2:
givenName: FirstName
familyName: LastName
email: Email
Under authorityMapping
, we can specify which SAML attribute to use for authorities. In this case, it’s Groups
for both Okta instances.
Under identityMapping
, we can specify a list of attributes to map from SAML to SCIM. In this case, the FirstName
SAML attribute will map to name.givenName
SCIM attribute and so on.
If you run the sample application, and visit the /whoami
endpoint you should see something like the following JSON. Also, if you create a group in Okta called Admins
and put the user groups in a SAML attribute called Groups
, you should be able to visit the /admins
endpoint as well.
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User"
],
"id": null,
"externalId": null,
"meta": null,
"userName": "<your email>",
"name": {
"formatted": null,
"familyName": "Casey",
"givenName": "Joshua",
"middleName": null,
"honorificPrefix": null,
"honorificSuffix": null
},
"displayName": null,
"nickName": null,
"profileUrl": null,
"title": null,
"userType": null,
"preferredLanguage": null,
"locale": null,
"timezone": null,
"active": null,
"password": null,
"emails": [
{
"value": "<your email>",
"display": null,
"type": null,
"primary": true
}
],
"phoneNumbers": null,
"ims": null,
"photos": null,
"addresses": null,
"groups": null,
"entitlements": null,
"roles": null,
"x509Certificates": null
}
I hope this helps you to understand how identity and authorization are configured in Okta and Spring Boot! Let me know if I can add clarifying information or more examples.