PingFederate + SAML in .NET Framework and .NET Core

How I worked through a stubborn SSO setup in an older .NET codebase and got the flow to behave.

What Usually Goes Wrong

How The Flow Works

In this flow, your app is the Service Provider (SP) and PingFederate is the Identity Provider (IdP). The app sends the user to PingFederate for sign-in, receives a SAML response at Assertion Consumer Service (ACS), validates the response, and then creates a local auth session.

What I Actually Faced

I started in an older .NET Framework codebase, and the first problem was not SAML itself. It was the framework version and the lack of a clean path to wire SSO without turning the code into a mess. OAuth examples were easy to find; SAML on my stack was not.

After that change, the integration became something I could actually maintain and troubleshoot without guessing.

Code I Added to Make It Maintainable

The key was to keep the SAML plumbing away from controllers and keep the claims mapping predictable.

public sealed class SamlOptions
{
    public string EntityId { get; set; } = "";
    public string AcsUrl { get; set; } = "";
    public string IdpMetadataUrl { get; set; } = "";
}
public static class ClaimMap
{
    public static ClaimsIdentity Map(ClaimsIdentity identity)
    {
        var email = identity.FindFirst(ClaimTypes.Email)?.Value ?? "";
        var name = identity.FindFirst(ClaimTypes.Name)?.Value ?? "";
        identity.AddClaim(new Claim("app_email", email));
        identity.AddClaim(new Claim("app_name", name));
        return identity;
    }
}
[HttpPost]
[AllowAnonymous]
public IActionResult Acs()
{
    // Validate the SAML response with the middleware, then create the local auth cookie.
    // The app should only continue after the claim set has been normalized.
    return RedirectToAction("Index", "Home");
}

Simple Analogy

Think of the app as a gated office building. PingFederate is the receptionist at the front desk. The user shows ID, the receptionist checks it, and then hands back a stamped visitor badge. The app only lets the user in after it sees that badge and matches it to the right person.

Data Panel
Reference table for quick scanning and comparison.
Reference Live Status
SAML PartPlain-English Meaning
PingFederateThe front-desk identity checker.
SP / AppThe building that wants to trust the check.
ACS URLThe door where the stamped pass is returned.
SAML ResponseThe stamped visitor badge with proof of identity.
Local auth cookieThe wristband the app gives after the visitor is accepted.

How The Flow Feels To A User

Diagram 1: Standard SAML Web SSO Flow

User Browser .NET App (SP) PingFederate (IdP) 1) Request resource 2) AuthnRequest 3) SAML Response 4) App session cookie App validates Signature, Audience, Recipient, Expiry, and InResponseTo

Checklist Before Coding

Data Panel
Reference table for quick scanning and comparison.
Reference Live Status
ItemMust Match
Entity IDSP entity ID in app and PingFederate connection config
ACS URLExact scheme/host/path, including trailing slash behavior
BindingPOST/Redirect expectations on both sides
Signing CertificateCurrent + next cert if rollover configured
NameID / ClaimsMapped attributes needed by application authorization

MVC Mapping: SAML Config to App Flow

Teams often understand SAML settings better when they map each item to a concrete app touchpoint. Use this as a debugging bridge between PingFederate config and MVC behavior.

Data Panel
Reference table for quick scanning and comparison.
Reference Live Status
SAML ItemMVC/Code TouchpointWhat to Verify
Entity ID Startup/auth middleware configuration SP entity identifier in app settings exactly matches PingFederate SP connection setting.
ACS URL Assertion callback endpoint handling (auth callback route) Protocol/host/path/trailing slash align with external URL seen by user (especially behind reverse proxy).
Binding (POST/Redirect) Auth challenge and callback handling flow App and PingFederate both expect the same binding mode for AuthnRequest/Response.
Signing Certificate Token validation configuration Current cert is trusted and next rollover cert is preloaded to avoid sudden outage.
NameID / Claims Claims transformation + authorization policy checks Mapped claims are present under canonical names used by controller/policy logic.
// Practical MVC flow anchors (conceptual)
// 1) Login endpoint -> triggers SAML challenge
// 2) ACS/callback endpoint -> validates SAML response
// 3) Claims mapping step -> canonical internal claims
// 4) Authorization checks -> controllers/policies use canonical claims only

Approach A: .NET Framework (OWIN + SAML Middleware)

Where it fits: legacy enterprise portals, MVC 5, WebForms wrappers, or apps not yet migrated to .NET Core.

  • Configure cookie middleware first, then SAML middleware.
  • Set SP Entity ID, ACS endpoint, and IdP metadata carefully.
  • Enable cert validation with rollover plan (current + next cert).
  • Persist session securely, especially across load-balanced nodes.

Typical failure pattern: redirect loop due to cookie path/domain mismatch or callback URL mismatch.

Approach B: .NET Core (ASP.NET Core Auth Schemes)

Where it fits: modern APIs + web apps behind reverse proxy and cloud load balancers.

  • Set default auth scheme and challenge scheme explicitly.
  • Configure forwarded headers so external host/scheme are preserved.
  • Use robust SameSite/cookie settings for SSO redirects.
  • Validate ACS callback against external URL, not internal pod/host URL.

Typical failure pattern: works locally, fails in prod because proxy headers are not trusted.

Real Scenario: SSO Works in Dev but Fails After Deployment

Users click the app, get redirected to PingFederate, and authenticate successfully. The browser comes back to the app, but the deployed environment keeps looping back to login or returns an ACS error.

Data Panel
Reference table for quick scanning and comparison.
Reference Live Status
SymptomWhat It Usually MeansWhat I Check First
Login loopCookie or callback mismatchForwarded headers, redirect URI, SameSite cookie settings
ACS returns 400/401SAML response not acceptedEntity ID, ACS URL, signature cert, audience
Works on one server onlyProxy or sticky-session differenceLoad balancer, host header, app pool identity

The fix is usually not in the login page. It is in the trust chain between browser, proxy, ACS endpoint, and claims mapping.

Diagram 2: .NET Framework vs .NET Core Deployment Sensitivity

.NET Framework Track Browser IIS + OWIN App Sensitive to cookie middleware order and callback URL configuration. .NET Core Track Browser Reverse Proxy + Kestrel Sensitive to forwarded headers, external URL generation, SameSite.

.NET Framework Notes

// Typical OWIN order (simplified)
app.UseCookieAuthentication(cookieOptions);
app.UseSaml2Authentication(samlOptions);

MVC Login and Logout Flow (Practical)

Login Flow (SP-initiated)

  1. User opens a protected MVC route (for example /orders).
  2. MVC auth middleware challenges unauthenticated request to SAML scheme.
  3. App sends AuthnRequest to PingFederate.
  4. User signs in on PingFederate and returns to MVC ACS endpoint.
  5. MVC validates SAML response and issues local auth cookie.
  6. User is redirected back to original return URL.

Logout Flow (SP-initiated)

  1. User clicks logout in MVC app.
  2. App clears local auth cookie/session.
  3. App optionally sends SLO request to PingFederate (if configured).
  4. PingFederate closes IdP session and returns to post-logout URL.
// MVC intent (simplified)
// Login challenge
return new ChallengeResult("Saml2", new AuthenticationProperties { RedirectUri = returnUrl });

// Logout
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("Saml2");

.NET Core Notes

// Example intent (simplified)
services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "Saml2";
});

Diagram 3: Troubleshooting Decision Tree

Login redirects loop? Check cookies + callback URL Invalid signature? Check cert + metadata rollover Audience mismatch? Check Entity ID + ACS + Recipient If all above pass, check: - Clock skew / NTP sync - Proxy forwarded headers - Environment-specific callback URLs - SAML attribute mapping gaps

High-Value Troubleshooting Sequence

  1. Capture browser trace: redirect chain and final ACS POST.
  2. Verify SAML response signature and certificate thumbprint.
  3. Check Audience, Recipient, and InResponseTo.
  4. Validate server clock sync (NTP) and acceptable clock skew.
  5. Check relay state and return URL validation logic.

Common SSO Errors and Meaning

Data Panel
Reference table for quick scanning and comparison.
Reference Live Status
Error PatternLikely Cause
Invalid signatureWrong/expired cert, wrong metadata, cert rollover mismatch
No audience matchEntity ID mismatch between PingFederate and app config
Message expired / not yet validClock skew or timezone/NTP issue
Looping login redirectsCookie/domain/samesite/proxy-forwarded headers issue
Works in lower env, fails in prodURL, cert, proxy headers, or host mapping drift

Common MVC Issue Scenarios (and Fix Direction)

Data Panel
Reference table for quick scanning and comparison.
Reference Live Status
ScenarioWhat You SeeLikely CauseFix Direction
Login redirect loop User returns from IdP but gets challenged again Cookie not persisted, wrong domain/path, callback URL mismatch Validate cookie options, callback/ACS URL, and proxy forwarded headers
ReturnUrl lost after login User lands on home instead of original MVC route Missing/blocked relay state, custom redirect logic override Preserve return URL safely and validate relay-state handling
Logout still appears logged in User logs out of app, but next navigation auto-logs in IdP session still active or local cookie not fully cleared Implement full signout for both local cookie and SAML/IdP session
Works in local, fails in prod Auth fails only behind load balancer Incorrect external scheme/host due to missing forwarded headers Enable forwarded headers and verify externally visible callback URLs
Intermittent auth failures Random token validity errors Clock skew / NTP drift across app nodes Sync server clocks and tune allowed skew conservatively

Identity vs UserIdentity, Token Size, and Cookie Chunking

In many real projects, teams unintentionally duplicate claims across different identity objects (for example, base identity + user identity) or include multiple token payloads carrying similar data. This inflates auth cookie size and introduces random sign-in failures.

What Happens in Practice

Symptoms

Where Token Chunking Helps

Cookie chunking splits large auth cookies into multiple smaller cookies so they can be transported within browser/server limits. It helps transport, but it does not solve root cause claim bloat.

Recommended Pattern

  1. Map only essential claims from SAML response.
  2. Normalize roles/groups into compact app-specific permissions.
  3. Avoid duplicating same values in multiple identities/claim types.
  4. Enable chunking if needed, then monitor cookie count/size in browser dev tools.
// Conceptual goal
// 1) keep essential claims only
// 2) enable cookie manager/chunking in auth middleware when required
// 3) validate total response header size across proxy + app stack

Same User Claim, Different Names (Claim Alias Conflicts)

Another common issue is receiving the same user attribute under different claim names. For example, one environment sends email, another sends mail, and a third sends http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress. The value is the same, but app logic can break if it expects only one key.

Typical Conflicting Aliases

Why It Causes Problems

Recommended Fix Pattern

  1. Create a canonical internal claim model (single names your app uses).
  2. Map external aliases to canonical claims in one centralized transformation step.
  3. Drop redundant aliases after mapping to avoid ambiguity.
  4. Use canonical claims only in authorization policies and business logic.
// Conceptual mapping example
// external aliases -> canonical
// mail/email/emailaddress -> app_email
// sub/nameidentifier/uid -> app_user_id
// role/groups -> app_roles

End-to-End Token and Session Lifecycle (Client -> Server -> PingFederate)

This is the practical flow teams should align on so everyone understands where authentication state lives and how it moves.

1) User Authenticates at PingFederate

2) Server Creates Local Auth Session

3) Client Stores and Sends Session State

4) Server Validates Cookie on Every Request

5) Where It Commonly Breaks

6) How PingFederate-Centric Governance Solves It

// Practical ownership model
// PingFederate: identity proof + canonical attribute release
// App middleware: strict validation + claim normalization
// Browser: transport of session cookie only
// App authorization: depends on canonical internal claims

Delivery Hardening Tips

© 2026 Anup Kumar Chandrakumaran