Single Sign-On with SAML doesn't have to be a headache. Whether you're migrating from a legacy authentication system or building a new infrastructure from scratch, Keycloak offers a robust solution that scales with your needs. This guide cuts through the complexity to deliver actionable steps for setting up SSO SAML with Keycloak in your environment.
Understanding the Keycloak-SAML Ecosystem
Before jumping into configuration, let's establish a solid foundation.
Keycloak is an open-source Identity and Access Management (IAM) solution backed by Red Hat. SAML 2.0 is an XML-based protocol that enables secure cross-domain authentication.
The authentication flow works like this:
- Your user attempts to access a protected application (Service Provider)
- The SP redirects to Keycloak (Identity Provider)
- Keycloak authenticates the user
- Keycloak generates a SAML assertion (a digitally signed XML document)
- This assertion is sent back to your application, proving the user's identity
The beauty here is that Keycloak handles the complex parts – credential storage, password policies, and federation – while your applications focus on their core functionality.
Deploying Keycloak: Production-Ready Installation Approaches
While the quick Docker method works for development, production environments need more consideration. Here are your options:
Docker Deployment with Persistence
# Create volumes for persistence
docker volume create keycloak-db
docker volume create keycloak-data
# Run with PostgreSQL for production use
docker run -d --name postgres \
-e POSTGRES_DB=keycloak \
-e POSTGRES_USER=keycloak \
-e POSTGRES_PASSWORD=password \
-v keycloak-db:/var/lib/postgresql/data \
postgres:13
# Run Keycloak connected to PostgreSQL
docker run -d --name keycloak \
-p 8443:8443 \
-e DB_VENDOR=postgres \
-e DB_ADDR=postgres \
-e DB_DATABASE=keycloak \
-e DB_USER=keycloak \
-e DB_PASSWORD=password \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin_password \
-e PROXY_ADDRESS_FORWARDING=true \
-v keycloak-data:/opt/keycloak/data \
--link postgres:postgres \
quay.io/keycloak/keycloak:latest \
start --https-certificate-file=/opt/keycloak/conf/server.crt.pem \
--https-certificate-key-file=/opt/keycloak/conf/server.key.pem
Standalone Server Installation
For direct server installation, you'll need:
- JDK 11+ installed
- A supported database (PostgreSQL/MySQL/MariaDB/Oracle/MSSQL)
Download the latest version from the Keycloak website and extract it:
# Extract the archive
tar -xvzf keycloak-{version}.tar.gz
cd keycloak-{version}
# Configure the database in conf/keycloak.conf
# Example for PostgreSQL:
# db=postgres
# db-url=jdbc:postgresql://localhost/keycloak
# db-username=keycloak
# db-password=password
# Start in production mode
bin/kc.sh start --optimized
Optimizing Organizational Structure for Enterprise Growth
Realms in Keycloak are isolation units for managing different sets of applications and users. For enterprise deployments, consider this architecture:
- Master Realm: Used exclusively for Keycloak administration
- Corporate Realm: For internal applications and employee access
- Customer Realm: For customer-facing applications with separate user bases
- Partner Realm: For third-party integrations with limited access
To create a well-structured realm:
- Navigate to the admin console
- Hover over the realm selector (top-left) and click "Create Realm"
- Set a meaningful name that reflects its purpose (e.g., "corporate" or "partners")
- Configure realm-specific settings:
- Enable/disable user registration based on access patterns
- Set session timeouts appropriate for security requirements
- Configure password policies (complexity, expiration, history)
- Set up email server for notifications and recovery
How to Configure SAML Clients into Service Provider Integration
SAML clients in Keycloak represent your applications. Here's a comprehensive approach to setting them up:
- Go to Clients → Create Client → Select SAML
- Configure the foundational settings:
- Client ID: Use your application's entity ID (typically a URL like
https://app.example.com/saml
) - Name: A human-readable identifier
- Description: Document the application's purpose for your team
- Client ID: Use your application's entity ID (typically a URL like
- Configure advanced settings in the client configuration tabs:
Essential SAML Configuration Parameters and Their Implications
Setting | Recommended Value | Technical Impact |
---|---|---|
Sign Documents | ON | Ensures all SAML messages are digitally signed |
Sign Assertions | ON | Adds an additional signature to the assertion itself |
Encrypt Assertions | Optional (ON for sensitive data) | Adds XML encryption to assertions |
Client Signature Required | ON | Requires the SP to sign authentication requests |
Force POST Binding | ON | Uses HTTP POST for all bindings instead of redirects |
Front Channel Logout | ON | Enables browser-based logout propagation |
Name ID Format | urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress |
Identifies users by email across systems |
Valid Redirect URIs | https://app.example.com/saml/SSO |
Prevents open redirector vulnerabilities |
Master SAML Processing URL | https://app.example.com/saml |
Base URL for all SAML endpoints |
Attribute Mapping for Rich User Profiles
SAML allows passing user attributes to applications. Set these up under the Mappers tab:
- Create a mapper for each attribute your application needs
- Common mappings include:
- Email →
urn:oid:1.2.840.113549.1.9.1
- First Name →
urn:oid:2.5.4.42
- Last Name →
urn:oid:2.5.4.4
- Groups →
urn:oid:2.5.4.31
- Email →
Example of creating a custom mapper for department information:
- Go to Clients → Your Client → Mappers → Create
- Set Mapper Type to "User Attribute"
- Set User Attribute to "department"
- Set SAML Attribute Name to "department"
- Set SAML Attribute NameFormat to "Basic"
How to Implement Role-Based Access Control through SAML Assertions
Keycloak's RBAC can be transmitted to applications through SAML:
- Create roles in Keycloak: Realm Roles or Client Roles
- Assign roles to users or groups
- Create a role mapper:
- Go to Clients → Your Client → Mappers → Create
- Name: "role-mapper"
- Mapper Type: "Role list"
- Role Attribute Name: "Roles"
- SAML Attribute NameFormat: "Basic"
- Single Role Attribute: ON (combines all roles into one attribute)
Your application can then parse the Roles attribute from the SAML assertion to apply permissions.
How to Generate and Manage SAML Metadata for Cross-Domain Trust
SAML relies on metadata exchange for establishing trust. Here's how to handle it properly:
Exporting Keycloak's Identity Provider Metadata
Fetch the metadata from:
https://your-keycloak-server/realms/{realm-name}/protocol/saml/descriptor
This XML document contains:
- Keycloak's entity ID
- Supported bindings and endpoints
- Certificate for signature verification
- Attribute formats supported
For high-security environments, consider signing the metadata itself:
- Go to Realm Settings → Keys
- Create a dedicated key pair for metadata signing
- Enable metadata signing in the Realm Settings → General tab
Importing Service Provider Metadata into Keycloak
If your application generates SAML metadata:
- Go to Clients → Create Client
- Select "Import from file" and upload the metadata XML
- Keycloak will pre-configure most settings based on the metadata
Keycloak Integration: Language-Specific Examples
Java Spring Boot Integration
For a Spring Boot application using Spring Security SAML:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;
@Bean
public SAMLAuthenticationProvider samlAuthenticationProvider() {
SAMLAuthenticationProvider provider = new SAMLAuthenticationProvider();
provider.setUserDetails(samlUserDetailsServiceImpl);
provider.setForcePrincipalAsString(false);
return provider;
}
@Bean
public MetadataGenerator metadataGenerator() {
MetadataGenerator generator = new MetadataGenerator();
generator.setEntityId("https://your-app.example.com");
generator.setExtendedMetadata(extendedMetadata());
generator.setIncludeDiscoveryExtension(false);
generator.setKeyManager(keyManager());
return generator;
}
@Bean
public ExtendedMetadata extendedMetadata() {
ExtendedMetadata metadata = new ExtendedMetadata();
metadata.setIdpDiscoveryEnabled(false);
metadata.setSignMetadata(true);
metadata.setEcpEnabled(false);
return metadata;
}
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:/saml/keystore.jks");
Map<String, String> passwords = new HashMap<>();
passwords.put("keystore", "keystorepass");
passwords.put("sp", "sppass");
return new JKSKeyManager(storeFile, "keystorepass", passwords, "sp");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(samlFilter(), BasicAuthenticationFilter.class);
}
@Bean
public SAMLProcessingFilter samlFilter() throws Exception {
SAMLProcessingFilter filter = new SAMLProcessingFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
filter.setAuthenticationFailureHandler(failureHandler());
return filter;
}
@Bean
public AuthenticationSuccessHandler successHandler() {
SavedRequestAwareAuthenticationSuccessHandler handler =
new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl("/home");
return handler;
}
@Bean
public AuthenticationFailureHandler failureHandler() {
SimpleUrlAuthenticationFailureHandler handler =
new SimpleUrlAuthenticationFailureHandler();
handler.setUseForward(true);
handler.setDefaultFailureUrl("/error");
return handler;
}
}
The above code configures Spring Security with SAML-based authentication in a Spring Boot application.
1. Security Configuration Setup
- The
@Configuration
and@EnableWebSecurity
annotations define this class as a security configuration. - It extends
WebSecurityConfigurerAdapter
, allowing custom security settings.
2. SAML Authentication Provider
SAMLAuthenticationProvider
handles authentication using SAML and integrates with a custom user details service (SAMLUserDetailsServiceImpl
).
3. Metadata Configuration
MetadataGenerator
generates metadata for the Service Provider (SP).- The SP entity ID is set to
"https://your-app.example.com"
. ExtendedMetadata
controls metadata settings (e.g., signing metadata, disabling IDP discovery).
4. Key Management
KeyManager
loads a Java Keystore (JKS) from the classpath (/saml/keystore.jks
) to store cryptographic keys used for SAML authentication.- It uses predefined passwords for the keystore and service provider key.
5. Security Rules & HTTP Configuration
- CSRF is disabled (
http.csrf().disable()
). /public/**
endpoints are accessible to everyone, while other requests require authentication.- A SAML authentication filter (
samlFilter()
) is added before the basic authentication filter to process SAML requests.
6. SAML Authentication Processing
SAMLProcessingFilter
handles SAML authentication responses.- The authentication manager processes login attempts.
- If successful, users are redirected to
"/home"
. - If authentication fails, users are redirected to
"/error"
.
Node.js Express Integration
For Node.js applications using Passport:
const express = require('express');
const passport = require('passport');
const SamlStrategy = require('passport-saml').Strategy;
const fs = require('fs');
const app = express();
// Configure the SAML strategy
const samlStrategy = new SamlStrategy({
callbackUrl: 'https://your-app.example.com/login/callback',
entryPoint: 'https://keycloak.example.com/realms/your-realm/protocol/saml',
issuer: 'https://your-app.example.com',
cert: fs.readFileSync('./certs/idp-certificate.pem', 'utf-8'),
identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
validateInResponseTo: true,
disableRequestedAuthnContext: true
}, (profile, done) => {
// Parse user from SAML profile
return done(null, {
id: profile.nameID,
email: profile.email,
name: profile.displayName,
roles: profile['http://schemas.microsoft.com/ws/2008/06/identity/claims/role']
});
});
passport.use(samlStrategy);
// Initialize passport middleware
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
// SAML endpoints
app.get('/login',
passport.authenticate('saml', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/');
}
);
app.post('/login/callback',
passport.authenticate('saml', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/');
}
);
// Protected routes
app.get('/profile', ensureAuthenticated, (req, res) => {
res.json(req.user);
});
// Authentication middleware
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
app.listen(3000, () => {
console.log('Server running on port 3000');
});
This Express.js app sets up SAML authentication using Passport.js and passport-saml
. Here's how it works:
1. Dependencies
You’re pulling in express
for the web server, passport
for authentication, and passport-saml
to handle SAML. The fs
module loads the IdP’s public certificate for validation.
2. SAML Strategy Setup
- The app defines a SAML authentication strategy with
passport-saml
. - The
callbackUrl
is where the IdP (e.g., Keycloak) sends the SAML response. entryPoint
is the IdP’s login URL.- The app’s
issuer
acts as its entity ID. - The IdP’s public cert is read from a file and used for verification.
identifierFormat
ensures the user’s email is used as the unique identifier.- The callback extracts user details (ID, email, name, roles) from the SAML response.
3. Passport Setup
- The strategy is registered with
passport.use()
. passport.initialize()
andpassport.session()
are set up to manage authentication.serializeUser
anddeserializeUser
store/retrieve user details in the session.
4. Authentication Routes
GET /login
triggers the SAML authentication flow.POST /login/callback
processes the SAML response and logs in the user.- Both routes use Passport’s
authenticate('saml')
middleware.
5. Protected Routes
GET /profile
returns the authenticated user’s details.ensureAuthenticated
middleware blocks access if the user isn’t logged in.
6. Running the Server
The app listens on port 3000 and logs a startup message.
In short: This setup lets users authenticate via SAML (Keycloak or another IdP), keeps them logged in using sessions, and restricts access to protected routes. If you’ve got an existing Express app and need SAML auth, you can drop this in with minimal changes.
.NET Core Integration
For ASP.NET Core applications:
// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = "SAML2";
})
.AddCookie()
.AddSaml2p("SAML2", options =>
{
options.Licensee = "DEMO";
options.LicenseKey = "DEMO";
options.SPOptions.EntityId = new EntityId("https://your-app.example.com");
options.SPOptions.ReturnUrl = new Uri("https://your-app.example.com");
options.IdentityProviders.Add(
new IdentityProvider(
new EntityId("https://keycloak.example.com/realms/your-realm"),
options.SPOptions)
{
MetadataLocation = "https://keycloak.example.com/realms/your-realm/protocol/saml/descriptor"
});
});
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Other middleware...
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
// In a controller
[Authorize]
public class ProfileController : Controller
{
public IActionResult Index()
{
var claims = User.Claims;
// Process SAML attributes from claims
return View();
}
}
This code sets up SAML authentication in an ASP.NET Core app using the SAML2P middleware. Here’s how it works:
1. ConfigureServices (Dependency Injection & Authentication Setup)
- The app configures authentication with cookies for session management.
- The default authentication and sign-in scheme is set to cookies, while the challenge scheme is set to SAML2.
- The SAML provider is added using
AddSaml2p()
, where:- The service provider (SP) entity ID is set to the app’s URL.
- The return URL specifies where users are redirected after authentication.
- The identity provider (IdP) is configured with its metadata URL from Keycloak.
2. Configure (Middleware Pipeline)
app.UseAuthentication()
enables authentication handling.app.UseAuthorization()
ensures authorization policies are applied.- The app defines a default route for controllers.
3. ProfileController (Protected Resource)
- The
[Authorize]
attribute ensures only authenticated users can access the profile page. - The controller retrieves SAML attributes from the user’s claims.
This setup allows you to authenticate via SAML (e.g., with Keycloak), maintains sessions using cookies, and restricts access to protected routes based on authentication.
Advanced Keycloak SAML Features
Implementing Multi-Factor Authentication with SAML
Keycloak supports adding MFA to your SAML authentication flow:
- Go to Authentication → Flows
- Select "Browser" flow
- Click "Copy" to create a custom flow
- Add an execution for OTP Form
- Set the requirement to "Required"
- Go to Bindings and set your custom flow as the Browser Flow
This forces users to provide a second factor during SAML authentication without modifying your applications.
Configuring Just-in-Time User Provisioning
When using external identity providers, you can provision users on first login:
- Go to Identity Providers → Add provider (e.g., SAML)
- Configure the connection to the external IdP
- Under the "First Login Flow" setting, select "first broker login"
- Configure attribute mapping to populate user profiles
Implementing SAML Single Logout (SLO)
SAML SLO enables logout propagation across all applications:
- In your SAML client configuration, enable "Front Channel Logout"
- Set the logout service URL to your application's logout endpoint
- In your application, implement a SAML logout endpoint:
// Spring Boot example
@RequestMapping("/logout")
public void logout(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
// Redirect to Keycloak SLO endpoint
try {
response.sendRedirect("/saml/SingleLogout");
} catch (IOException e) {
logger.error("Error during logout", e);
}
}
Troubleshooting Complex SAML Issues:
Decoding and Analyzing SAML Messages
SAML messages are Base64-encoded XML. To troubleshoot:
- Use browser dev tools to capture the SAMLRequest or SAMLResponse parameters
- Decode using a tool like SAML-tracer
- Examine the XML for errors in:
- Signature validation
- Audience restrictions
- Time validity
- Attribute formatting
Example of a common issue in decoded XML:
<saml2:Conditions NotBefore="2023-05-01T12:00:00Z" NotOnOrAfter="2023-05-01T12:10:00Z">
<saml2:AudienceRestriction>
<saml2:Audience>https://wrong-audience.example.com</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
If your client ID is https://correct-audience.example.com
, this will fail with an audience mismatch.
Certificate Issues and Resolution Strategies
Certificate problems are among the most common SAML issues:
- Expiration: Check certificate validity dates in Keycloak (Realm Settings → Keys)
- Algorithm Mismatch: Ensure both parties support the same signing algorithm (RSA-SHA256 is recommended)
- Trust Chain: If using CA-signed certificates, ensure the entire trust chain is imported
To update an expired certificate:
- Go to Realm Settings → Keys
- Generate a new certificate pair
- Update your applications with the new certificate
- Set a calendar reminder before the next expiration
Clock Synchronization Problems
SAML assertions have a validity window, typically 5-10 minutes. Ensure:
- All servers use NTP for time synchronization
- Time zones are correctly configured
- Consider increasing the NotOnOrAfter window in high-latency environments
Scaling Keycloak for Enterprise SAML Deployments
High Availability Architecture
For enterprise deployments, implement:
- Load-balanced Keycloak cluster (minimum 3 nodes)
- Shared database backend (PostgreSQL with replication)
- Sticky sessions at the load balancer level
- Distributed cache using Infinispan
Configuration example for clustered deployment:
# In conf/keycloak.conf
# Database
db=postgres
db-url=jdbc:postgresql://db-cluster.example.com:5432/keycloak
db-username=keycloak
db-password=password
# Clustering
cache=ispn
cache-stack=kubernetes
# Health check for load balancer
health-enabled=true
# For performance
http-pool-max-threads=100
Monitoring and Metrics
Integrate Keycloak with your monitoring stack:
- Enable metrics endpoint in
conf/keycloak.conf
:
metrics-enabled=true
- Configure Prometheus scraping:
scrape_configs:
- job_name: 'keycloak'
metrics_path: /metrics
static_configs:
- targets: ['keycloak-1:8080', 'keycloak-2:8080', 'keycloak-3:8080']
- Set up alerts for:
- Failed login attempts (spike detection)
- Certificate expiration (30 days warning)
- JVM memory pressure
- Response time degradation
Conclusion
Setting up SSO SAML with Keycloak is undeniably complex, but it pays dividends in simplified user experience, enhanced security, and reduced maintenance overhead.
Remember that authentication is a critical infrastructure component - take time to design it right, test thoroughly, and maintain it diligently. Your future self (and your users) will thank you.
FAQs
How do I handle multiple domains in my SAML setup?
You can configure multiple valid redirect URIs in your client settings. For cross-domain scenarios:
- Add all domains to the valid redirect URIs list
- Ensure cookies are properly handled (consider using JWT instead of cookie-based sessions)
- For completely separate domains, consider using separate clients for each domain
Can Keycloak act as both a SAML Identity Provider and Service Provider simultaneously?
Yes, Keycloak can be both:
- As an IdP, it provides authentication for your applications
- As an SP, it can authenticate against external systems
To configure Keycloak as an SP:
- Go to Identity Providers → Add Provider → SAML v2.0
- Configure the connection to the external IdP
- Set up user attribute mapping
How do I migrate users from an existing SAML provider to Keycloak?
Approach this in phases:
- Set up Keycloak to authenticate against your existing IdP
- Gradually migrate user data to Keycloak
- Once all users are migrated, switch the authentication flow
For password migration, use Keycloak's password hash import feature if your existing system uses compatible hashing algorithms.
How can I customize the SAML assertion contents?
Use protocol mappers to control what's included in assertions:
- Go to Clients → Your Client → Mappers
- Create mappers for each attribute you want to include
- Configure the mapper type, SAML attribute name, and source
For complex mappings, use script mappers with JavaScript to transform data.
What's the difference between SAML and OIDC, and when should I use each?
Both are SSO protocols, but with key differences:
SAML:
- XML-based protocol
- Well-established in enterprise environments
- Better support in legacy applications
- More verbose payloads
- Primarily uses browser redirects and POST bindings
OIDC:
- JSON-based protocol built on OAuth 2.0
- More modern and lightweight
- Better for mobile and SPA applications
- Easier to implement for developers
- Supports multiple flows (authorization code, implicit, etc.)
Choose SAML when:
- Integrating with enterprise applications that require SAML
- Working in highly regulated industries with established SAML requirements
- Dealing with legacy systems
Choose OIDC when:
- Building modern web or mobile applications
- API-driven architecture is a priority
- User experience and performance are critical