OpenID Connect from ASP.NET Core - on Mac OSX

Niels Flensted-Jensen, Feb 25, 2017

Sometimes you do stuff becuase you can. This is sort of one of those accounts. But hopefully one that will gain relevance as .NET Core and ASP.NET Core moves to the mainstream. And to make it more fun we will do it all on Mac OSX. Open house, no Windows!

Here we will authenticate your users with Norwegian or Swedish BankID or Danish NemID using ASP.NET Core on Mac OSX. By leveraging OpenID Connect, connecting ASP.NET Core applications to national identity services via easyID is a trivial job, as shown in this post.

To immediately get a running version of the code from this post get the sample from GitHub

The steps shown here creates a site from scratch, but the instructions may as well be applied to an existing application.

Three steps are involved in getting going from zero:

  1. Get set up for .NET Core and ASP.NET Core on your Mac
  2. Modify your ASP.NET Core application to defer authentication to easyID.

and to make it all work with the BankIDs you need to

  1. Set up an account with easyID to enable OpenID Connect on top of the national identities.

1. Get ready for ASP.NET Core on Mac OSX

First off, you must install a version of .NET Core with support for the MS Build project format. Not the project.json format which is being abondoned. This took me some time, and with limited time on my hands a few of those “whatever works” moments.

So at the time of writing this meant I had to go here to get a working .NET Core downlaod that would work with the ASP.NET Core Yeoman template. But that will change once things fall into place and all the ducks line up.

Once you have the right version of .NET you want to install Yeoman and the Yeoman ASP.NET Core generator. Assuming you already have NPM installed, this is simple:

# You may have to sudo these global installs
npm install -g yo
npm install -g generator-aspnet

and then, to generate the fully functional ASP.NET site, run Yeoman and answer the questions asked along the way.

# Generate your starting point
yo aspnet

Chose the Web Application Basic [without ...] application type: Yeoman ASP.NET Core

Once the site is in place go into the application directory go through the .NET restore, build, and run motions:

dotnet restore
dotnet build

# launch site on port 5000
dotnet run

That’s it.

You can now watch your new ASP.NET Core site on your Mac by going to localhost:5000.

2. Modify you ASP.NET Core app for OpenID Connect

So you’re up an running at you see something like this:

Basic ASP.NET Core site

That’s lovely, on a Mac and all. But no authentication, so let’s add some plumbing to sign in users with Swedish BankID.

Next up is adding the references to the JWT and OpenID stuff. Open the generated .csproj file and add the three last lines, the three package references:

<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp1.0</TargetFramework>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore" Version="1.0.3" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.0.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.0.1" />
  <!-- Next 3 packages for OpenID Connect authentication -->
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.0" />
  </ItemGroup>
</Project>

This will add the new references, but you still need to retrieve the referenced packages:

# Pull down the new packages
dotnet restore
dotnet build

Now we have the packages and infrastructure components we need.

Next we will add the initialization code to the Startup class (in Startup.cs):

// Import the relevant namespaces at the top of the file
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

In the Configuration method add these lines after the app.UseStaticFiles(); line.

app.UseCookieAuthentication(new CookieAuthenticationOptions {
    AuthenticationScheme = "Cookies",
    AutomaticAuthenticate = true
});

var options = new OpenIdConnectOptions() {
    AuthenticationScheme = "oidc", // callback will be on /signin-oidc
    SignInScheme = "Cookies",
    ResponseType = "code",
    Authority = YOUR_DOMAIN, // For testing: "https://acme-corp.grean.id"
    ClientId = YOUR_CLIENT_ID, // For testing: "urn:easyid:aspnet-core-demo" 
    ClientSecret = YOUR_CLIENT_SECRET // For testing: "0m4bGC+LO7QSBk7zf4d2Uhhlq48IRHbUC/D5yM4EROU="
};

// This may be modified to get the choice of authentication method from
// some other source, e.g. a dropdown in the UI
// Not needed for most OIDC identity proivders, such as Google, etc.
options.Events = new OpenIdConnectEvents() {
    OnRedirectToIdentityProvider = context => {
        context.ProtocolMessage.AcrValues = "urn:grn:authn:se:bankid:same-device";
        return Task.FromResult(0);
    }
};

// Wire in OIDC middelware
app.UseOpenIdConnectAuthentication(options);

The options object sets the OpenID Connect middelware behaviour.

Specifically the AuthorizationScheme property determines the callback you must register with your OpenID Connect identity provider (see the section at the end on how to do that for easyID). In this case we set it to oidc which means the callback will be on /signin-oidc. Still, this is handled by the middelware so no need for any additional code.

For easyID, the Authority, ClientID, and ClientSecret properties reference your domain, client ID and client secret. If you’re in a hurry to try this out, you may go with the values provided in the comments next to the properties.

Also note the AcrValues (authentication context reference) parameter in the OnRedirectToIdentityProvider event handler. This is needed for easyID which supports several different identity services on the same endpoint. If you use a simple OIDC identity provider e.g. Google, you will not need this event handler.

For easyID the AcrValues identifies the specific kind of authentication you choose. Your options at the time of writing include:

  • Norwegian BankID:
    • Mobile: urn:grn:authn:no:bankid:mobile
    • Hardware token (kodebrikke): urn:grn:authn:no:bankid:central
  • Swedish BankID:
    • Same device: urn:grn:authn:se:bankid:same-device
    • Another device (aka mobile): urn:grn:authn:se:bankid:another-device
  • Danish NemID:
    • Personal with code card: urn:grn:authn:dk:nemid:poces
    • Employee with code card: urn:grn:authn:dk:nemid:moces
    • Employee with code file: urn:grn:authn:dk:nemid:moces:codefile

We are now ready to authenticate. Before trying it out we just need a protected resource that will display user information.

The protected view: Start the login and show the user info

First add a login link to the front page. Put a login link in the menu, or something to the same effect, in the _Layout.cshtml shared view where the top menu is rendered:

@if (Context.User.Identity.IsAuthenticated) 
{
    <li><a asp-area="" asp-controller="Home" asp-action="Logout">Logout</a></li>                        
}
else 
{
    <li><a asp-area="" asp-controller="Home" asp-action="Protected">Login</a></li>                        
}

To implement the Protected view which will kick off the authentication process, add a new action to the HomeController. Notice the Authorize attribute which will start the OIDC flow.

// The Authorize attribute requires the user to be authenticated and will
// kick off the OIDC authentication flow 
[Microsoft.AspNetCore.Authorization.Authorize]
public IActionResult Protected()
{
    return View();
}
    
public async Task<IActionResult> Logout()
{
    await HttpContext.Authentication.SignOutAsync("Cookies");
    return View("Index");
}

Now, adding a simple view, Protected.cshtml, in the Views/Home to display the claims, and we are set.

@{
    ViewData["Title"] = "ASP.NET Core + easyID";
}
<h2>Welcome @User.Claims.Where( c => c.Type == "name").FirstOrDefault().Value</h2>
 
<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

Running the application

To execute an a login flow, remember to set the Authorization, the ClientID, and the ClientSecret. If you haven’t already set up an easyID account, go to last section to do that. Or simply run with the test values given in the comments in the code snippet further up.

Now all that remains is to run the site:

# Build if you have already done so
dotnet build

# ... and fire up your ASP.NET core web site
dotnet run

Now navigate to http://localhost:5000 and click the Login menu at the top.

That’s it! Or if you didn’t actually go through the steps, you may view, download, and run this sample from GitHub

3. Setting up your easyID account

As the various national and bank identity services do not support OpenID Connect or any other standard protocol, we use Grean easyID as the identity service in the middle.

So, if you haven’t done so already, sign up for easyID: grean.com/easyid

A note on test users

In order to test the use of Norwegian BankID, Swedish BankID, and Danish NemID you need test identities.

For the sample shown above, we’ve set it up to use Swedish BankID (on the same device as your browser). As shown earlier you may easily switch to one of the other supported identities.

To obtain a test identity for Swedish BankID, go to https://demo.bankid.com and create the test¸identities you need. (Note that to install a test identity on your phone you will have to follow the guidelines on the site).

Also, please let us know if you need help getting your own test identities, and we will help you set them up. Just go to grean.com/easyid and sign up. Once signed up you can join our Slack channel from the easyID dashboard.

Setting up your easyID account

Name your first domain which will be a subdomain of grean.id. This will be the domain name we will use in the following.

Sign up

Once signed up, go to the Applications tab to register your application.

Note that if you use Auth0 as your identity broker your life just got a little bit easier and you should take a look at our screencast showing how that scenario works.

But this post is not about that, so for this exercise just go for the manual option.

Create application

Remember to copy the Client ID value - you will need it when configuring the OpenID Connect middleware. For the Callback URLs just enter the URL where your application may be reached for notification of the authenticated user. In our example that would be http://localhost:5000/signin-oidc

Once the application has been registered in easyID, you have one more step to get it set up for OpenID Connect. Open the easyID application registration again and switch on the OAuth2 Code flow. This will generate a client secret that you will need in your application. Be sure to copy the value or you will have to generate a new one. (As with any other password we will, of course, only store the hashed value)

Turn on OIDC

With this you will be able to set up your application to authenticate using any of the enabled identity services: Danish NemID, Swedish BankID and/or Norwegian BankID.


Go to criipto.com to sign up today to quickly enable your applications to accept real people’s real identities. We run a tight ship to ensure that you get the same level of security and safety that you get from the various proprietary bank and government solutions!