{"id":2771,"date":"2026-05-04T09:55:30","date_gmt":"2026-05-04T13:55:30","guid":{"rendered":"https:\/\/shirishranjit.com\/blog1\/?page_id=2771"},"modified":"2026-05-04T09:55:31","modified_gmt":"2026-05-04T13:55:31","slug":"web-login-identity-federation-deep-dive-into-saml-oauth-oidc-mfa-secure-design","status":"publish","type":"page","link":"https:\/\/shirishranjit.com\/blog1\/architect-principles\/web-login-identity-federation-deep-dive-into-saml-oauth-oidc-mfa-secure-design","title":{"rendered":"Web Login &amp; Identity Federation: Deep Dive into SAML, OAuth\/OIDC, MFA &amp; Secure Design"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Web Login &amp; Identity Federation: Deep Dive into SAML, OAuth\/OIDC, MFA &amp; Secure Design<\/h1>\n\n\n\n<p>Modern web applications&nbsp;offload user authentication to a centralized Identity Provider (IdP)&nbsp;such as&nbsp;Ping Identity(PingFederate \/ PingOne Advanced Identity Cloud) or&nbsp;Microsoft Entra ID (Azure AD). This&nbsp;federated login&nbsp;model lets multiple applications trust a single identity hub for verifying user credentials, enabling&nbsp;Single Sign-On (SSO): users sign in once and gain access to multiple systems. The business application (the Service Provider or OAuth Client) never sees the user&#8217;s password \u2014 it receives only&nbsp;security tokens&nbsp;from the IdP. In SAML, the application trusts the IdP&#8217;s signed assertion and grants access without ever handling the user&#8217;s credentials directly. This design improves security and user experience: credentials are managed in one hardened service, and users avoid repeating logins across apps.<\/p>\n\n\n\n<p>Both&nbsp;SAML 2.0&nbsp;and&nbsp;OAuth 2.0 \/ OpenID Connect (OIDC)&nbsp;are widely-used standards enabling federation and SSO. SAML uses XML-based assertions and works best in browser-based enterprise applications, while OIDC uses lightweight JSON tokens and fits modern web, mobile, and API-driven architectures. Below is a comprehensive technical analysis of each, including how to incorporate&nbsp;multi-factor authentication (MFA),&nbsp;secure password handling,&nbsp;token design &amp; validation,&nbsp;API security, and&nbsp;threat mitigations&nbsp;\u2014 all oriented toward designing a&nbsp;custom identity solution&nbsp;with the robustness of a commercial IdP.<\/p>\n\n\n\n<style>\n        :root {\n        --accent: #464feb;\n        --timeline-ln: linear-gradient(to bottom, transparent 0%, #b0beff 15%, #b0beff 85%, transparent 100%);\n        --timeline-border: #ffffff;\n        --bg-card: #f5f7fa;\n        --bg-hover: #ebefff;\n        --text-title: #424242;\n        --text-accent: var(--accent);\n        --text-sub: #424242;\n        --radius: 12px;\n        --border: #e0e0e0;\n        --shadow: 0 2px 10px rgba(0, 0, 0, 0.06);\n        --hover-shadow: 0 4px 14px rgba(39, 16, 16, 0.1);\n        --font: \"Segoe Sans\", \"Segoe UI\", \"Segoe UI Web (West European)\", -apple-system, \"system-ui\", Roboto, \"Helvetica Neue\", sans-serif;\n        --overflow-wrap: break-word;\n    }\n\n    @media (prefers-color-scheme: dark) {\n        :root {\n            --accent: #7385ff;\n            --timeline-ln: linear-gradient(to bottom, transparent 0%, transparent 3%, #6264a7 30%, #6264a7 50%, transparent 97%, transparent 100%);\n            --timeline-border: #424242;\n            --bg-card: #1a1a1a;\n            --bg-hover: #2a2a2a;\n            --text-title: #ffffff;\n            --text-sub: #ffffff;\n            --shadow: 0 2px 10px rgba(0, 0, 0, 0.3);\n            --hover-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n            --border: #3d3d3d;\n        }\n    }\n\n    @media (prefers-contrast: more),\n    (forced-colors: active) {\n        :root {\n            --accent: ActiveText;\n            --timeline-ln: ActiveText;\n            --timeline-border: Canvas;\n            --bg-card: Canvas;\n            --bg-hover: Canvas;\n            --text-title: CanvasText;\n            --text-sub: CanvasText;\n            --shadow: 0 2px 10px Canvas;\n            --hover-shadow: 0 4px 14px Canvas;\n            --border: ButtonBorder;\n        }\n    }\n\n    .insights-container {\n        display: grid;\n        grid-template-columns: repeat(2,minmax(240px,1fr));\n        padding: 0px 16px 0px 16px;\n        gap: 16px;\n        margin: 0 0;\n        font-family: var(--font);\n    }\n\n    .insight-card:last-child:nth-child(odd){\n        grid-column: 1 \/ -1;\n    }\n\n    .insight-card {\n        background-color: var(--bg-card);\n        border-radius: var(--radius);\n        border: 1px solid var(--border);\n        box-shadow: var(--shadow);\n        min-width: 220px;\n        padding: 16px 20px 16px 20px;\n    }\n\n    .insight-card:hover {\n        background-color: var(--bg-hover);\n    }\n\n    .insight-card h4 {\n        margin: 0px 0px 8px 0px;\n        font-size: 1.1rem;\n        color: var(--text-accent);\n        font-weight: 600;\n        display: flex;\n        align-items: center;\n        gap: 8px;\n    }\n\n    .insight-card .icon {\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        width: 20px;\n        height: 20px;\n        font-size: 1.1rem;\n        color: var(--text-accent);\n    }\n\n    .insight-card p {\n        font-size: 0.92rem;\n        color: var(--text-sub);\n        line-height: 1.5;\n        margin: 0px;\n        overflow-wrap: var(--overflow-wrap);\n    }\n\n    .insight-card p b, .insight-card p strong {\n        font-weight: 600;\n    }\n\n    .metrics-container {\n        display:grid;\n        grid-template-columns:repeat(2,minmax(210px,1fr));\n        font-family: var(--font);\n        padding: 0px 16px 0px 16px;\n        gap: 16px;\n    }\n\n    .metric-card:last-child:nth-child(odd){\n        grid-column:1 \/ -1; \n    }\n\n    .metric-card {\n        flex: 1 1 210px;\n        padding: 16px;\n        background-color: var(--bg-card);\n        border-radius: var(--radius);\n        border: 1px solid var(--border);\n        text-align: center;\n        display: flex;\n        flex-direction: column;\n        gap: 8px;\n    }\n\n    .metric-card:hover {\n        background-color: var(--bg-hover);\n    }\n\n    .metric-card h4 {\n        margin: 0px;\n        font-size: 1rem;\n        color: var(--text-title);\n        font-weight: 600;\n    }\n\n    .metric-card .metric-card-value {\n        margin: 0px;\n        font-size: 1.4rem;\n        font-weight: 600;\n        color: var(--text-accent);\n    }\n\n    .metric-card p {\n        font-size: 0.85rem;\n        color: var(--text-sub);\n        line-height: 1.45;\n        margin: 0;\n        overflow-wrap: var(--overflow-wrap);\n    }\n\n    .timeline-container {\n        position: relative;\n        margin: 0 0 0 0;\n        padding: 0px 16px 0px 56px;\n        list-style: none;\n        font-family: var(--font);\n        font-size: 0.9rem;\n        color: var(--text-sub);\n        line-height: 1.4;\n    }\n\n    .timeline-container::before {\n        content: \"\";\n        position: absolute;\n        top: 0;\n        left: calc(-40px + 56px);\n        width: 2px;\n        height: 100%;\n        background: var(--timeline-ln);\n    }\n\n    .timeline-container > li {\n        position: relative;\n        margin-bottom: 16px;\n        padding: 16px 20px 16px 20px;\n        border-radius: var(--radius);\n        background: var(--bg-card);\n        border: 1px solid var(--border);\n    }\n\n    .timeline-container > li:last-child {\n        margin-bottom: 0px;\n    }\n\n    .timeline-container > li:hover {\n        background-color: var(--bg-hover);\n    }\n\n    .timeline-container > li::before {\n        content: \"\";\n        position: absolute;\n        top: 18px;\n        left: -40px;\n        width: 14px;\n        height: 14px;\n        background: var(--accent);\n        border: var(--timeline-border) 2px solid;\n        border-radius: 50%;\n        transform: translateX(-50%);\n        box-shadow: 0px 0px 2px 0px #00000012, 0px 4px 8px 0px #00000014;\n    }\n\n    .timeline-container > li h4 {\n        margin: 0 0 5px;\n        font-size: 1rem;\n        font-weight: 600;\n        color: var(--accent);\n    }\n\n    .timeline-container > li h4 em {\n        margin: 0 0 5px;\n        font-size: 1rem;\n        font-weight: 600;\n        color: var(--accent);\n        font-style: normal;\n    }\n\n    .timeline-container > li * {\n        margin: 0;\n        font-size: 0.9rem;\n        color: var(--text-sub);\n        line-height: 1.4;\n    }\n\n    .timeline-container > li * b, .timeline-container > li * strong {\n        font-weight: 600;\n    }\n        @media (max-width:600px){\n        .metrics-container,\n        .insights-container{\n            grid-template-columns:1fr;\n      }\n    }\n<\/style>\n<div class=\"insights-container\">\n  <div class=\"insight-card\">\n    <h4>?  The Core Idea: Centralized Authentication<\/h4>\n    <p>In a federated login, applications delegate sign-in to a centralized Identity Provider. The IdP verifies user credentials (e.g. passwords and MFA) and issues <strong>security tokens<\/strong> to the app. The app trusts the IdP and never handles the raw password itself. A microservices delivery platform must provide the ability to <em>secure authentication identities and issue secure tokens that can be verified by other system components<\/em>.<\/p>\n  <\/div>\n  <div class=\"insight-card\">\n    <h4>?  SAML vs. OAuth 2.0 \/ OIDC<\/h4>\n    <p><strong>SAML 2.0<\/strong> is an XML-based standard ideal for browser-based enterprise SSO (released 2002), while <strong>OIDC<\/strong> (released 2014, built on OAuth 2.0) uses JSON Web Tokens and suits modern web, mobile, and API-driven applications. Most organizations run both together rather than choosing one over the other.<\/p>\n  <\/div>\n  <div class=\"insight-card\">\n    <h4>?  MFA &#038; Security by Design<\/h4>\n    <p>Enterprise IdPs incorporate layered security: <strong>MFA<\/strong> (e.g. OTP, push, FIDO2 keys) at login, <strong>strong password hashing<\/strong> and TLS to protect credentials, short-lived signed tokens with scoping for API access, and runtime protection from common attack patterns including those described by the OWASP Top Ten and the OWASP API Security Top Ten. A custom solution should adopt these practices from the ground up.<\/p>\n  <\/div>\n<\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. SAML 2.0 Web SSO Flow (Federated Login via SAML)<\/h2>\n\n\n\n<p>Security Assertion Markup Language (SAML) 2.0&nbsp;is an open standard for exchanging authorization and authentication information. The Web Browser SAML\/SSO Profile with Redirect\/POST bindings is one of the most common SSO implementations. SAML became the enterprise standard for SSO in the early 2000s because organizations wanted users to log in once and access dozens of internal applications without repeatedly typing passwords. Below is a step-by-step&nbsp;SP-Initiated SAML login&nbsp;(using Ping Identity or Azure AD as the IdP):<\/p>\n\n\n\n<style>\n        :root {\n        --accent: #464feb;\n        --timeline-ln: linear-gradient(to bottom, transparent 0%, #b0beff 15%, #b0beff 85%, transparent 100%);\n        --timeline-border: #ffffff;\n        --bg-card: #f5f7fa;\n        --bg-hover: #ebefff;\n        --text-title: #424242;\n        --text-accent: var(--accent);\n        --text-sub: #424242;\n        --radius: 12px;\n        --border: #e0e0e0;\n        --shadow: 0 2px 10px rgba(0, 0, 0, 0.06);\n        --hover-shadow: 0 4px 14px rgba(39, 16, 16, 0.1);\n        --font: \"Segoe Sans\", \"Segoe UI\", \"Segoe UI Web (West European)\", -apple-system, \"system-ui\", Roboto, \"Helvetica Neue\", sans-serif;\n        --overflow-wrap: break-word;\n    }\n\n    @media (prefers-color-scheme: dark) {\n        :root {\n            --accent: #7385ff;\n            --timeline-ln: linear-gradient(to bottom, transparent 0%, transparent 3%, #6264a7 30%, #6264a7 50%, transparent 97%, transparent 100%);\n            --timeline-border: #424242;\n            --bg-card: #1a1a1a;\n            --bg-hover: #2a2a2a;\n            --text-title: #ffffff;\n            --text-sub: #ffffff;\n            --shadow: 0 2px 10px rgba(0, 0, 0, 0.3);\n            --hover-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n            --border: #3d3d3d;\n        }\n    }\n\n    @media (prefers-contrast: more),\n    (forced-colors: active) {\n        :root {\n            --accent: ActiveText;\n            --timeline-ln: ActiveText;\n            --timeline-border: Canvas;\n            --bg-card: Canvas;\n            --bg-hover: Canvas;\n            --text-title: CanvasText;\n            --text-sub: CanvasText;\n            --shadow: 0 2px 10px Canvas;\n            --hover-shadow: 0 4px 14px Canvas;\n            --border: ButtonBorder;\n        }\n    }\n\n    .insights-container {\n        display: grid;\n        grid-template-columns: repeat(2,minmax(240px,1fr));\n        padding: 0px 16px 0px 16px;\n        gap: 16px;\n        margin: 0 0;\n        font-family: var(--font);\n    }\n\n    .insight-card:last-child:nth-child(odd){\n        grid-column: 1 \/ -1;\n    }\n\n    .insight-card {\n        background-color: var(--bg-card);\n        border-radius: var(--radius);\n        border: 1px solid var(--border);\n        box-shadow: var(--shadow);\n        min-width: 220px;\n        padding: 16px 20px 16px 20px;\n    }\n\n    .insight-card:hover {\n        background-color: var(--bg-hover);\n    }\n\n    .insight-card h4 {\n        margin: 0px 0px 8px 0px;\n        font-size: 1.1rem;\n        color: var(--text-accent);\n        font-weight: 600;\n        display: flex;\n        align-items: center;\n        gap: 8px;\n    }\n\n    .insight-card .icon {\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        width: 20px;\n        height: 20px;\n        font-size: 1.1rem;\n        color: var(--text-accent);\n    }\n\n    .insight-card p {\n        font-size: 0.92rem;\n        color: var(--text-sub);\n        line-height: 1.5;\n        margin: 0px;\n        overflow-wrap: var(--overflow-wrap);\n    }\n\n    .insight-card p b, .insight-card p strong {\n        font-weight: 600;\n    }\n\n    .metrics-container {\n        display:grid;\n        grid-template-columns:repeat(2,minmax(210px,1fr));\n        font-family: var(--font);\n        padding: 0px 16px 0px 16px;\n        gap: 16px;\n    }\n\n    .metric-card:last-child:nth-child(odd){\n        grid-column:1 \/ -1; \n    }\n\n    .metric-card {\n        flex: 1 1 210px;\n        padding: 16px;\n        background-color: var(--bg-card);\n        border-radius: var(--radius);\n        border: 1px solid var(--border);\n        text-align: center;\n        display: flex;\n        flex-direction: column;\n        gap: 8px;\n    }\n\n    .metric-card:hover {\n        background-color: var(--bg-hover);\n    }\n\n    .metric-card h4 {\n        margin: 0px;\n        font-size: 1rem;\n        color: var(--text-title);\n        font-weight: 600;\n    }\n\n    .metric-card .metric-card-value {\n        margin: 0px;\n        font-size: 1.4rem;\n        font-weight: 600;\n        color: var(--text-accent);\n    }\n\n    .metric-card p {\n        font-size: 0.85rem;\n        color: var(--text-sub);\n        line-height: 1.45;\n        margin: 0;\n        overflow-wrap: var(--overflow-wrap);\n    }\n\n    .timeline-container {\n        position: relative;\n        margin: 0 0 0 0;\n        padding: 0px 16px 0px 56px;\n        list-style: none;\n        font-family: var(--font);\n        font-size: 0.9rem;\n        color: var(--text-sub);\n        line-height: 1.4;\n    }\n\n    .timeline-container::before {\n        content: \"\";\n        position: absolute;\n        top: 0;\n        left: calc(-40px + 56px);\n        width: 2px;\n        height: 100%;\n        background: var(--timeline-ln);\n    }\n\n    .timeline-container > li {\n        position: relative;\n        margin-bottom: 16px;\n        padding: 16px 20px 16px 20px;\n        border-radius: var(--radius);\n        background: var(--bg-card);\n        border: 1px solid var(--border);\n    }\n\n    .timeline-container > li:last-child {\n        margin-bottom: 0px;\n    }\n\n    .timeline-container > li:hover {\n        background-color: var(--bg-hover);\n    }\n\n    .timeline-container > li::before {\n        content: \"\";\n        position: absolute;\n        top: 18px;\n        left: -40px;\n        width: 14px;\n        height: 14px;\n        background: var(--accent);\n        border: var(--timeline-border) 2px solid;\n        border-radius: 50%;\n        transform: translateX(-50%);\n        box-shadow: 0px 0px 2px 0px #00000012, 0px 4px 8px 0px #00000014;\n    }\n\n    .timeline-container > li h4 {\n        margin: 0 0 5px;\n        font-size: 1rem;\n        font-weight: 600;\n        color: var(--accent);\n    }\n\n    .timeline-container > li h4 em {\n        margin: 0 0 5px;\n        font-size: 1rem;\n        font-weight: 600;\n        color: var(--accent);\n        font-style: normal;\n    }\n\n    .timeline-container > li * {\n        margin: 0;\n        font-size: 0.9rem;\n        color: var(--text-sub);\n        line-height: 1.4;\n    }\n\n    .timeline-container > li * b, .timeline-container > li * strong {\n        font-weight: 600;\n    }\n        @media (max-width:600px){\n        .metrics-container,\n        .insights-container{\n            grid-template-columns:1fr;\n      }\n    }\n<\/style>\n<ul class=\"timeline-container\">\n  <li>\n    <h4>Step 1: User Accesses Protected Application (SP)<\/h4>\n    <p>The user tries to access an application (Service Provider) without a session. The SP detects no active session and <strong>generates a SAML <code>AuthnRequest<\/code><\/strong> message. This request is typically sent via an HTTP <strong>redirect<\/strong> to the IdP&#8217;s SSO URL (compressed and URL-encoded) or via an auto-submitted form POST. The user&#8217;s browser is <strong>redirected to the IdP<\/strong>. The AuthnRequest must contain an <code>ID<\/code> (a string uniquely identifying the request) and an <code>SP<\/code> identifier.<\/p>\n  <\/li>\n  <li>\n    <h4>Step 2: Identity Provider Authenticates the User<\/h4>\n    <p>The IdP (PingFederate, Azure Entra ID, etc.) receives the AuthnRequest and identifies the requesting SP. It then <strong>authenticates the user using its own policies<\/strong>. The user enters credentials on the IdP&#8217;s login page served over HTTPS. TLS 1.2 (or higher) guarantees message confidentiality and integrity at the transport layer, countering eavesdropping, theft of user authentication information, theft of the bearer token, message deletion, message modification, and man-in-the-middle attacks. The IdP checks credentials against its user directory (e.g. Active Directory, Ping Directory, or an internal store). <strong>The user&#8217;s password is never shared with the SP<\/strong>. If policy requires it, the IdP may also enforce <strong>MFA at this stage<\/strong> before proceeding.<\/p>\n  <\/li>\n  <li>\n    <h4>Step 3: IdP Issues a SAML Assertion<\/h4>\n    <p>Upon successful authentication, the IdP generates a <strong>SAML Response<\/strong> containing a SAML <strong>Assertion<\/strong> \u2014 an XML document with the user&#8217;s identity information (subject name\/ID), authentication timestamp, conditions for validity, and optional attributes (e.g. roles, email). The IdP <strong>digitally signs<\/strong> the assertion (or entire response) with its private key using a certified key, which helps guarantee message integrity and authentication and counters man-in-the-middle (6.4.2), forged assertion (6.4.3), and message modification (7.1.1.7) attacks. Assertions may also be <strong>encrypted<\/strong> via XMLEnc to prevent disclosure of sensitive attributes post-transportation, countering theft of user authentication information (7.1.1.2). The response includes a unique ID and an <code>InResponseTo<\/code> field binding it to the original AuthnRequest \u2014 a safeguard whose absence famously left Google&#8217;s SSO vulnerable to a man-in-the-middle attack from a malicious SP. The IdP then redirects the user&#8217;s browser back to the SP&#8217;s <strong>Assertion Consumer Service (ACS)<\/strong> URL, typically via an auto-POST form.<\/p>\n  <\/li>\n  <li>\n    <h4>Step 4: Service Provider Validates &#038; Grants Access<\/h4>\n    <p>The SP&#8217;s ACS endpoint receives the SAML Response and validates it against the IdP&#8217;s <strong>public signing certificate<\/strong> (exchanged via SAML metadata beforehand). It checks: (a) the XML signature is valid and from the trusted IdP; (b) the assertion&#8217;s conditions \u2014 <code>NotBefore<\/code>\/<code>NotOnOrAfter<\/code> window, <code>AudienceRestriction<\/code> matching the SP&#8217;s identifier; (c) the <code>InResponseTo<\/code> matches the original request ID. Once verified, the assertion proves the user&#8217;s identity. The SP creates a local <strong>session<\/strong> and authorizes access. The user is now logged in without ever directly providing credentials to the SP.<\/p>\n  <\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">SAML Assertion Internals<\/h3>\n\n\n\n<p>A SAML 2.0 assertion issued by the IdP contains several key elements:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Authentication Statement:\u00a0Confirms\u00a0<em>when<\/em>\u00a0and\u00a0<em>how<\/em>\u00a0the user was authenticated (e.g., &#8220;via password plus OTP&#8221;). May include an\u00a0AuthnContextClassRef\u00a0indicating if MFA was used.<\/li>\n\n\n\n<li>Subject &amp; Identifiers:\u00a0The user&#8217;s identity (NameID, e.g. email format), plus a\u00a0<code>SubjectConfirmation<\/code>\u00a0element ensuring the token was delivered to the intended SP.<\/li>\n\n\n\n<li>Attribute Statements:\u00a0Optional user profile data (e.g. name, role, group) attested by the IdP, which the SP can use for authorization decisions.<\/li>\n\n\n\n<li>Conditions:\u00a0Validity constraints \u2014 a\u00a0<code>NotBefore<\/code>\/<code>NotOnOrAfter<\/code>\u00a0window (often just minutes) and\u00a0<code>AudienceRestriction<\/code>specifying the SP&#8217;s identifier.\u00a0Short lifetimes limit the impact of a stolen assertion\u00a0\u2014 a key defense recommended by OWASP.<\/li>\n\n\n\n<li>Signature:\u00a0The IdP&#8217;s digital signature covering the assertion using RSA or ECDSA via XML Digital Signature standards. The SP\u00a0must reject any unsigned or tampered SAML response.<\/li>\n\n\n\n<li>Encryption (Optional):\u00a0The IdP can encrypt the assertion with the SP&#8217;s public key via XMLEnc so only that specific SP can decrypt and read it.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">IdP-Initiated SSO<\/h3>\n\n\n\n<p>In an&nbsp;IdP-initiated&nbsp;flow, the user starts at the IdP portal and launches an application directly \u2014 the IdP creates an assertion&nbsp;without a prior AuthnRequest. Despite being convenient, this flow is considered less controlled; OWASP notes IdP-initiated SSO is supported primarily for backward compatibility (notably with SAML 1.1) and is susceptible to replay attacks (6.1.2). If it must be enabled, additional validation per SAML Profiles section 4.1.5 should be applied. SP-initiated SSO with strict&nbsp;<code>InResponseTo<\/code>&nbsp;validation is the preferred secure approach.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Session Management in SAML<\/h3>\n\n\n\n<p>After SAML login, sessions exist at two layers:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>IdP Session:\u00a0The IdP sets a secure cookie so the user remains authenticated. This enables SSO: if the user later accesses another SAML-enabled app, the IdP can silently re-authenticate them (no password needed) until this IdP session expires.<\/li>\n\n\n\n<li>SP Session:\u00a0The application creates its own session (e.g. an HTTP-only session cookie). The SAML assertion is typically consumed once at login; after that, the app trusts its own session mechanism.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Where SAML Remains the Right Choice<\/h3>\n\n\n\n<p>SAML remains appropriate for enterprise workforce SSO into HR systems, CRM platforms, and internal portals; B2B integrations where both parties operate within structured enterprise environments; government and regulated industries with long-standing SAML-based infrastructure; and legacy applications built before modern token-based protocols existed. In one enterprise environment, SAML is the only SSO integration currently offered to B2B customers, though introduction of OIDC is recommended for those SAML customers as part of the overall integration design.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. OAuth 2.0 &amp; OpenID Connect (OIDC) Login Flow<\/h2>\n\n\n\n<p>OpenID Connect (OIDC)&nbsp;is an identity layer built on top of OAuth 2.0. Where OAuth handles authorization (granting limited access to resources), OIDC adds authentication, giving applications a standardized way to verify who a user is. Instead of XML assertions, OIDC uses lightweight JSON Web Tokens (JWTs) that are compact, easy to validate across languages and frameworks, and travel natively across APIs, mobile apps, and microservices.<\/p>\n\n\n\n<p>A concrete example: the Staples.com consumer application was onboarded with CIAM using&nbsp;OpenID Connect protocol and PKCE&nbsp;for user authentication, integrating with Ping Advanced Identity Cloud. Below is the standard&nbsp;OIDC Authorization Code flow with PKCE:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">OIDC Token Internals<\/h3>\n\n\n\n<p>The&nbsp;ID Token&nbsp;in OIDC is a JSON Web Token \u2014 three Base64Url-encoded parts (header, payload, signature). IdPs publish their JWT signing keys in a JWKS (JSON Web Key Set) endpoint accessible via the&nbsp;<code>.well-known\/openid-configuration<\/code>discovery document. The client fetches the key matching the token&#8217;s&nbsp;<code>kid<\/code>&nbsp;(Key ID) to verify the signature. Standard claims include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>iss<\/code>\u00a0(issuer): Identifier of the IdP \u2014 the client must verify this matches the expected IdP.<\/li>\n\n\n\n<li><code>aud<\/code>\u00a0(audience): The client&#8217;s ID \u2014 the token is intended for this specific application.<\/li>\n\n\n\n<li><code>exp<\/code>\u00a0and\u00a0<code>iat<\/code>: Expiration and issued-at timestamps. Tokens should have short lifetimes.<\/li>\n\n\n\n<li>User claims:\u00a0<code>sub<\/code>\u00a0(subject ID),\u00a0<code>name<\/code>,\u00a0<code>email<\/code>, etc.<\/li>\n\n\n\n<li><code>acr<\/code>\u00a0or\u00a0<code>amr<\/code>: Authentication Context Class Reference or Authentication Methods References \u2014 indicating what measures were used (e.g., password, OTP). In the Ping implementation, specific authentication journeys can be assigned to OAuth clients by mapping\u00a0<code>acr_values<\/code>\u00a0to authentication journeys in the OpenID Connect configuration.<\/li>\n<\/ul>\n\n\n\n<p>Access Tokens&nbsp;can be JWTs (validated locally by checking signature, audience, expiry, and scopes) or opaque tokens (validated by calling the IdP&#8217;s&nbsp;introspection endpoint). The platform should support management of&nbsp;claims-based authorization, such that claims and scopes can be included in secure tokens for verification by other system components.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Where OIDC Fits Best<\/h3>\n\n\n\n<p>OIDC is ideal for modern web applications, SPAs, and mobile apps; API-driven architectures where services need to validate identity across calls; consumer-facing products where social login is expected; cloud-native and microservices environments; and any new system being built today. OIDC also gives users built-in consent flows, letting them control which attributes they share \u2014 a feature requiring extensive custom development in SAML.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. SAML vs. OIDC: Comparison<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td>Aspect<\/td><td>SAML 2.0<\/td><td>OIDC (OAuth 2.0 + ID)<\/td><\/tr><\/thead><tbody><tr><td>Released<\/td><td>2002<\/td><td>2014<\/td><\/tr><tr><td>Data Format<\/td><td>XML<\/td><td>JSON (JWT)<\/td><\/tr><tr><td>Built On<\/td><td>Independent standard<\/td><td>OAuth 2.0<\/td><\/tr><tr><td>Best For<\/td><td>Browser-based enterprise SSO<\/td><td>Web apps, mobile, APIs, SPAs<\/td><\/tr><tr><td>Token Type<\/td><td>SAML Assertion (signed XML)<\/td><td>ID Token + Access Token (JWT)<\/td><\/tr><tr><td>Integration Complexity<\/td><td>Higher \u2014 XML, certificates, metadata<\/td><td>Lower \u2014 REST APIs, standard libraries<\/td><\/tr><tr><td>Mobile Support<\/td><td>Limited<\/td><td>Native<\/td><\/tr><tr><td>User Consent<\/td><td>Manual to configure<\/td><td>Built in<\/td><\/tr><tr><td>Adoption Trend<\/td><td>Dominant in legacy enterprise<\/td><td>Growing in modern architectures<\/td><\/tr><tr><td>Login Flow<\/td><td>Browser redirects + auto-POST form carrying a SAML Response<\/td><td>Browser redirects to \/authorize, then server-to-server token exchange (Auth Code flow)<\/td><\/tr><tr><td>API Compatibility<\/td><td>Not designed for direct API calls; adapting to mobile\/API requires complex workarounds<\/td><td>Tokens travel naturally through HTTP headers and work natively with mobile clients and APIs<\/td><\/tr><tr><td>Security Risk Profile<\/td><td>Complexity makes it more prone to misconfiguration<\/td><td>Inherits OAuth 2.0&#8217;s documented threat model; common attack surfaces are better defined and easier to address<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Key Tradeoff:&nbsp;SAML&#8217;s XML verbosity creates more processing overhead and is harder to diagnose when misconfigured, while OIDC&#8217;s JSON\/JWT format is significantly faster to process \u2014 in high-throughput environments with thousands of API calls per minute, that difference is measurable. However, replacing functioning SAML integrations carries real cost and risk. The practical recommendation: if SAML is working in your environment, default new systems to OIDC going forward while maintaining SAML for existing integrations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ping Identity vs. Azure Entra ID \u2014 Conceptual Differences<\/h3>\n\n\n\n<p>Both platforms implement SAML, OAuth2, and OIDC. Key differences lie in deployment model and ecosystem:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Azure Entra ID (formerly Azure AD)\u00a0is a\u00a0cloud-based multi-tenant IdP and directory\u00a0tightly integrated with the Microsoft\/Office 365 ecosystem. Organizations use Azure AD as the central corporate identity source. It offers built-in Conditional Access, identity protection, and device integration. One enterprise initiative retired on-premises SiteMinder infrastructure and migrated all workforce SSO integrations to Azure AD, achieving\u00a0$2.2M in cost avoidance\u00a0along with streamlined user lifecycle provisioning, robust conditional access, entitlement management, and advanced Identity Governance capabilities. Azure AD&#8217;s architecture framework recommends using managed identities for resource-to-resource authentication, preferring passwordless methods, and enabling conditional access based on key security attributes especially for high-privilege accounts.<\/li>\n\n\n\n<li>Ping Identity\u00a0offers both on-premises (PingFederate) and cloud solutions (PingOne Advanced Identity Cloud). Ping provides greater deployment flexibility \u2014 enterprises can run their own IdP with custom integration, or use Ping&#8217;s cloud for customer identity management (CIAM) separately from corporate IT systems. In the same enterprise, Ping Identity&#8217;s cloud IDaaS was selected for\u00a0customer identity modernization, migrating away from on-premises CA Broadcom infrastructure to a cloud-based IDaaS service via Ping Identity. Ping&#8217;s strength is in customizable authentication &#8220;journeys&#8221; (with 12 journeys anticipated for one integration) and integration kits that can be tailored to unique use cases. The PingOne MFA Integration Kit allows PingFederate to use the PingOne MFA service, integrating MFA into SAML, WS-Fed, OAuth, and OIDC flows in a seamless way leveraging one configuration for all use cases.<\/li>\n<\/ul>\n\n\n\n<p>From a developer&#8217;s perspective,&nbsp;both&nbsp;platforms expose standard endpoints (SAML metadata, OAuth2\/OIDC endpoints) that custom applications integrate with in the same standard way \u2014 the differences lie in management, deployment flexibility, and ancillary features.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. Multi-Factor Authentication (MFA) Integration<\/h2>\n\n\n\n<p>MFA ensures that digital users are who they say they are by requiring at least two pieces of evidence from different categories: something they know, something they have, or something they are.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How MFA Fits into Authentication Flows<\/h3>\n\n\n\n<p>In a federated login,&nbsp;MFA is handled by the Identity Provider, not by each individual application. The IdP&#8217;s authentication policy decides when to require MFA:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Triggering Conditions:\u00a0The IdP can enforce MFA always, or conditionally (e.g., based on risk level, device posture, user group, network location). Azure AD&#8217;s Conditional Access enables risk-based MFA. Ping&#8217;s authentication journeys allow rule-based step-up. A custom IdP should have a\u00a0policy engine\u00a0to determine when extra factors are needed.<\/li>\n\n\n\n<li>Timing in the Flow:\u00a0If MFA is required, the IdP prompts\u00a0right after the primary password\u00a0is verified (Step 2 in both SAML and OIDC flows). The IdP pauses to perform the second factor before issuing any tokens. In Ping&#8217;s implementation, MFA preference and verification status are tracked as custom user attributes:\u00a0<code>FrIndexedInteger1<\/code>(tagged for Preferred MFA, bit-masked) and\u00a0<code>FrIndexedInteger2<\/code>\u00a0(tagged to Verified MFA, bit-masked).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">MFA Methods<\/h3>\n\n\n\n<p>Common second factors include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>TOTP\/OTP:\u00a0One-time passcodes generated by an authenticator app or sent via SMS.<\/li>\n\n\n\n<li>Push Approval:\u00a0Notifications sent to a mobile app (e.g., PingID, Microsoft Authenticator) requiring user confirmation.<\/li>\n\n\n\n<li>FIDO2\/WebAuthn:\u00a0Hardware security keys or platform biometric authenticators. These are\u00a0phishing-resistantbecause the private key never leaves the device and the login operation is cryptographically bound to the origin.<\/li>\n\n\n\n<li>Phone Callbacks:\u00a0Voice call verification.<\/li>\n<\/ul>\n\n\n\n<p>Ping Identity provides PingID for MFA, which supports web, VPN (via RADIUS protocol), Windows login, and Mac login as second-factor authentication. In the Staples.com integration,&nbsp;1Kosmos&nbsp;serves as the MFA partner, integrated via API into Ping Identity&#8217;s authentication journeys.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">MFA State Management<\/h3>\n\n\n\n<p>Implementing MFA requires the IdP to handle a multi-step interaction. The IdP must maintain the user&#8217;s partial authentication state between steps (e.g., password verified, awaiting OTP). In Ping&#8217;s implementation, this is managed through&nbsp;authentication journeys&nbsp;\u2014 configurable workflow trees that chain authentication nodes (username\/password ? MFA challenge ? success). Environment variables and secrets are used across scripts and authentication journeys.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Mitigating MFA Fatigue<\/h3>\n\n\n\n<p>A critical concern:&nbsp;MFA fatigue attacks&nbsp;where attackers bombard users with push notifications hoping for an inadvertent approval. Microsoft addressed this by enforcing&nbsp;number matching&nbsp;in Microsoft Authenticator push notifications as of May 8, 2023 \u2014 users must type a number displayed on the login screen into their authenticator app rather than simply tapping &#8220;Approve&#8221;. For custom builds, implement interactive verification that cannot be easily &#8220;fatigued&#8221; \u2014 e.g., requiring the user to enter a displayed code, or showing additional context (app name, geographic location) in the approval prompt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">MFA Signals in Tokens<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SAML:\u00a0The\u00a0<code>&lt;AuthnContextClassRef><\/code>\u00a0in the assertion can indicate the authentication method used (e.g.,\u00a0<code>urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken<\/code>\u00a0for OTP).<\/li>\n\n\n\n<li>OIDC:\u00a0The\u00a0<code>acr<\/code>\u00a0(Authentication Context Class Reference) and\u00a0<code>amr<\/code>\u00a0(Authentication Methods Reference) claims in the ID token indicate whether MFA was completed and which methods were used.<\/li>\n<\/ul>\n\n\n\n<p>Applications that need to&nbsp;ensure&nbsp;a user completed MFA should configure the IdP to require it and validate these token claims.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Secure Password Handling &amp; Server-Side Processing<\/h2>\n\n\n\n<p>When using federated IdPs, web applications&nbsp;do not handle raw passwords, but the IdP must handle them for password-based logins. Whether operating a custom IdP or using Ping\/Entra, secure password management is essential.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Password Transmission: TLS in Transit<\/h3>\n\n\n\n<p>All login pages and API calls must use HTTPS.&nbsp;TLS 1.2 (or higher) is the most common solution to guarantee message confidentiality and integrity at the transport layer, countering eavesdropping (7.1.1.1), theft of user authentication information (7.1.1.2), theft of the bearer token (7.1.1.3), message deletion (7.1.1.6), message modification (7.1.1.7), and man-in-the-middle (7.1.1.8) attacks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Password Storage: Hashing, Salting, and KDFs<\/h3>\n\n\n\n<p>Passwords must&nbsp;never&nbsp;be stored in plaintext or reversible encryption. Hashing is a one-way function \u2014 it is impossible to &#8220;decrypt&#8221; a hash and obtain the original plaintext value, making it the most appropriate approach for password validation. OWASP&#8217;s specific recommendations for password hashing algorithms (in priority order):<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td>Algorithm<\/td><td>Recommended Configuration<\/td><td>Notes<\/td><\/tr><\/thead><tbody><tr><td>Argon2id(preferred)<\/td><td>Minimum 19 MiB of memory, iteration count of 2, and 1 degree of parallelism<\/td><td>Memory-hard, best defense against GPU attacks<\/td><\/tr><tr><td>scrypt<\/td><td>Minimum CPU\/memory cost parameter of 2^17, minimum block size of 8 (1024 bytes), parallelization parameter of 1<\/td><td>Alternative when Argon2id unavailable<\/td><\/tr><tr><td>bcrypt(legacy)<\/td><td>Work factor of 10 or more, password limit of 72 bytes<\/td><td>Widely supported in legacy systems<\/td><\/tr><tr><td>PBKDF2(FIPS-140)<\/td><td>Work factor of 600,000 or more, internal hash function of HMAC-SHA-256<\/td><td>Required for FIPS-140 compliance<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Salting&nbsp;is critical: a unique, randomly generated string added to each password before hashing. Since the salt is unique for every user, an attacker must crack hashes one at a time using the respective salt rather than pre-computing a hash once. This makes cracking large numbers of hashes significantly harder, and also means it&#8217;s impossible to determine whether two users have the same password without cracking the hashes. Modern algorithms (Argon2id, bcrypt, PBKDF2) handle salt generation internally.<\/p>\n\n\n\n<p>Peppering&nbsp;provides additional defense in depth \u2014 a shared secret stored separately from the password database (ideally in HSMs or secrets vaults), preventing an attacker from cracking hashes even if they obtain the database. However, a pepper compromise requires forcing all protected users to reset passwords.<\/p>\n\n\n\n<p>Work Factor Tuning:&nbsp;The work factor makes hashing more computationally expensive, reducing the speed at which an attacker can crack hashes. As a general rule, calculating a hash should take&nbsp;less than one second&nbsp;to balance security and performance \u2014 if too high, it could enable denial-of-service via excessive CPU exhaustion from login attempts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Real-World Password Handling in Enterprise Migration<\/h3>\n\n\n\n<p>In one enterprise&#8217;s Ping Identity migration, a specific milestone was planned to &#8220;design solution to standardize password hashing from four legacy methods&#8221;, involving conversion and reconciliation of over&nbsp;1 million user accountsacross master accounts from Ping Directory to Ping AIC in production environments. The password hashing conversion approach was identified and under testing as of the most recent reporting.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Password Policy Example (Ping AIC Implementation)<\/h3>\n\n\n\n<p>The Ping AIC deployment enforces the following password policy:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td>Field<\/td><td>Min Length<\/td><td>Max Length<\/td><td>Requirements<\/td><\/tr><\/thead><tbody><tr><td>Password<\/td><td>8<\/td><td>20<\/td><td>8 characters minimum, case sensitive, no spaces. Must contain 3 of 4 criteria: uppercase letter, lowercase letter, number, special character<\/td><\/tr><tr><td>Email<\/td><td>N\/A<\/td><td>256<\/td><td>Must have local part, @ symbol, and valid domain<\/td><\/tr><tr><td>First name<\/td><td>1<\/td><td>15<\/td><td>Letters, numbers, spaces allowed<\/td><\/tr><tr><td>Last name<\/td><td>1<\/td><td>20<\/td><td>Letters, numbers, spaces allowed<\/td><\/tr><tr><td>Phone<\/td><td>1<\/td><td>10<\/td><td>Exactly 10 digits, area code first digit 2-9, central office first digit 2-9<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Account lockout&nbsp;is configured to lock the user after&nbsp;10 invalid attempts. Accounts can be unlocked via self-service (Forgot Password journey) or by calling customer care.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Credential Protection Beyond Hashing<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Compromised credential detection:\u00a0The Ping deployment integrates with\u00a0SpyCloud\u00a0to detect compromised credentials and stop logins.<\/li>\n\n\n\n<li>Bot mitigation:\u00a0NuData CAPTCHA\u00a0(transitioning to\u00a0reCAPTCHA) protects login endpoints from fraud and automated abuse.<\/li>\n\n\n\n<li>Passwordless options:\u00a0Both Azure and Ping support passwordless authentication (FIDO2 keys, magic links). Azure AD&#8217;s architecture framework recommends preferring passwordless methods or opting for modern password methods.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Token Security: Signing, Encryption, and Key Management<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Token Signing &amp; Validation<\/h3>\n\n\n\n<p>All tokens&nbsp;must be signed by the Identity Provider:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SAML:\u00a0A digitally signed message with a certified key guarantees message integrity and authentication. The SP uses the IdP&#8217;s public signing certificate (from SAML metadata) to verify the XML signature. Use RSA with at least 2048-bit keys and SHA-256 or better.<\/li>\n\n\n\n<li>OIDC\/OAuth:\u00a0ID and Access tokens are JWTs signed via JWS (e.g., RS256). The OIDC discovery document provides the IdP&#8217;s JWKS (JSON Web Key Set). The JWT header&#8217;s\u00a0<code>kid<\/code>\u00a0claim identifies which key was used.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Token Encryption<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SAML:\u00a0Assertions may be encrypted via XMLEnc to prevent disclosure of sensitive attributes post-transportation. The IdP uses the SP&#8217;s public key from metadata.<\/li>\n\n\n\n<li>OIDC:\u00a0JWT supports JWE (JSON Web Encryption) for ID or access tokens, though in practice ID tokens are usually just signed since their contents flow over TLS. Access tokens can be issued as opaque reference tokens if confidentiality of claims is a concern.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Key Management &amp; Rotation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Protection:\u00a0Store private keys in secure vaults or HSMs. In the Ping AIC implementation, service accounts are created with minimum necessary scopes, and private keys must be downloaded at creation time as they will not be available again. Before regenerating a key, all dependent integrations that use the key to sign JWTs must be identified and updated with the new key.<\/li>\n\n\n\n<li>Rotation:\u00a0Publish new public keys before transitioning. Use the JWT\u00a0<code>kid<\/code>\u00a0header to allow parallel keys. For SAML, metadata can list multiple certificates for overlap during rotation.<\/li>\n\n\n\n<li>Scope Control:\u00a0Ping AIC service accounts use granular scopes:\u00a0<code>fr:am:*<\/code>\u00a0for \/am\/* API endpoints,\u00a0<code>fr:idm:*<\/code>\u00a0for \/openidm\/* and ESV API endpoints,\u00a0<code>fr:idc:esv:*<\/code>\u00a0for ESV API endpoints,\u00a0<code>fr:idc:esv:read<\/code>\u00a0for read-only ESV access,\u00a0<code>fr:idc:esv:update<\/code>\u00a0for create\/update\/delete ESV access, and\u00a0<code>fr:idc:esv:restart<\/code>\u00a0for restarting Identity Cloud services. The principle: always choose the minimum number of scopes needed.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Token Lifetime &amp; Refresh<\/h3>\n\n\n\n<p>Short-lived tokens&nbsp;are a core defense. Short assertion lifetimes help counter stolen assertions and replay attacks. Access tokens typically expire in 1 hour or less. Refresh tokens enable long-lived sessions without re-login but must be stored securely and revocable. If a token&#8217;s validity is questioned, the system should support&nbsp;token introspection&nbsp;(per RFC 7662) where resource servers verify token status with the IdP.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Audit Logging<\/h3>\n\n\n\n<p>PingOne Advanced Identity Cloud provides&nbsp;audit and debug logs&nbsp;to help manage tenants. Audit logs investigate user and system behavior; debug logs investigate production issues. Advanced Identity Cloud stores logs for&nbsp;30 days&nbsp;accessible via the&nbsp;<code>\/monitoring\/logs<\/code>&nbsp;endpoint. Log sources include&nbsp;<code>am-access<\/code>,&nbsp;<code>am-activity<\/code>,&nbsp;<code>am-authentication<\/code>,&nbsp;<code>am-config<\/code>,&nbsp;<code>am-core<\/code>,&nbsp;<code>am-everything<\/code>,&nbsp;<code>idm-access<\/code>,&nbsp;<code>idm-activity<\/code>,&nbsp;<code>idm-authentication<\/code>,&nbsp;<code>idm-config<\/code>,&nbsp;<code>idm-core<\/code>,&nbsp;<code>idm-everything<\/code>,&nbsp;<code>idm-recon<\/code>, and&nbsp;<code>idm-sync<\/code>&nbsp;\u2014 14 sources total. The deployment also integrates logs with&nbsp;Splunk&nbsp;for operational convenience.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7. Securing APIs with OAuth2 Tokens<\/h2>\n\n\n\n<p>When an IdP issues&nbsp;access tokens&nbsp;for API access, those tokens must be properly validated by resource servers. A microservices delivery platform must provide the ability to secure authentication identities and issue secure tokens that can be verified by other system components, support management of claims-based authorization (claims and scopes included in tokens for verification), and provide runtime protection from common attack patterns including OWASP Top Ten for web applications and the OWASP API Security Top Ten.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">OAuth Scopes and Audience<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Scopes:\u00a0Define named permissions for each API (e.g.,\u00a0<code>order.read<\/code>,\u00a0<code>order.write<\/code>). Include granted scopes as claims in access tokens. The API must\u00a0check the token&#8217;s scope claim\u00a0to authorize each request \u2014 principle of least privilege.<\/li>\n\n\n\n<li>Audience Restriction:\u00a0Each access token must be issued for a specific\u00a0audience\u00a0(unique API identifier). APIs must verify the\u00a0<code>aud<\/code>\u00a0claim matches their own identifier. This prevents a\u00a0confused deputy\u00a0problem where a token for one service is replayed to another.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Token Validation at the API Layer<\/h3>\n\n\n\n<p>For each API call, the resource server validates the access token:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>JWT validation:\u00a0Verify signature (using IdP&#8217;s public key from JWKS), check\u00a0<code>aud<\/code>\u00a0matches the service,\u00a0<code>exp<\/code>\u00a0is in the future, and required scopes\/claims are present.<\/li>\n\n\n\n<li>Opaque token validation:\u00a0Call the IdP&#8217;s introspection endpoint to verify active\/inactive status and retrieve associated claims.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Proof-of-Possession &amp; mTLS<\/h3>\n\n\n\n<p>Bearer tokens can be enhanced with&nbsp;mutual TLS (mTLS)&nbsp;\u2014 requiring the client to present a client certificate when calling the API \u2014 or&nbsp;DPoP (Demonstration of Proof-of-Possession)&nbsp;where the token is cryptographically bound to a key held by the client. These techniques ensure a stolen access token alone cannot be used by an attacker. At minimum, prefer&nbsp;short-lived tokens&nbsp;to limit the window of exposure.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">API Gateway Integration<\/h3>\n\n\n\n<p>APIs are often fronted by a gateway or reverse proxy that handles auth checks. The enterprise architecture references Azure API Management with identity and access management, network security baselines, and secure developer portal access as key practices. The delivery platform may integrate with API gateways to validate that only intentional APIs are accessible and provide API discovery capabilities.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8. Internal Architecture Components of an Identity System<\/h2>\n\n\n\n<p>Designing a system comparable to Ping or Azure AD requires several interconnected components:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Core Components<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td>Component<\/td><td>Purpose<\/td><td>Implementation Notes<\/td><\/tr><\/thead><tbody><tr><td>User Directory \/ Identity Store<\/td><td>Stores user identities, credential hashes, profile data, group\/role memberships, and user status<\/td><td>Azure AD is a cloud directory; Ping can integrate with AD, LDAP, or PingDirectory. In a custom solution, use a secured SQL\/NoSQL DB or delegate to enterprise AD via LDAP<\/td><\/tr><tr><td>Authentication Service (IdP Server)<\/td><td>Provides protocol endpoints (\/authorize, \/token, SAML SSO), validates credentials, enforces policies<\/td><td>Must handle authentication journeys (Ping&#8217;s term) or authentication flows. Consider open-source bases like Keycloak rather than coding from scratch<\/td><\/tr><tr><td>Authorization \/ Policy Engine<\/td><td>Makes adaptive decisions (risk-based MFA, conditional access, IP restrictions)<\/td><td>Azure AD&#8217;s Conditional Access; Ping&#8217;s policy rules. Custom IdPs should implement rules based on user attributes, client app, and context<\/td><\/tr><tr><td>Token Service &amp; Key Management<\/td><td>Issues and signs tokens (SAML assertions, JWTs), manages cryptographic keys<\/td><td>Should run in an isolated environment with HSM key storage. Track key rotation and auditing<\/td><\/tr><tr><td>MFA Service<\/td><td>Manages second-factor challenges across methods (OTP, push, FIDO2)<\/td><td>Can be built-in or external. Ping uses PingID\/1Kosmos; Azure has Microsoft Authenticator. Consider TOTP libraries or WebAuthn for custom builds<\/td><\/tr><tr><td>Session \/ Cache Store<\/td><td>Tracks active IdP sessions, used assertion\/code IDs (for replay prevention), partial auth state<\/td><td>Fast store (Redis, in-memory cache) for code lifecycle and session tracking<\/td><\/tr><tr><td>Administration &amp; APIs<\/td><td>Manages client registrations, SP connections, user\/group administration, scopes, logs<\/td><td>Ping AIC provides an admin UI plus REST API management for service accounts and configurations<\/td><\/tr><tr><td>Monitoring &amp; Logging<\/td><td>Audit logs for auth events, token issuance, admin changes; debug logs for production issues<\/td><td>Ping AIC: 30-day log retention via&nbsp;<code>\/monitoring\/logs<\/code>&nbsp;endpoint. Integrate with SIEM (e.g., Splunk)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Security Architecture Pillars<\/h3>\n\n\n\n<p>A reference enterprise security architecture defines five pillars:&nbsp;(1) Platform Security&nbsp;\u2014 securing resources via Azure Key Vault and ACLs;&nbsp;(2) User Authentication Security&nbsp;\u2014 Active Directory integrated security with multi-layer SSO (SAML) and MFA;&nbsp;(3) Role-Based Security&nbsp;\u2014 fine-grain access to specific data based on metadata;&nbsp;(4) Data Transit Security&nbsp;\u2014 encryption at rest and in transit via TLS, server-side encryption, and tokenization;&nbsp;(5) Auditing&nbsp;\u2014 logging and audit strategy for security events and alerts generated based on unauthorized access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">High Availability &amp; Scaling<\/h3>\n\n\n\n<p>Both Ping and Azure run in multi-server, highly available setups (Azure AD is globally distributed; PingFederate can be deployed in clusters). A custom system should eliminate single points of failure through multiple IdP instances, load-balanced, with shared state (database\/Redis) or stateless tokens. SAML XML processing and JWT signing\/verification can be CPU-intensive \u2014 size servers accordingly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">9. Threat Modeling &amp; Mitigations<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Threat Matrix<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td>Threat<\/td><td>Attack Description<\/td><td>Key Mitigations<\/td><\/tr><\/thead><tbody><tr><td>Eavesdropping &amp; MITM<\/td><td>Attacker intercepts passwords or tokens in transit<\/td><td>TLS 1.2+ for all communications; HSTS enforcement; certificate validation; IP filtering where appropriate<\/td><\/tr><tr><td>Phishing<\/td><td>Fake login pages trick users into entering credentials<\/td><td>Domain-specific branded login pages; phishing-resistant MFA (FIDO2\/WebAuthn); MFA number matching to prevent push fatigue; user education<\/td><\/tr><tr><td>Credential Stuffing \/ Password Spray<\/td><td>Automated attempts using stolen or common password lists<\/td><td>Account lockout\/throttling (e.g., 10 attempts); CAPTCHA\/bot detection (reCAPTCHA, NuData); compromised-credential checks (SpyCloud); MFA as a second barrier<\/td><\/tr><tr><td>Replay Attacks<\/td><td>Capturing and reusing valid assertions or auth codes<\/td><td>SAML assertion unique IDs tracked for one-time use; OAuth auth codes are single-use; short token lifetimes; validate&nbsp;<code>InResponseTo<\/code>&nbsp;in SAML; OIDC&nbsp;<code>state<\/code>&nbsp;parameter for CSRF prevention<\/td><\/tr><tr><td>Forged Assertions \/ Token Tampering<\/td><td>Attacker modifies or fabricates tokens<\/td><td>Digital signatures on all tokens; strict signature validation; reject unexpected algorithms; guard against XML Signature Wrapping in SAML<\/td><\/tr><tr><td>CSRF<\/td><td>Tricking user into initiating unwanted login or action<\/td><td>OAuth&nbsp;<code>state<\/code>&nbsp;parameter binding request-to-response; anti-CSRF tokens on all IdP forms; SameSite cookie attributes<\/td><\/tr><tr><td>XSS<\/td><td>Injected script steals session or tokens<\/td><td>HTTPOnly cookies for session tokens; Content Security Policy; output encoding; avoid storing tokens in localStorage (prefer secure HTTPOnly cookies or memory-only storage)<\/td><\/tr><tr><td>Token Theft<\/td><td>Malicious app or malware steals access\/refresh tokens<\/td><td>Short-lived access tokens; secure token storage (HTTPOnly cookies, OS secure storage on mobile); DPoP\/mTLS for proof-of-possession; easy token revocation by admin or user<\/td><\/tr><tr><td>Confused Deputy<\/td><td>Token meant for one API is presented to another<\/td><td>Strict audience (<code>aud<\/code>) validation on all resource servers; per-API audience identifiers<\/td><\/tr><tr><td>MFA Fatigue<\/td><td>Attacker spams push notifications until user approves<\/td><td>Number matching in push notifications; additional context display (app name, location); anomaly detection on repeated MFA prompts<\/td><\/tr><tr><td>Golden SAML<\/td><td>Attacker steals IdP signing key to forge arbitrary assertions<\/td><td>HSM-based key storage; key rotation; monitoring for anomalous assertion patterns; restrict access to IdP infrastructure<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Detailed Mitigations for Critical Threats<\/h3>\n\n\n\n<p>SAML-Specific Security:&nbsp;The OWASP SAML Security Cheat Sheet identifies that the SSO Web Browser Profile is most susceptible to attacks from trusted partners. The AuthnRequest must contain&nbsp;<code>ID<\/code>&nbsp;and&nbsp;<code>SP<\/code>; the Response must contain&nbsp;<code>ID<\/code>,&nbsp;<code>SP<\/code>,&nbsp;<code>IdP<\/code>, and a signed assertion (<code>{AA} K-1\/IdP<\/code>). The request&nbsp;<code>ID<\/code>&nbsp;must be returned in the response (<code>InResponseTo=\"&lt;requestid&gt;\"<\/code>) to guarantee authenticity. Assertions may additionally be encrypted to prevent post-transport disclosure. IP filtering per trusted partner endpoint can help counter stolen assertion (6.4.1) and man-in-the-middle (6.4.2) attacks.<\/p>\n\n\n\n<p>Credential Protection Pipeline:&nbsp;A production-grade implementation integrates multiple defense layers at the login endpoint:&nbsp;SpyCloud&nbsp;nodes for compromised credential detection,&nbsp;1Kosmos MFA APIs&nbsp;for second-factor challenges,&nbsp;NuData\/reCAPTCHA&nbsp;for bot traffic detection, and&nbsp;Akamai&nbsp;edge controls for additional security.<\/p>\n\n\n\n<p>AM Hardening Guidelines:&nbsp;The security control objectives for an IdP platform are to ensure access is authenticated, authorized, and audited. Hardening guidelines include: securing communication with encryption for all data transmissions, protecting network access with HTTPS protocols, turning off services or features not being used, and auditing all events and changes.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10. Implementation Guidance for Custom Applications<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Design Principles<\/h3>\n\n\n\n<p>When building a custom identity system, follow these principles drawn from enterprise practice:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Use standards-based protocols.\u00a0A standards-based architecture delivers modern authentication options and capabilities. Leverage SAML for compatibility with enterprise apps and OIDC\/OAuth for modern applications and APIs. Do not invent new auth protocols.<\/li>\n\n\n\n<li>Centralize authentication.\u00a0Applications should delegate auth to the IdP, which issues signed tokens that apps rely on. This eliminates per-app password silos and concentrates security investment in a single hardened service.<\/li>\n\n\n\n<li>Never roll your own cryptography.\u00a0Use well-vetted libraries for XML signature processing, JWT signing\/verification, password hashing, and TLS configuration. Standard libraries handle edge cases (constant-time comparison, algorithm validation, key management) that are easy to get wrong.<\/li>\n\n\n\n<li>Implement defense in depth.\u00a0Layer multiple protections: TLS in transit, hashed passwords at rest, MFA at login, signed\/encrypted tokens, short lifetimes, scope-limited access, monitoring, and intrusion detection.<\/li>\n\n\n\n<li>Plan for migration and evolution.\u00a0Enterprise migrations (e.g., from SiteMinder to Azure AD or Ping) reveal the importance of standardization \u2014 legacy systems may have multiple incompatible password hashing methods requiring consolidation. Design for future algorithm upgrades from the start.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Comparison: Local Password Auth vs. Federated SSO<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td>Aspect<\/td><td>Local Password Authentication<\/td><td>Federated SSO (SAML\/OIDC)<\/td><\/tr><\/thead><tbody><tr><td>Credential Handling<\/td><td>App stores and verifies password hashes directly<\/td><td>App never sees or stores passwords; IdP handles all credential verification<\/td><\/tr><tr><td>Attack Surface<\/td><td>Each app is a target for credential theft<\/td><td>Credentials concentrated at one hardened IdP; apps have smaller attack surface<\/td><\/tr><tr><td>MFA<\/td><td>Must be implemented per-app<\/td><td>Centralized at IdP; all apps benefit automatically<\/td><\/tr><tr><td>User Experience<\/td><td>Users manage separate credentials per app<\/td><td>Single login grants access to multiple apps<\/td><\/tr><tr><td>Complexity<\/td><td>Simpler for a single app; scales poorly<\/td><td>More initial setup (protocol integration, metadata exchange); scales well across many apps<\/td><\/tr><tr><td>Standards<\/td><td>Custom implementation<\/td><td>SAML, OIDC, OAuth 2.0 \u2014 interoperable with commercial IdPs and SaaS apps<\/td><\/tr><tr><td>Maintenance<\/td><td>Each app must update auth independently<\/td><td>Security improvements (stronger hashing, new MFA methods) deployed centrally once<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Practical Architecture Decisions<\/h3>\n\n\n\n<p>If building from scratch:&nbsp;Consider starting with an open-source identity platform (e.g., Keycloak) that provides SAML and OIDC endpoints, user management, MFA plugins, and admin UI out of the box. Customize it to your needs rather than building every component from scratch. This approach gives you the benefit of community-audited security implementations while maintaining flexibility.<\/p>\n\n\n\n<p>If integrating with existing IdPs:&nbsp;Your application only needs to implement the&nbsp;Relying Party&nbsp;(OIDC) or&nbsp;Service Provider&nbsp;(SAML) side \u2014 validate tokens\/assertions, manage local sessions, and enforce scope-based authorization on APIs. Libraries exist for every major framework (Spring Security, passport.js, MSAL, etc.) that handle protocol mechanics.<\/p>\n\n\n\n<p>Hybrid approach:&nbsp;Many enterprises operate both SAML and OIDC simultaneously through an IdP that brokers both protocols. The challenge is maintaining consistency \u2014 when identity flows through two protocols across different systems, audit trails can fragment and session management becomes complicated. Design for this reality by standardizing user identifiers and session management across both protocols.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Key Standards References<\/h3>\n\n\n\n<p>For implementation, the authoritative sources are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SAML 2.0:\u00a0OASIS Security Assertion Markup Language specification, SAML Profiles section 4.1.4.1 (AuthnRequest) and 4.1.4.2 (Response), SAML Security Considerations (sections 4.2.1, 4.2.2, 4.3)<\/li>\n\n\n\n<li>OAuth 2.0:\u00a0RFC 6749 (framework), RFC 7636 (PKCE), RFC 7662 (introspection), RFC 8252 (native apps)<\/li>\n\n\n\n<li>OIDC:\u00a0OpenID Connect Core 1.0<\/li>\n\n\n\n<li>Password Storage:\u00a0OWASP Password Storage Cheat Sheet; NIST SP 800-63B (Digital Identity Guidelines)<\/li>\n\n\n\n<li>SAML Security:\u00a0OWASP SAML Security Cheat Sheet<\/li>\n\n\n\n<li>API Security:\u00a0OWASP API Security Top Ten<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>In summary, a well-designed identity system centralizes authentication at a trusted IdP, uses standards-based protocols (SAML for enterprise federation, OIDC for modern apps and APIs), protects passwords with memory-hard hashing algorithms and TLS, issues cryptographically signed tokens with appropriate scopes and audiences, integrates MFA as a seamless part of the authentication journey, and defends against a comprehensive threat model spanning phishing, replay, credential stuffing, token theft, and more. By following the patterns established by Ping Identity and Azure Entra ID \u2014 while grounding every design decision in public standards and proven security practices \u2014 a custom-developed solution can achieve comparable security and interoperability.<\/p>\n<div class=\"twttr_buttons\"><div class=\"twttr_twitter\">\n\t\t\t\t\t<a href=\"http:\/\/twitter.com\/share?text=Web+Login+%26amp%3B+Identity+Federation%3A+Deep+Dive+into+SAML%2C+OAuth%2FOIDC%2C+MFA+%26amp%3B+Secure+Design\" class=\"twitter-share-button\" data-via=\"\" data-hashtags=\"\"  data-size=\"default\" data-url=\"https:\/\/shirishranjit.com\/blog1\/architect-principles\/web-login-identity-federation-deep-dive-into-saml-oauth-oidc-mfa-secure-design\"  data-related=\"\" target=\"_blank\">Tweet<\/a>\n\t\t\t\t<\/div><div class=\"twttr_followme\">\n\t\t\t\t\t\t<a href=\"https:\/\/twitter.com\/shiranjit\" class=\"twitter-follow-button\" data-size=\"default\"  data-show-screen-name=\"false\"  target=\"_blank\">Follow me<\/a>\n\t\t\t\t\t<\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>Web Login &amp; Identity Federation: Deep Dive into SAML, OAuth\/OIDC, MFA &amp; Secure Design Modern web applications&nbsp;offload user authentication to a centralized Identity Provider (IdP)&nbsp;such as&nbsp;Ping Identity(PingFederate \/ PingOne Advanced Identity Cloud) or&nbsp;Microsoft Entra ID (Azure AD). This&nbsp;federated login&nbsp;model lets &hellip; <a href=\"https:\/\/shirishranjit.com\/blog1\/architect-principles\/web-login-identity-federation-deep-dive-into-saml-oauth-oidc-mfa-secure-design\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"parent":2688,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2771","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/pages\/2771"}],"collection":[{"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/comments?post=2771"}],"version-history":[{"count":1,"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/pages\/2771\/revisions"}],"predecessor-version":[{"id":2773,"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/pages\/2771\/revisions\/2773"}],"up":[{"embeddable":true,"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/pages\/2688"}],"wp:attachment":[{"href":"https:\/\/shirishranjit.com\/blog1\/wp-json\/wp\/v2\/media?parent=2771"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}