SAML Integration with Spring Boot & Spring Security — Microsoft Azure AD

Sravan K
7 min readAug 27, 2020

--

Hey Net-Heads!!
Being part of IT industry, whatever role we are in, we usually access multiple services within an organization and memorizing the passwords to access these services is tedious. Well not anymore. SAML comes in to serve companies and services with better authentication mechanism, without having to manually sign up for every service.

This post will walk you through SAML integration with a Spring boot application and Microsoft Azure AD.

Before we dive-in

This post will focus on the below aspects:
1. About SAML.
2. Set up Microsoft Azure Portal.
3. Spring boot App with SAML support.
4. Run locally

About SAML

SAML stands for Security Assertion Markup Language. It provides a single authentication endpoint and allow users to log in once and then be granted access to any number of applications.

SAML authentication involves two parties:

Service Provider (Relying Party): The downstream service or application which the user is attempting to log-in. In our post, it is the spring boot application.

Identity Provider (Asserting Party): Performs authentication and passes the user’s identity and authorization level to the service provider within a federation. In our post, it is the Microsoft Azure AD.

These 2 parties establish trust by passing XML metadata files from one to the other.

SAML SSO Flow

Service Provider-initiated SSO Flow
  1. User attempts to access a service provider but they have not yet authenticated
  2. Service provider discovers the IDP to contact for authentication.
  3. User is redirected or prompted for Active Directory credentials by the Identity Provider with SAML Request.
  4. User provides valid credentials, so that the Identity Provider creates an assertion.
  5. Identity provider will validate the credentials and the assertion is provided back to the Service Provider.
  6. The user is now permitted to access the application.

Set up Microsoft Azure Portal

  1. Sign up and create an Azure account.
  2. Navigate to Active Directory Menu Blade.
  3. You can manage applications on the Enterprise applications blade located in the Manage section of the Azure Active Directory portal.

4. Click on ‘New Application’ to create and add new application and enable Single Sign on.

5. Choose the type of application to add as ‘Non-gallery application’ and enter your application name and click on ‘add’.

6. You will find your new application listed in the overview page.

7. Lets register the newly added application. Navigate back to Active Directory Menu Blade and click on ‘App Registrations’.

8. Choose your application listed in the overview page of ‘App registrations’

9. Click on Redirect URLs and place your localhost end point. In this case, it is https://localhost:8443/

10. Change the Application Id to meaningful name — ‘dummybot-saml’

11. Now, lets configure the Sign Sign on. Click on Managed application section and update the Identifier(Entity Id) and Rely Url.
Please note:
Entity Id must match with Application Id (i.e. dummybot-saml)and Rely Url must match with Redirect URLs (i.e. https://localhost:8443/).

12. Add Users to access the application by navigating to ‘Users and groups’ section.

13. Copy the App Federation Metadata Url from SAML based Sign On page. We need this URL to configure Spring Boot Application.

Spring boot App with SAML support

Navigate to https://start.spring.io in your favorite browser and select Security, Web, Thymeleaf, and DevTools as dependencies.

Click Generate Project, download the generated ZIP file and open it in your favorite editor. Add the spring-security-saml-dsl and spring-security-saml2-coredependency to your pom.xml.

<dependency>
<groupId>org.springframework.security.extensions</groupId>
<artifactId>spring-security-saml-dsl</artifactId>
<version>1.0.0.M3</version>
</dependency>
<dependency>
<groupId>org.springframework.security.extensions</groupId>
<artifactId>spring-security-saml2-core</artifactId>
<version>2.0.0.M31</version>
</dependency>

You’ll also need to add the Spring Milestone repository since a milestone release is all that’s available at the time of this writing.

<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
</repositories>

In src/main/resources/application.yml, add the following configuration. Make sure to use the “Identity Provider metadata” value you copied earlier (hint: you can find it again under the “SAML based Sign ” page in your Azure Portal).

security:
require-ssl:true
server:
port: 8443
ssl:
enabled: true
key-alias: spring
key-store: classpath:saml/keystore.jks
key-store-password: secret
servlet:
context-path: /

spring:
thymeleaf:
cache: false
enabled: true
prefix: classpath:templates/
suffix: .html
security:
saml2:
network:
read-timeout: 10000
connect-timeout: 5000
service-provider:
entity-id: <your entity id>
alias: dummybot-saml
sign-metadata: false
sign-requests: false
want-assertions-signed: true
single-logout-enabled: true
encrypt-assertions: false
name-ids:
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
keys:
active:
name: sp-signing-key-1

providers:
- alias: enter-into-dummybot-saml
metadata: <your metadata url>
skip-ssl-validation: true
link-text: enter-into-dummybot-saml
authentication-request-binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST

Navigate to the src/main/resources directory of your app and create a saml directory.
Navigate into the directory and run the following command. Use “secret” when prompted for a keystore password.

keytool -genkey -v -keystore keystore.jks -alias spring -keyalg RSA -keysize 2048 -validity 10000

Create a SamlAppConfig.java file in the com.example.saml.config package.

package com.example.saml.saml.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.saml.provider.SamlServerConfiguration;

@ConfigurationProperties(prefix = "spring.security.saml2")
@Configuration
public class SamlAppConfig extends SamlServerConfiguration {
}

Create a SamlSecurityConfiguration.java file in the com.example.saml.securitypackage.

package com.example.saml.security;
import com.example.saml.saml.config.SamlAppConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.saml.provider.SamlServerConfiguration;
import org.springframework.security.saml.provider.service.config.SamlServiceProviderServerBeanConfiguration;

@Configuration
public class SamlSecurityConfiguration extends SamlServiceProviderServerBeanConfiguration {

private final SamlAppConfig samlAppConfig;

public SamlSecurityConfiguration(SamlAppConfig samlAppConfig){
this.samlAppConfig = samlAppConfig;
}

@Override
protected SamlServerConfiguration getDefaultHostSamlServerConfiguration() {
return samlAppConfig;
}
}

Create a SamlWebSecurityConfiguration.java file in the com.example.saml.securitypackage.

package com.example.saml.security;

import static org.springframework.security.saml.provider.service.config.SamlServiceProviderSecurityDsl.serviceProvider;

import com.example.saml.saml.config.SamlAppConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.saml.provider.service.config.SamlServiceProviderSecurityConfiguration;
import org.springframework.security.saml.provider.service.config.SamlServiceProviderServerBeanConfiguration;

@EnableWebSecurity
@Configuration
@Order(1)
public class SamlWebSecurityConfiguration extends SamlServiceProviderSecurityConfiguration {

private SamlAppConfig samlAppConfig;

public SamlWebSecurityConfiguration(
SamlServiceProviderServerBeanConfiguration configuration, @Qualifier("samlAppConfig") SamlAppConfig samlAppConfig) {
super("/saml/sp/",configuration);
this.samlAppConfig = samlAppConfig;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.apply(serviceProvider()).configure(samlAppConfig);
}
}

Create a SecurityConfiguration.java file in the com.example.saml.securitypackage.

package com.example.saml.security;
import static org.springframework.http.HttpMethod.GET;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
@Configuration
@Order(2)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers(GET, "/home").permitAll()
.antMatchers(GET, "/api/samlLink").permitAll()
.antMatchers("/**").authenticated()
.and()
.formLogin().loginPage("/home")
;

http.csrf().disable();
http.headers().cacheControl().disable();
}
}

Create an IndexController.java file in the same directory and use it to set the default view to index.

package com.example.saml.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.saml.provider.provisioning.SamlProviderProvisioning;
import org.springframework.security.saml.provider.service.ServiceProviderService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class IndexController {

private SamlProviderProvisioning<ServiceProviderService> provisioning;

@Value("${saml.discovery.url:/saml/sp/discovery}")
private String samlDiscoveryUrl;

@Value("${saml.discovery.entity-id:https://sts.windows.net/c5ca272d-4a1f-4a91-bc8d-0612fc364138/}")
private String samlDiscoveryEntityId;

@Autowired
public void setSamlService(SamlProviderProvisioning<ServiceProviderService> provisioning) {
this.provisioning = provisioning;
}

@RequestMapping(value = {"/home"})
public String home(Model model) {

model.addAttribute("samlLink", "0; url='"+ samlDiscoveryUrl + "?idp="+ samlDiscoveryEntityId+"'");
return "saml-login";
}
@RequestMapping(value = {"/"})
public String home(){
return "index";
}


@RequestMapping(value = {"/main"})
public String main(){
return "main";
}

}

Run the App and Login With Microsoft Azure

Start the app using your IDE or mvn spring-boot:run and navigate to https://localhost:8443/main. You’ll be redirected to Microsoft to sign in and redirected back to your app

After you’ve logged in, you should see a screen like the one below.

You can test the scenario using the below credentials:
Username: dummybotuser8@gmail.com
Password: dummybot88

You can download the source code of this article at https://github.com/sravankadium/dummybot-example-saml

--

--