Build the Azure SSO login page in Blazor Server application with SAML

Kevin (Xiaocong) Zheng
7 min readMar 16, 2022

There is two way to use Azure SSO, OpenID, and SAML, in this article we will discuss how to use SAML for SSO login.

What is SSO and How it works?

SSO is short for Single Sign-On. Azure Active Directory Seamless Single Sign-On (Azure AD Seamless SSO) automatically signs users in when they are on their corporate devices connected to your corporate network. When enabled, users don’t need to type in their passwords to sign in to Azure AD, and usually, even type in their usernames. This feature provides your users easy access to your cloud-based applications without needing any additional on-premises components.

The sign-in flow on a web browser is as follows:

  1. The user tries to access a web application (for example, the Outlook Web App — https://outlook.office365.com/owa/) from a domain-joined corporate device inside your corporate network.
  2. If the user is not already signed in, the user is redirected to the Azure AD sign-in page.
  3. The user types in their user name into the Azure AD sign-in page.
  4. Using JavaScript in the background, Azure AD challenges the browser, via a 401 Unauthorized response, to provide a Kerberos ticket.
  5. The browser, in turn, requests a ticket from Active Directory for the AZUREADSSOACC computer account (which represents Azure AD).
  6. Active Directory locates the computer account and returns a Kerberos ticket to the browser encrypted with the computer account’s secret.
  7. The browser forwards the Kerberos ticket it acquired from Active Directory to Azure AD.
  8. Azure AD decrypts the Kerberos ticket, which includes the identity of the user signed into the corporate device, using the previously shared key.
  9. After evaluation, Azure AD either returns a token back to the application or asks the user to perform additional proofs, such as Multi-Factor Authentication.
  10. If the user sign-in is successful, the user is able to access the application.

What is SAML and how does it work?

SAML is an acronym used to describe the Security Assertion Markup Language (SAML).

SAML is an open standard used for authentication. Based upon the Extensible Markup Language (XML) format, web applications use SAML to transfer authentication data between two parties — the identity provider (IdP) and the service provider (SP).

The technology industry created SAML to simplify the authentication process where users needed to access multiple, independent web applications across domains. Prior to SAML, single sign-on (SSO) was achievable but relied on cookies that were only viable within the same domain. It achieves this objective by centralizing user authentication with an identity provider. Web applications can then leverage SAML via the identity provider to grant access to their users. This SAML authentication approach means users do not need to remember multiple usernames and passwords. It also benefits service providers as it increases the security of their own platform, primarily by avoiding the need to store (often weak and insecure) passwords and not having to address forgotten password issues.

How to code on Blazor application to implement the SSO login page

Basically, we need some packages to achieve this goal, there are a lot of packages on the internet we could choose to build the SAML login function, e.g. OneLogin, CompontentSpace. We would like to use CompontentSpace in this article.

First, we need to download the free trial version of CompontentSpace for the .NET core product from its official website(and if you want to use it on the server or for business, please buy their license). It has developer guide documents and also some examples we could follow.

Blazor server application hosts in an asp.net core environment so we could think of it as an asp.net core program. So we must install the ComponentSpace.SAML.2.0 package by using the NuGet manager.

After the installation, we need to inject the service of ComponentSpace into our project. we could do this by adding service and cookies(also setting the cookies’ mode) in the program.cs file( we are using .net 6.0 it may be different if you chose .net 5.0) which is the main entrance of our program, those code looks like this:

builder.Services.Configure<CookiePolicyOptions>(options =>
{
// SameSiteMode.None is required to support SAML SSO.
options.MinimumSameSitePolicy = SameSiteMode.None;
});

builder.Services.ConfigureApplicationCookie(options =>
{
// Use a unique identity cookie name rather than sharing the cookie across applications in the domain.
options.Cookie.Name = “StudentPortal.Identity”;

// SameSiteMode.None is required to support SAML logout.
options.Cookie.SameSite = SameSiteMode.None;
});

// Add SAML SSO services.
builder.Services.AddSaml(builder.Configuration.GetSection(“SAML”));

Then we could modify the configuration file(appsettings.json) to add our CompontentSpace settings. Add those parts and we will add explain of it.

schema part is to download the standard from its official site leave it as default.

LocalServiceProviderConfiguration part is related to our own application because our application provided service to clients so it works as the Service Provided in the SSO’s model. AssertionConsumerServiceUrl is the service that receives the response sent from the Idp, it is defined in the SamlController.cs file we would talk about it later. (if you change the definition you must also change it here, and you need to update the ‘localhost:7193’ part to your actual URL, for other service URLs listed below also follow the same logic). SingleLogoutServiceUrl is the service that logout users from our IDP and it is also defined in the Saml Controller(please also modify the URL to your actual URL). LocalCertificates part is to set where your local certificate files are located and their password.

PartnerIdentityProviderConfigurations part is where we define our IDP's information, we could add many IDPs’ information here. SingleSignOnServiceUrl, SingleLogoutServiceUrl and ArtifactResolutionServiceUrl refers to the IDP server side’s service URL, in many cases, it should be your Azure Cloud’s URL and you must get it from your IT department(Azure AD is the IDP in our cases, it should respond to our SSO’s requests). Those URLs are where our applications send the sign-on and logout requests to. We also need to update them to the actual URLs.

PartnerName: is the IDP's name and URL.

Now we move to the SAML controller part. We have an example in CompontentSpace’s documents. We could follow their examples but we may need to modify some logic. For our case, we don’t use UserManger lib to manage the clients’ status so we remove it from the constructor

And in the AssertionConsumerService function, we need to modify the logic, we need to use Student’s Email or student’s Number to check their permissions and build the claims. So the code looks like this:

public async Task<IActionResult> AssertionConsumerService()
{
// Receive and process the SAML assertion contained in the SAML response.
// The SAML response is received either as part of IdP-initiated or SP-initiated SSO.
var ssoResult = await _samlServiceProvider.ReceiveSsoAsync();

// inject our own userSerive to get user’s information by their email which retrieve form the SSO response
string userName = await _userService.GetUserNameByEmail(ssoResult.UserID);

if (!String.IsNullOrEmpty(userName))
{
//create claims based on student’s username
var claims = _authorizationManager.CreateClaims(userName);
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
//we use HttpContext.SignInAsync function to tell asp.net core this user has logined.
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal);
}

// Redirect to the target URL if specified.
if (!string.IsNullOrEmpty(ssoResult.RelayState))
{
return LocalRedirect(ssoResult.RelayState);
}
//if not redirect to index or home
return LocalRedirect(“~/”);
}

In this code we inject our own userSerive to get users’ information by their email which retrieves from the SSO response, also create claims based on the student’s Username.

The next step is to modify the login form, in this form we could only put a button there to initiate SSO and we don’t need to put the traditional username/password input box there. The makeup code looks like this:

<div id=”loginForm” class=”container” style=”height:50vh”>
<div class=”row” style=”height:15vh”></div>
<div class=”row” style=”text-align:center;margin:auto;top:0;left:0;right:0;bottom:auto;min-width:350px” >
<div class=”col-sm-4">
</div>
<div class=”col-sm-4">
<div class=”row”>
<label style=”font-size:large”>Click to login</label>
</div>
<div class=”row”>
<button type=”button” class=”btn btn-primary btn-lg” @onclick=”InitiateSingleSignOn”>Login</button>
</div>
</div>
<div class=”col-sm-4">
</div>
</div>
</div>

And the response function for on click is listed below. It will call the InitiateSingleSignOn function in the SamlController, which will forward our sign-in request to IDP.

private void InitiateSingleSignOn()
{
NavigationManager.NavigateTo(“SAML/InitiateSingleSignOn”, true);
}

Next is to modify the logout service, we need to modify the logout logics, send the logout request to IDPs.(we use HttpContext instead of SignManager, but they are basically the same function)

public async Task<ActionResult> Logout()
{
//logout user’s status at our own application
await HttpContext.SignOutAsync();
//get idp’s status
var ssoState = await _samlIdentityProvider.GetStatusAsync();

if (await ssoState.CanSloAsync())
{
// Initiate SAML logout.
return RedirectToAction(“InitiateSingleLogout”, “Saml”);
}

return Redirect(“/”);
}

And at last, we need to create a folder for our certificates in our solution.

The whole working process is, when we click our login button, the program will call SAML/InitiateSingleSignOn function, and send an SSO sign-in request to IDP, and now our program automatically run into the SAML/AssertionConsumerService function and start to wait for IDP's reply, after receiving the response from the IDP, we retrieve the student’s email/NO from the reply and use the student’s email/NO to create claims and set permissions.

How to test our application on your local

You need to run CompontentSpace’s example ‘BlazorServerIdentityProvider’ at first and let it works as the IDP (because in most cases we couldn’t connect our dev-laptop to our organization’s Azure SSO). Login to the pop-up page and then run our own application and click the login button to test.

--

--