Single Sign-On (SSO) is a simple idea that can quickly cause developers not-so-simple headaches. At a high level, it’s very straightforward: SSO is what allows you to sign into Gmail and switch over to Google Calendar or YouTube without typing your password in again. It’s so common that you probably use it every day without thinking – and so do your users.
Like most “simple” things in software engineering, making single sign-on work on the backend can be painful. Stormpath already makes SSO between your applications easy with our ID Site solution, and we recently rolled out support for another flavor of single sign-on: SAML.
What is SAML?
SAML (Security Assertion Markup Language) is a standard for exchanging authentication and authorization information between parties. As far as standards go, it’s a relatively old one – it was ratified over a decade ago. As a result, it’s been adopted by a large number of identity providers, such as Salesforce, Okta, OneLogin, and Ping Identity (to name a few).
SAML allows you to sign into a site with your credentials from one of these providers. If you’ve ever used your Salesforce credentials to log in somewhere that wasn’t Salesfoce, for example, you’ve used SAML.
Service Providers vs. Identity Providers
The terminology of SAML can be a little confusing at first glance. There are two main entities:
Easy SAML SSO for Your .NET Applications
A downside of SAML being an old standard is that it relies on paradigms from the early-2000s, namely every developer’s favorite way of tearing out their hair expressing data: XML. This is one of the reasons why many teams find it difficult to quickly implement SAML.
Never fear! The SAML support in the Stormpath .NET SDK can significantly decrease the time (and pain) it takes to implement SAML login in your .NET application.
If you’re using an older version of the SDK, be sure to update to release 0.5 or greater. That release contains all the SAML goodies.
Then, follow the SAML configuration guide to set up a Stormpath Directory with SAML credentials, and configure your identity provider on the other side.
Time for some code! You’ll need a SAML login controller which constructs a special URL and redirects the user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class LoginController : Controller { public async Task<ActionResult> Index() { var app = await stormpathClient.GetApplicationAsync("myApplicationHref"); // Your Stormpath Application href var samlUrlBuilder = await app.NewSamlIdpUrlBuilderAsync(); var redirectUrl = samlUrlBuilder .SetCallbackUri("myCallbackUrl") // The URL to your callback controller, see below .Build(); HttpContext.Response.Headers.Add("Cache-control", "no-cache, no-store"); HttpContext.Response.Headers.Add("Pragma", "no-cache"); HttpContext.Response.Headers.Add("Expires", "-1"); return Redirect(redirectUrl); } } |
After the user authenticates with the external identity provider, Stormpath will redirect the browser back to the callback URL that you defined. (Make sure this URL is in your Application’s authorized callback URI list!)
The callback controller will need to handle the incoming assertion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class LoginRedirectController : Controller { public async Task<ActionResult> Index() { var app = await stormpathClient.GetApplicationAsync("myApplicationHref"); // Your Stormpath Application href var incomingRequest = HttpRequests.NewRequestDescriptor() .WithMethod(Request.HttpMethod) .WithUri(Request.Url) .Build(); var samlHandler = app.NewSamlAsyncCallbackHandler(incomingRequest); try { var accountResult = await samlHandler.GetAccountResultAsync(); var account = await accountResult.GetAccountAsync(); // Success! Do something with the account details } catch (InvalidJwtException ije) { // JWT validation failed (see Message property for details) } catch (SamlException se) { // SAML exchange failed (see Message property for details) } return RedirectToAction("Index", "Home"); } } |
That’s all there is to it!