<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Emre Baykal</title>
    <description>The latest articles on DEV Community by Emre Baykal (@emre_baykal_a4a7a479d48c5).</description>
    <link>https://dev.to/emre_baykal_a4a7a479d48c5</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3847401%2F80842041-66f0-4ae5-bdf1-1d471dd9b006.jpg</url>
      <title>DEV Community: Emre Baykal</title>
      <link>https://dev.to/emre_baykal_a4a7a479d48c5</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emre_baykal_a4a7a479d48c5"/>
    <language>en</language>
    <item>
      <title>HPE Morpheus Enterprise &amp; VM Essentials SAML Integration with Keycloak: A Complete Technical Guide</title>
      <dc:creator>Emre Baykal</dc:creator>
      <pubDate>Sat, 28 Mar 2026 17:26:07 +0000</pubDate>
      <link>https://dev.to/emre_baykal_a4a7a479d48c5/hpe-morpheus-enterprise-vm-essentials-saml-integration-with-keycloak-a-complete-technical-guide-7a8</link>
      <guid>https://dev.to/emre_baykal_a4a7a479d48c5/hpe-morpheus-enterprise-vm-essentials-saml-integration-with-keycloak-a-complete-technical-guide-7a8</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 What is SAML 2.0?
&lt;/h3&gt;

&lt;p&gt;SAML (Security Assertion Markup Language) 2.0 is an XML-based open standard for exchanging authentication and authorization data between two parties: an &lt;strong&gt;Identity Provider (IdP)&lt;/strong&gt; that authenticates users, and a &lt;strong&gt;Service Provider (SP)&lt;/strong&gt; that hosts the application. Instead of every application managing its own username/password database, SAML lets you delegate authentication to a central IdP. When a user logs in once at the IdP, they get access to all connected SPs without entering credentials again — this is &lt;strong&gt;Single Sign-On (SSO)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practical terms: the user clicks "Login with SSO" on the application, gets redirected to the IdP login page, authenticates there, and is sent back to the application with a cryptographically signed XML document (the "SAML assertion") that proves who they are and what groups they belong to.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Why Keycloak?
&lt;/h3&gt;

&lt;p&gt;There are several IdP options available (Okta, Azure AD, ADFS, Ping Identity, etc.), so why Keycloak?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open-source and free&lt;/strong&gt; — No per-user licensing costs, which matters at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted&lt;/strong&gt; — Full control over your identity infrastructure; no dependency on external SaaS providers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol versatility&lt;/strong&gt; — Supports SAML 2.0, OpenID Connect, and OAuth 2.0 in a single platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LDAP/AD federation&lt;/strong&gt; — Connects directly to Active Directory without migrating users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes-native&lt;/strong&gt; — Runs well as a containerized deployment with built-in clustering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CNCF project&lt;/strong&gt; — Active community, regular releases, and long-term sustainability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.3 What We Will Build
&lt;/h3&gt;

&lt;p&gt;Enterprise environments demand centralized identity management. When you operate HPE Morpheus Enterprise as your cloud management platform, integrating it with a dedicated Identity Provider (IdP) via SAML 2.0 eliminates password sprawl and gives you single sign-on (SSO) across the entire infrastructure stack.&lt;/p&gt;

&lt;p&gt;In this post, we walk through the entire journey: understanding how Morpheus handles SAML under the hood, deploying Keycloak on Kubernetes as your IdP, wiring the two together, and diagnosing issues when things do not go as planned. Every configuration value and YAML snippet comes from a real lab deployment, so you can replicate it in your own environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lab environment:&lt;/strong&gt; Kubernetes (single-node / Minikube compatible), Keycloak 26.x, Morpheus Enterprise 8.x, Active Directory for user federation.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Morpheus SAML Architecture
&lt;/h2&gt;

&lt;p&gt;HPE Morpheus Enterprise supports SAML 2.0 as an Identity Source type within its Administration panel. Understanding how Morpheus participates in the SAML exchange is essential before configuring anything on the Keycloak side.&lt;/p&gt;

&lt;p&gt;📸 IMAGE: Morpheus &amp;gt; Login Screen&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpkhxjfj01eoynggqsyg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpkhxjfj01eoynggqsyg.png" alt=" " width="695" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📸 IMAGE: Morpheus &amp;gt; Forward Keycloak Login Screen&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8v0yna8llvpcbe0imcc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8v0yna8llvpcbe0imcc.png" alt=" " width="695" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Morpheus as a SAML Service Provider (SP)
&lt;/h3&gt;

&lt;p&gt;Morpheus acts exclusively as a &lt;strong&gt;SAML Service Provider&lt;/strong&gt;. It does not function as an Identity Provider itself. When a user attempts to log in via SSO, Morpheus generates a SAML AuthnRequest, redirects the browser to the configured IdP, and then consumes the SAML Response (assertion) returned by the IdP.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Key SAML Endpoints in Morpheus
&lt;/h3&gt;

&lt;p&gt;When you create a SAML Identity Source in Morpheus, the platform automatically generates two critical values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SP Entity ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A unique identifier for Morpheus as a Service Provider. Auto-generated from the hostname, e.g. &lt;code&gt;https://morpheus-url/saml/&amp;lt;uniqueID&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SP ACS URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The callback URL where the IdP posts the SAML Response after authentication, e.g. &lt;code&gt;https://morpheus-url/externalLogin/callback/&amp;lt;uniqueID&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Login Redirect URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The IdP's SAML SSO endpoint where Morpheus sends the AuthnRequest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAML Logout Redirect URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The IdP's SAML SLO endpoint for single logout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The SP Entity ID and ACS URL are generated only after you save the Identity Source for the first time. You must save first, then copy these values to configure the IdP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.3 SAML Request and Response Configuration
&lt;/h3&gt;

&lt;p&gt;Morpheus provides granular control over how SAML requests are signed and responses are validated:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Options &amp;amp; Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAML Request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No Signature / Self Signed / Custom RSA Signature — Controls whether AuthnRequest messages are signed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAML Response&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Do Not Validate / Validate Assertion Signature — Controls signature validation on the IdP's assertion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;POST Binding Mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ON/OFF — Uses HTTP-POST binding instead of HTTP-Redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Includes SAML Request Parameter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes/No — Whether the SAML request is included in the redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.4 Assertion Attribute Mappings
&lt;/h3&gt;

&lt;p&gt;Morpheus maps SAML assertion attributes to internal user fields:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Morpheus Field&lt;/th&gt;
&lt;th&gt;Expected SAML Attribute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Given Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;firstName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Surname&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lastName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;email&lt;/code&gt; (or NameID)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2.5 Role Mapping Mechanism
&lt;/h3&gt;

&lt;p&gt;Morpheus supports role-based access control through SAML group assertions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role Mapping Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Default Role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The role assigned to all authenticated users (e.g., Standard User)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Role Attribute Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The SAML attribute containing group/role info (e.g., &lt;code&gt;groups&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Required Role Attribute Value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A group name the user must belong to for authorization (e.g., &lt;code&gt;mspusers&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;📸 IMAGE: Morpheus &amp;gt; Identity Sources &amp;gt; Keycloak&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4d8izve0axdxvfqj0fu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4d8izve0axdxvfqj0fu7.png" alt="Morpheus SAML SSO Configuration" width="722" height="868"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Keycloak-SAML Ecosystem: How It Works
&lt;/h2&gt;

&lt;p&gt;Keycloak is an open-source Identity and Access Management (IAM) solution maintained by the CNCF. It supports OpenID Connect, OAuth 2.0, and SAML 2.0 protocols natively. In our setup, Keycloak serves as the SAML Identity Provider (IdP) that authenticates users against an Active Directory backend via LDAP federation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Core Keycloak Concepts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Realm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A tenant-level isolation boundary. Each realm has its own users, clients, roles, and identity providers. Our realm: &lt;code&gt;morpheus-lab&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An application that delegates authentication to Keycloak. Morpheus is registered as a SAML client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Federation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Allows Keycloak to pull users from LDAP/Active Directory without duplicating credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protocol Mappers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transform user attributes and group memberships into SAML assertions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Roles &amp;amp; Groups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Realm-level and client-level roles. AD groups can be synced and mapped into assertions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3.2 SAML 2.0 Authentication Flow (SP-Initiated SSO)
&lt;/h3&gt;

&lt;p&gt;The diagram below illustrates the SP-Initiated SAML SSO flow between Morpheus and Keycloak:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdz38hluaexap97meqmwe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdz38hluaexap97meqmwe.png" alt="SAML 2.0 SP-Initiated SSO Flow Diagram" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-step:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user navigates to the Morpheus login page and clicks the SSO login button.&lt;/li&gt;
&lt;li&gt;Morpheus generates a SAML AuthnRequest and redirects the user's browser to Keycloak's SAML endpoint: &lt;code&gt;https://keycloak-server:30443/realms/morpheus-lab/protocol/saml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Keycloak presents the login form. The user enters their AD credentials.&lt;/li&gt;
&lt;li&gt;Keycloak authenticates the user against the federated LDAP/AD backend.&lt;/li&gt;
&lt;li&gt;On successful authentication, Keycloak constructs a &lt;strong&gt;SAML Response&lt;/strong&gt; containing signed assertions with user attributes (&lt;code&gt;firstName&lt;/code&gt;, &lt;code&gt;lastName&lt;/code&gt;) and group memberships (&lt;code&gt;groups&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Keycloak POSTs the SAML Response to the Morpheus ACS URL.&lt;/li&gt;
&lt;li&gt;Morpheus validates the assertion signature, maps attributes and roles, creates or updates the user session, and grants access.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.3 Authentication vs Authorization
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Authentication (Who are you?)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keycloak acts as the authentication broker&lt;/strong&gt;, sitting between the application (Morpheus) and the identity store (Active Directory).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Federation (LDAP)&lt;/strong&gt; allows Keycloak to verify credentials against AD without storing passwords locally. Keycloak performs LDAP BIND operations to authenticate users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MFA&lt;/strong&gt; can be layered on top through Keycloak's authentication flow configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Management:&lt;/strong&gt; Once authenticated, Keycloak creates a session. Subsequent SAML requests within the session's lifetime do not require re-authentication (SSO behavior).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvef5xllize3ka1tvqyt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvef5xllize3ka1tvqyt6.png" alt="Keycloak authentication flow diagram" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Authorization (What can you do?)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Keycloak syncs AD groups via the &lt;strong&gt;LDAP Group Mapper&lt;/strong&gt; (&lt;code&gt;mspusers&lt;/code&gt;, &lt;code&gt;apparchitech&lt;/code&gt;, &lt;code&gt;selfservice&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;SAML Group List Mapper&lt;/strong&gt; serializes group memberships into the SAML assertion as a &lt;code&gt;groups&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;Morpheus reads the &lt;code&gt;groups&lt;/code&gt; attribute and maps it to internal roles:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mspusers&lt;/code&gt; → authorized user (Required Role)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apparchitech&lt;/code&gt; → Application Architect role&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;selfservice&lt;/code&gt; → Self-Service User role&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Users not in the required group are &lt;strong&gt;denied access&lt;/strong&gt; even if authentication succeeds.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.4 Single Logout (SLO) Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User clicks Logout → Morpheus sends LogoutRequest → Keycloak terminates session
→ Keycloak POSTs LogoutResponse to /login/auth → User lands on login page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The Logout Service POST Binding URL must be set to &lt;code&gt;/login/auth&lt;/code&gt; (the login page), NOT the ACS callback URL. Morpheus's ACS handler cannot process LogoutResponse objects and throws a &lt;code&gt;GroovyCastException&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Deploying Keycloak on Kubernetes
&lt;/h2&gt;

&lt;p&gt;This section walks through deploying a production-grade Keycloak cluster on Kubernetes using a single YAML manifest.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The complete Kubernetes manifest used in this guide is available on GitHub:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/emrbaykal/morpheus-k8/blob/main/sso-k8s/keycloak/keycloak.yaml" rel="noopener noreferrer"&gt;keycloak.yaml on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.1 Architecture Overview
&lt;/h3&gt;

&lt;p&gt;The deployment stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keycloak StatefulSet&lt;/strong&gt; (2 replicas) with Infinispan clustering for HA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL Deployment&lt;/strong&gt; with PersistentVolumeClaim (10Gi)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Secret&lt;/strong&gt; for admin and database passwords&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-signed TLS certificates&lt;/strong&gt; generated by init containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NodePort Service&lt;/strong&gt; for external HTTPS access on port 30443&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headless Service&lt;/strong&gt; for Infinispan/JGroups cluster discovery&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.2 Prerequisites
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running Kubernetes cluster or Minikube instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; configured and connected to your cluster&lt;/li&gt;
&lt;li&gt;At least &lt;strong&gt;4GB RAM&lt;/strong&gt; and &lt;strong&gt;2 CPU cores&lt;/strong&gt; available for the Keycloak + PostgreSQL pods&lt;/li&gt;
&lt;li&gt;A storage provisioner (default StorageClass or Rook-Ceph). For Minikube, enable it with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;default-storageclass
minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;storage-provisioner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Minikube users:&lt;/strong&gt; Start Minikube with sufficient resources:&lt;br&gt;
&lt;code&gt;minikube start --cpus=4 --memory=8192 --driver=docker&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Network &amp;amp; DNS requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Kubernetes node IP must be reachable from the Morpheus server (for SAML redirects)&lt;/li&gt;
&lt;li&gt;The Morpheus server hostname (e.g., &lt;code&gt;morpheus-server&lt;/code&gt;) must be resolvable from both the user's browser &lt;strong&gt;and&lt;/strong&gt; the Keycloak pods. If you're using a local domain, add entries to &lt;code&gt;/etc/hosts&lt;/code&gt; on the machines or configure your internal DNS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall rules:&lt;/strong&gt; Ensure these ports are open between the components:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User Browser&lt;/td&gt;
&lt;td&gt;Morpheus Server&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;td&gt;Access Morpheus UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Browser&lt;/td&gt;
&lt;td&gt;K8s Node&lt;/td&gt;
&lt;td&gt;30443&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;td&gt;Keycloak login page (SAML redirect)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Morpheus Server&lt;/td&gt;
&lt;td&gt;K8s Node&lt;/td&gt;
&lt;td&gt;30443&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;td&gt;SAML backchannel (POST binding)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;K8s Pod Network&lt;/td&gt;
&lt;td&gt;AD Domain Controller&lt;/td&gt;
&lt;td&gt;389&lt;/td&gt;
&lt;td&gt;LDAP&lt;/td&gt;
&lt;td&gt;User federation / authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Active Directory requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;dedicated service account&lt;/strong&gt; for Keycloak LDAP binding (e.g., &lt;code&gt;svc-keycloak&lt;/code&gt;). This account needs &lt;strong&gt;read-only access&lt;/strong&gt; to the Users container (&lt;code&gt;CN=Users,DC=yourdomain,DC=local&lt;/code&gt;). It does not need Domain Admin privileges — basic "Read all user information" permission is sufficient&lt;/li&gt;
&lt;li&gt;AD groups that will map to Morpheus roles (e.g., &lt;code&gt;mspusers&lt;/code&gt;, &lt;code&gt;apparchitech&lt;/code&gt;, &lt;code&gt;selfservice&lt;/code&gt;) must exist and users must be members of the appropriate groups&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3 Step 1: Prepare Secrets
&lt;/h3&gt;

&lt;p&gt;The YAML uses a Kubernetes Secret to store sensitive credentials. The passwords are Base64-encoded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Encode your passwords&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'YourAdminPassword!'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;span class="c"&gt;# Output: WW91*****UGFzc3***cmQh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'YourDBPassword!'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;span class="c"&gt;# Output: WW9************mQh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Secret resource in the YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak-secret&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;admin-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;base64-encoded-admin-password&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;db-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;base64-encoded-db-password&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never commit plain-text passwords to version control. Use a secrets manager (Vault, Sealed Secrets) in production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.4 Step 2: Namespace and Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespace&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/part-of&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sso-stack&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-pvc&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ReadWriteOnce&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.5 Step 3: PostgreSQL Database
&lt;/h3&gt;

&lt;p&gt;Keycloak requires an external database in production mode. Key points from our manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:17&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD&lt;/span&gt;
        &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak-secret&lt;/span&gt;
            &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PGDATA&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data/pgdata&lt;/span&gt;
    &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-U"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keycloak"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256Mi&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100m&lt;/span&gt;
      &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;500m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.6 Step 4: Keycloak StatefulSet
&lt;/h3&gt;

&lt;p&gt;The Keycloak deployment uses a &lt;strong&gt;StatefulSet with 2 replicas&lt;/strong&gt; for high availability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Init Containers:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;wait-for-postgres:&lt;/strong&gt; Polls PostgreSQL port 5432 before Keycloak starts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;generate-tls-cert:&lt;/strong&gt; Generates a self-signed TLS certificate (10 years validity)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;initContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;generate-tls-cert&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine:3.19&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;apk add --no-cache openssl&lt;/span&gt;
        &lt;span class="s"&gt;openssl req -x509 -nodes -days 3650 \&lt;/span&gt;
          &lt;span class="s"&gt;-newkey rsa:2048 \&lt;/span&gt;
          &lt;span class="s"&gt;-keyout /certs/tls.key \&lt;/span&gt;
          &lt;span class="s"&gt;-out /certs/tls.crt \&lt;/span&gt;
          &lt;span class="s"&gt;-subj "/CN=keycloak-morpheus-lab/O=Lab/C=TR"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Environment Variables:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_BOOTSTRAP_ADMIN_USERNAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initial admin username (&lt;code&gt;admin&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_BOOTSTRAP_ADMIN_PASSWORD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Admin password from Secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;KC_DB&lt;/code&gt; / &lt;code&gt;KC_DB_URL_HOST&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Database type and host (&lt;code&gt;postgres&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_HTTPS_CERTIFICATE_FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to TLS certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_HTTPS_CERTIFICATE_KEY_FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to TLS key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_CACHE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clustering mode (&lt;code&gt;ispn&lt;/code&gt; = Infinispan)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_HOSTNAME_STRICT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disabled for NodePort/self-signed setups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_FEATURES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Additional features (&lt;code&gt;token-exchange&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KC_HEALTH_ENABLED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables &lt;code&gt;/health/*&lt;/code&gt; endpoints for probes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Health Probes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;startupProbe:&lt;/strong&gt; &lt;code&gt;/health/started&lt;/code&gt; — 1s interval, 600 retries (10-minute startup window)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;readinessProbe:&lt;/strong&gt; &lt;code&gt;/health/ready&lt;/code&gt; — Every 10s, 3 failures to mark unready&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;livenessProbe:&lt;/strong&gt; &lt;code&gt;/health/live&lt;/code&gt; — Every 10s, 3 failures to restart pod&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.7 Step 5: Services
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Type &amp;amp; Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;keycloak&lt;/code&gt; (ClusterIP)&lt;/td&gt;
&lt;td&gt;Internal access: ports 8080 (HTTP) and 8443 (HTTPS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;keycloak-discovery&lt;/code&gt; (Headless)&lt;/td&gt;
&lt;td&gt;JGroups cluster member discovery on port 7800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;keycloak-nodeport&lt;/code&gt; (NodePort)&lt;/td&gt;
&lt;td&gt;External access: port &lt;strong&gt;30080&lt;/strong&gt; (HTTP) and &lt;strong&gt;30443&lt;/strong&gt; (HTTPS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.8 Step 6: Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apply the complete manifest&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; keycloak.yaml

&lt;span class="c"&gt;# Watch the rollout&lt;/span&gt;
kubectl rollout status statefulset/keycloak &lt;span class="nt"&gt;-n&lt;/span&gt; keycloak &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10m

&lt;span class="c"&gt;# Verify all pods are running&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; keycloak

&lt;span class="c"&gt;# Access the admin console&lt;/span&gt;
&lt;span class="c"&gt;# https://&amp;lt;node-ip&amp;gt;:30443/admin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; For Minikube, use &lt;code&gt;minikube ip&lt;/code&gt; to get the node IP. For a multi-node cluster, any node's IP will work with NodePort.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📸 IMAGE: Terminal output of kubectl get pods -n keycloak showing all pods running&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7ezfolhb43tk9nfz9rz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7ezfolhb43tk9nfz9rz.png" alt="Terminal output of kubectl get pods -n keycloak showing all pods running" width="800" height="211"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Keycloak-Morpheus SAML Integration Guide
&lt;/h2&gt;

&lt;p&gt;With Keycloak deployed and running, we can now configure the SAML integration. The configuration involves both the Keycloak side (IdP) and the Morpheus side (SP), and the &lt;strong&gt;order matters&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important — Configuration Order:&lt;/strong&gt;&lt;br&gt;
There is a chicken-and-egg situation here. Keycloak needs the &lt;strong&gt;SP Entity ID&lt;/strong&gt; and &lt;strong&gt;ACS URL&lt;/strong&gt; to create the SAML client, but these values are auto-generated by Morpheus only after you save an Identity Source. The correct order is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the Keycloak Realm and LDAP Federation first (Steps 1-2)&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;preliminary&lt;/strong&gt; Identity Source in Morpheus to obtain the SP Entity ID and ACS URL (Step 3)&lt;/li&gt;
&lt;li&gt;Use those values to create the SAML Client in Keycloak (Steps 4-7)&lt;/li&gt;
&lt;li&gt;Come back to Morpheus and complete the Identity Source configuration (Step 8)&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.1 Step 1: Create the Keycloak Realm
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to Keycloak Admin Console at &lt;code&gt;https://keycloak-server:30443/admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click the realm dropdown (top-left) and select "Create Realm"&lt;/li&gt;
&lt;li&gt;Set Realm Name to: &lt;code&gt;morpheus-lab&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click Create&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📸 IMAGE: Keycloak Admin Console &amp;gt; Manage Realm &amp;gt; Create Realm&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5c3rfxj98koe6lad6ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5c3rfxj98koe6lad6ng.png" alt="Keycloak Admin Console &amp;gt; Create Realm dialog" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Step 2: Configure LDAP User Federation
&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;strong&gt;morpheus-lab &amp;gt; User Federation &amp;gt; Add New provider &amp;gt; LDAP&lt;/strong&gt; and configure:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LAB AD&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vendor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Active Directory&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ldap://active-directory:389&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bind Type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;simple&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bind DN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CN=svc-keycloak,CN=Users,DC=domain,DC=domain&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Users DN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CN=Users,DC=domain,DC=domain&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Username LDAP Attribute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sAMAccountName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit Mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;READ_ONLY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import Users&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ON&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Add Group Mapper&lt;/strong&gt; (Mappers &amp;gt; Add mapper):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mapper Type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;group-ldap-mapper&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Groups DN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CN=Users,DC=domain,DC=domian&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group Name LDAP Attribute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Membership LDAP Attribute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;member&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;READ_ONLY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Roles Retrieve Strategy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LOAD_GROUPS_BY_MEMBER_ATTRIBUTE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; After saving, click &lt;strong&gt;'Sync all users'&lt;/strong&gt; and &lt;strong&gt;'Sync LDAP groups to Keycloak'&lt;/strong&gt; to import users and groups from Active Directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📸 IMAGE: Keycloak &amp;gt; User Federation &amp;gt; LAB AD settings&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fotbn2eukf4wo109g7s45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fotbn2eukf4wo109g7s45.png" alt="LDAP 1" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwk7n772eavobu2vhax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwk7n772eavobu2vhax.png" alt="LDAP 2" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 Step 3: Create Preliminary Identity Source in Morpheus (Get SP Entity ID)
&lt;/h3&gt;

&lt;p&gt;Before creating the SAML client in Keycloak, we need to obtain the SP Entity ID and ACS URL from Morpheus. These values are auto-generated and unique to your Morpheus instance.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to Morpheus at &lt;code&gt;https://morpheus-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Administration &amp;gt; Identity Sources&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;+ Add Identity Source&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Type&lt;/strong&gt; to &lt;code&gt;SAML SSO&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Name&lt;/strong&gt; to &lt;code&gt;Keycloak-SSO&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For now, enter any placeholder URL in &lt;strong&gt;Login Redirect URL&lt;/strong&gt; (e.g., &lt;code&gt;https://placeholder.local&lt;/code&gt;) — we will update this later&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save Changes&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After saving, Morpheus generates and displays two critical values in the Identity Source list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SP Entity ID&lt;/strong&gt; — e.g., &lt;code&gt;https://morpheus-server/saml/N3h***B***O&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SP ACS URL&lt;/strong&gt; — e.g., &lt;code&gt;https://morpheus-server/externalLogin/callback/N***3***O&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Copy both of these values!&lt;/strong&gt; You will need them in the next step to configure the SAML client in Keycloak. The &lt;code&gt;N3****BO&lt;/code&gt; part is a unique identifier generated by your Morpheus instance — yours will be different.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📸 IMAGE: Morpheus &amp;gt; Identity Sources list showing the auto-generated SP Entity ID and ACS URL&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01w6rcum5xhjfzkd2ona.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01w6rcum5xhjfzkd2ona.png" alt=" " width="731" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4 Step 4: Create the SAML Client in Keycloak
&lt;/h3&gt;

&lt;p&gt;Now go back to the Keycloak Admin Console and create the SAML client using the values from the previous step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;morpheus-lab &amp;gt; Clients &amp;gt; Create Client&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set Client Type to &lt;strong&gt;SAML&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Client ID&lt;/strong&gt; to the SP Entity ID you copied from Morpheus: &lt;code&gt;https://morpheus-server/saml/N3h**K**5**&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set Name to: &lt;code&gt;Morpheus Enterprise&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Access Settings:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Root URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://morpheus-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Valid Redirect URIs (ACS URL)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://morpheus-server/externalLogin/callback/N3h**K**5**&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Master SAML Processing URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://morpheus-server/externalLogin/callback/N3h**K**5**&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IDP-Initiated SSO URL Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;morpheus&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SAML Capabilities:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name ID Format&lt;/td&gt;
&lt;td&gt;&lt;code&gt;username&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Force POST Binding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ON&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Include AuthnStatement&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ON&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Signature &amp;amp; Encryption:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sign Documents&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ON&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign Assertions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ON&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature Algorithm&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RSA_SHA256&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client Signature Required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;OFF&lt;/code&gt; (CRITICAL!)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt Assertions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OFF&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; &lt;code&gt;Client Signature Required&lt;/code&gt; must be &lt;strong&gt;OFF&lt;/strong&gt;. Morpheus signs requests with a self-signed certificate that does not match the certificate registered in Keycloak. If this is ON, every SAML request from Morpheus will be rejected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📸 IMAGE: Keycloak &amp;gt; Clients &amp;gt; Morpheus Enterprise &amp;gt; Settings&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0q4zq6rljlnm1qyut85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0q4zq6rljlnm1qyut85.png" alt=" " width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.5 Step 5: Configure Logout (Advanced Settings)
&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Advanced tab &amp;gt; Fine Grain SAML Endpoint Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logout Service POST Binding URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://morpheus-server/login/auth&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is crucial!&lt;/strong&gt; Setting this URL to &lt;code&gt;/login/auth&lt;/code&gt; prevents the &lt;code&gt;GroovyCastException&lt;/code&gt; bug. The Morpheus ACS callback handler cannot process SAML LogoutResponse objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.6 Step 6: Configure SAML Mappers
&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Client Scopes &amp;gt; dedicated &amp;gt; Mappers&lt;/strong&gt; and add:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mapper 1: Groups (Group List)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mapper Type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Group list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Attribute Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;groups&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Attribute NameFormat&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Basic&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single Group Attribute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OFF&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full Group Path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OFF&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Mapper 2: firstName (User Attribute)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mapper Type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;User Attribute&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Attribute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;firstName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Attribute Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;firstName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Mapper 3: lastName (User Attribute)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mapper Type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;User Attribute&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Attribute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lastName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Attribute Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lastName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;📸 IMAGE: Keycloak &amp;gt; Client Scopes &amp;gt; dedicated &amp;gt; Mappers list&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjq2jo6p96otyqp3i2xb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjq2jo6p96otyqp3i2xb.png" alt=" " width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.7 Step 7: Copy the Realm Certificate
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;morpheus-lab &amp;gt; Realm Settings &amp;gt; Keys&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Find the &lt;strong&gt;RS256&lt;/strong&gt; key row and click the &lt;strong&gt;Certificate&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Copy the entire certificate string&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📸 IMAGE: Keycloak &amp;gt; Realm Settings &amp;gt; Keys &amp;gt; RS256 certificate&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlg74czikm8u6dks5cpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlg74czikm8u6dks5cpv.png" alt=" " width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.8 Step 8: Complete the Morpheus Identity Source Configuration
&lt;/h3&gt;

&lt;p&gt;Now go back to the &lt;strong&gt;preliminary Identity Source&lt;/strong&gt; you created in Step 3 and update it with the real values. Navigate to &lt;strong&gt;Administration &amp;gt; Identity Sources &amp;gt; Keycloak-SSO &amp;gt; Edit&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Login Redirect URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://keycloak-server:30443/realms/morpheus-lab/protocol/saml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Logout Redirect URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://keycloak-server:30443/realms/morpheus-lab/protocol/saml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Includes SAML Request Parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Yes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST Binding Mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ON&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Request&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Self Signed&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Response&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Validate Assertion Signature&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML Response Public Key&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(paste RS256 certificate from Keycloak)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Assertion Attribute Mappings:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Morpheus Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Given Name Attribute Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;firstName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Surname Attribute Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lastName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Role Mappings:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Morpheus Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default Role&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Standard User&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role Attribute Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;groups&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Required Role Attribute Value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mspusers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application Architect Role&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apparchitech&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self Service User Role&lt;/td&gt;
&lt;td&gt;&lt;code&gt;selfservice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Click &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;📸 IMAGE: Morpheus &amp;gt; Identity Sources &amp;gt; Keycloak-SSO configuration&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zd2fdz1gjw1za4ofo7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zd2fdz1gjw1za4ofo7t.png" alt=" " width="718" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.9 Step 9: Test the Integration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open a new browser / incognito window&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;https://morpheus-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click the SSO login option (Keycloak-SSO should appear)&lt;/li&gt;
&lt;li&gt;You will be redirected to Keycloak's login page&lt;/li&gt;
&lt;li&gt;Enter an AD username (e.g., &lt;code&gt;emre.baykal&lt;/code&gt;) and password&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. On success, you will be redirected back to Morpheus and logged in
&lt;/h2&gt;

&lt;h2&gt;
  
  
  6. Troubleshooting SAML Integration
&lt;/h2&gt;

&lt;p&gt;SAML integrations can fail silently or produce cryptic errors. This section covers the most common issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Diagnostic Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SAML Tracer&lt;/strong&gt; (Browser Extension)&lt;/td&gt;
&lt;td&gt;Captures SAML requests/responses in real-time. Available for Firefox and Chrome&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Keycloak Events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enable in Realm Settings &amp;gt; Events. Shows auth attempts and errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Keycloak Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kubectl logs statefulset/keycloak -n keycloak -f&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Morpheus Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/log/morpheus/morpheus-ui/current&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Base64 Decoder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;`echo '' \&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6.2 Common Issues and Solutions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Issue 1: 'Invalid Requester' Error
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keycloak returns 'Invalid requester' or 'Client not found'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SP Entity ID in Morpheus doesn't match Client ID in Keycloak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Copy the exact SP Entity ID from Morpheus Identity Source and use it as Client ID in Keycloak&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Issue 2: Signature Validation Failure
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;'Signature validation failed' or 'Invalid signature'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SAML Response Public Key in Morpheus doesn't match Keycloak's RS256 certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Copy fresh certificate from Keycloak &amp;gt; Realm Settings &amp;gt; Keys &amp;gt; RS256 &amp;gt; Certificate. &lt;strong&gt;Must be updated after every Keycloak redeployment&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Issue 3: User Authenticated but Access Denied
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User authenticates at Keycloak but gets 'Access Denied' in Morpheus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User not in the required group, or Group List mapper missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1) Verify user is in {% raw %}&lt;code&gt;mspusers&lt;/code&gt; AD group. 2) Check Group List mapper in client scopes. 3) Use SAML Tracer to verify &lt;code&gt;groups&lt;/code&gt; attribute in assertion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Issue 4: GroovyCastException on Logout
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500 error on logout with &lt;code&gt;GroovyCastException&lt;/code&gt; in logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LogoutResponse sent to ACS URL instead of login page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Set Logout Service POST Binding URL to &lt;code&gt;https://morpheus-server/login/auth&lt;/code&gt; in Keycloak Advanced settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Issue 5: Login Loop / Redirect Loop
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser keeps redirecting between Morpheus and Keycloak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mixed HTTP/HTTPS, clock skew, or invalid ACS URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1) Ensure both use HTTPS. 2) Verify NTP sync (SAML assertions are time-sensitive). 3) Check Valid Redirect URIs matches exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Issue 6: Attributes Not Mapped
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;firstName/lastName are empty, roles not assigned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SAML mappers missing or attribute names don't match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1) Add User Attribute mappers for &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt;. 2) Names are &lt;strong&gt;case-sensitive&lt;/strong&gt;. 3) Use SAML Tracer to verify attributes in assertion XML&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Issue 7: LDAP Users Not Appearing
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No users appear after LDAP federation setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wrong Bind DN, Users DN, or network connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1) Test connectivity from pod. 2) Verify Bind DN has read access. 3) Click 'Sync all users'. 4) Check Keycloak logs for LDAP errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6.3 SAML Assertion Debugging Checklist
&lt;/h3&gt;

&lt;p&gt;When debugging, use SAML Tracer to capture the assertion and verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NameID:&lt;/strong&gt; Present and in expected format (&lt;code&gt;username&lt;/code&gt;)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuer:&lt;/strong&gt; Matches the Keycloak realm URL?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AudienceRestriction:&lt;/strong&gt; Audience matches Morpheus SP Entity ID?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditions/NotBefore/NotOnOrAfter:&lt;/strong&gt; Valid timestamps? Check for clock skew&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AuthnStatement:&lt;/strong&gt; Present? (Required by Morpheus)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AttributeStatement:&lt;/strong&gt; &lt;code&gt;firstName&lt;/code&gt;, &lt;code&gt;lastName&lt;/code&gt;, &lt;code&gt;groups&lt;/code&gt; attributes present with correct values?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature:&lt;/strong&gt; Assertion signed? Certificate matches?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.4 Useful Debug Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check Keycloak pod logs&lt;/span&gt;
kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; statefulset/keycloak &lt;span class="nt"&gt;-n&lt;/span&gt; keycloak

&lt;span class="c"&gt;# Decode a SAML Response from browser&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;base64-saml-response&amp;gt;'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; | xmllint &lt;span class="nt"&gt;--format&lt;/span&gt; -

&lt;span class="c"&gt;# Test LDAP connectivity from inside the cluster&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; keycloak-0 &lt;span class="nt"&gt;-n&lt;/span&gt; keycloak &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'nc -zv domain-controller 389'&lt;/span&gt;

&lt;span class="c"&gt;# Check Keycloak health&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; keycloak-0 &lt;span class="nt"&gt;-n&lt;/span&gt; keycloak &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  curl &lt;span class="nt"&gt;-sk&lt;/span&gt; https://localhost:8443/health/ready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Conclusion
&lt;/h2&gt;

&lt;p&gt;Integrating Morpheus Enterprise with Keycloak via SAML provides a robust, centralized authentication solution for enterprise cloud management. Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Morpheus acts as a SAML SP;&lt;/strong&gt; Keycloak acts as the SAML IdP with AD backend&lt;/li&gt;
&lt;li&gt;The Kubernetes deployment uses a &lt;strong&gt;StatefulSet with Infinispan clustering&lt;/strong&gt; for HA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-signed TLS certificates&lt;/strong&gt; are generated by init containers — no external cert-manager required for lab environments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Client Signature Required&lt;/code&gt; must be &lt;strong&gt;OFF&lt;/strong&gt; in Keycloak for Morpheus compatibility&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Logout Service POST Binding URL&lt;/strong&gt; must point to &lt;code&gt;/login/auth&lt;/code&gt; to avoid the &lt;code&gt;GroovyCastException&lt;/code&gt; bug&lt;/li&gt;
&lt;li&gt;Always &lt;strong&gt;update the SAML Response Public Key&lt;/strong&gt; in Morpheus after redeploying Keycloak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have questions or ran into a different issue? Drop a comment below!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by Emre Baykal — March 2026&lt;/em&gt;&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>sso</category>
      <category>kubernetes</category>
      <category>morpheus</category>
    </item>
  </channel>
</rss>
