This quickstart will walk you through setting up IdP-Initiated Single Sign On (SSO) using the Rock Solid Knowledge SAML component. We will use the SAML component with IdentityServer acting as an IdP to create a page that displays a list of compatible Service Providers that a user can select to log into.
This quickstart assumes that you already have a working IdentityServer configured with SAML and Service Providers. For this example, we will use one of our sample Service Providers. However, any Service Provider that accepts IdP-Initiated SSO responses will work.
Please note that this SSO flow is insecure and should be avoided if possible. We have an article that goes into why this flow is unsecure.
Configuring Service Providers to accept IdP-Initiated SSO
You will need to deliberately enable the IdP-initiated SSO feature for each Service Provider. For each Service Provider you want to use IdP-Initiated SSO with, you will need to set AllowIdpInitiatedSso
on the ServiceProvider
object to true
. Otherwise, our SAML component will not send IdP-Initiated SSO responses to the Service Provider.
Here we have an example IdentityServer Client and Saml Service Provider.
Client rskSamlClient = new Client
{
ClientId = "http://localhost:5001/saml",
ClientName = "RSK SAML Client",
ProtocolType = IdentityServerConstants.ProtocolTypes.Saml2p,
RedirectUris = { "http://localhost:5001/saml" },
AllowedScopes = { "openid", "profile" }
};
ServiceProvider rskSamlSp = new ServiceProvider
{
EntityId = "http://localhost:5001/saml",
AllowIdpInitiatedSso = true, // This must be set to true
AssertionConsumerServices = { new Service(SamlConstants.BindingTypes.HttpRedirect, "http://localhost:5001/signin-saml") },
SingleLogoutServices = { new Service(SamlConstants.BindingTypes.HttpRedirect, "http://localhost:5001/signout-saml") }
};
Implementing IdP Initiated SSO on your IdentityServer
The ISamlInteractionService
contains the methods we will use to implement IdP-Initiated SSO in our IdentityServer.
- The
GetIdpInitiatedSsoCompatibleServiceProviders
method will return a list of all Service Providers that support IdP-Initiated SSO. - The
CreateIdpInitiatedSsoResponse
method will generate the Single Sign-On Response for a Service Provider. - The
ExecuteIdpInitiatedSso
method will send the SSO Response to a Service Provider.
We can inject the ISamlInteractionService
into our controller and use these methods to create a page for IdP-Initiated log on. First, let's create a page to display all available Service Providers, in this example we're doing this in the Home Controller.
[HttpGet]
public async Task<IActionResult> IdpInitiatedSso()
{
var serviceProviders = await samlInteractionService.GetIdpInitiatedSsoCompatibleServiceProviders();
var vm = new IdpInitiatedSsoViewModel(serviceProviders);
return View("IdpInitiatedSso", vm);
}
Here we call the ISamlInteractionService
to retrieve all compatible Service Providers and pass them to a View Model for displaying in the View.
public class IdpInitiatedSsoViewModel
{
public List<ServiceProvider> IdpInitiatedSsoEnabledProviders;
public string ServiceProviderId { get; set; }
public IdpInitiatedSsoViewModel(IEnumerable<ServiceProvider> serviceProviders)
{
IdpInitiatedSsoEnabledProviders = new List<ServiceProvider>(serviceProviders);
}
}
Here's the View Model. It holds both a list of Service Providers available and which Service Provider the user selected.
Next, we will need the View itself.
@model IdentityServer4.Saml.Tests.Host.Quickstart.Home.IdpInitiatedSsoViewModel
@{
ViewData["Title"] = "IdpInitiatedSso";
}
<h2>IdpInitiatedSso</h2>
@foreach(var sp in Model.IdpInitiatedSsoEnabledProviders)
{
<form method="post" asp-action="ExecuteSpSso">
<label asp-for="ServiceProviderId">@sp.EntityId</label>
<input asp-for="ServiceProviderId" value="@sp.EntityId" hidden/>
<button type="submit">Log on</button>
</form>
}
This View creates a form for each Service Provider and posts the ID of the selected Service Provider back to the Controller.
Now we can see a list of available and compatible Service Providers. And call a post method to log the user onto the selected Service Provider.
In the Home controller, we will create this post method for executing the SSO request.
[HttpPost]
public async Task ExecuteSpSso(string serviceProviderId)
{
var ssoResponse = await samlInteractionService.CreateIdpInitiatedSsoResponse(serviceProviderId);
await samlInteractionService.ExecuteIdpInitiatedSso(HttpContext, ssoResponse);
}
The ISamlInteractionService.CreateIdpInitiatedSsoResponse()
method takes in a ServiceProvider EntityId string
, and optionally, a AssertionConsumerServiceUrl URI
, SAML ProtocolBinding string
and relayState string
. The method will create and return GeneratedMessage
for the correct binding type, which is our IdP-Initiated Sso Response.
If no AssertionConsumerServiceUrl or ProtocolBinding is given, the SAML component will use the first Assertion Consumer Service (ACS) Endpoint found on the Service Provider, or the one marked as isDefault
. If either the AssertionConsumerServiceUrl or Protocol Binding is given, it will try to find a match for a configured ACS endpoint. If no match is found a AssertionConsumerServiceNotFoundException
will be thrown.
Then we can execute the request with ExecuteIdpInitiatedSso()
.
You will now be able to log into your Service Providers from your IdentityServer.