Skip to main content
Skip table of contents

.NET 8+ OpenID Connect example

.NET 8+ OpenID Connect example 

Last updated: April 2025 
.NET version tested: SDK 9.0.203 / Runtime 9.0.4 

This tutorial takes you through the minimum steps needed to integrate a .NET Web Application with OpenAthens Keystone using OpenID Connect. It is assumed that you have experience of developing applications using ASP.NET in Visual Studio or Visual Studio Code. 

This tutorial is based on the Microsoft Open ID Connect tutorial at https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-oidc-web-authentication?view=aspnetcore-9.0 as it appeared in April 2025. 

Goal in this example 

Authenticate a user and display all the received claims on a page. In the real world you would feed the claims into your authorisation / user-session management process, so that you can grant access only to your customers. 

Instructions 

Create project 

Create a new project using the Razor Web App template. 

In launchSettings.json, remove the “http” profile. We need the app running on https. When you first start the app, accept the prompt to create and trust a self-signed certificate. 

Create connection in OpenAthens 

Create a new OpenID Connect Application record in the OpenAthens Service Provider dashboard, using the https URL from launchSettings.json (but with no trailing /). Copy the same URL (including the port number) to the Redirect URL, this time include the trailing / followed by ‘signin-oidc’. Here is an example of how the two settings should appear (your port number will vary): 

After creating the application, go to the Configuration tab: you will need the Client ID and Client Secret values in the next step. 

Add OIDC-based authentication 

Add the following packages: 

  • Microsoft.AspNetCore.Authentication.Cookies 

  • Microsoft.AspNetCore.Authentication.OpenIdConnect 

Add the following settings block to appSettings.Development.json, substituting values from the OpenAthens Service Provider dashboard: 

CODE
"OpenIDConnectSettings": { 

  "Authority": " https://connect.openathens.net", 

  "ClientId": "{clientId from the OIDC application record created in the publisher dashboard}", 

  "ClientSecret": "{clientSecret from the OIDC application created in the publisher dashboard}" 

} 
 

(You should keep the ClientSecret private. In the real world you would keep it out of source control and load it separately, from a file outside the application root or from an environment variable.) 

In Program.cs, add the following above builder.Build() to add OIDC authentication: 

CODE
builder.Services.AddAuthentication(options => 

    { 

        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; 

        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; 

    }) 

    .AddCookie() 

    .AddOpenIdConnect(options => 

    { 

        var oidcConfig = builder.Configuration.GetSection("OpenIDConnectSettings"); 

 

        options.Authority = oidcConfig["Authority"]; 

        options.ClientId = oidcConfig["ClientId"]; 

        options.ClientSecret = oidcConfig["ClientSecret"]; 

 

        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; 

        options.ResponseType = OpenIdConnectResponseType.Code; 

        options.SaveTokens = true; 

        options.GetClaimsFromUserInfoEndpoint = true; 

        options.ClaimActions.MapAll(); 

    }); 
 

Then add app.UseAuthentication(); between app.UseRouting(); and app.UseAuthorization(); 

CODE
app.UseRouting(); 

app.UseAuthentication(); 

app.UseAuthorization(); 
 

Add login and logout 

In _Layout.cshtml, add a right-hand navigation block with login and logout links: 

CODE
<ul class="navbar-nav navbar-right"> 

    @if (Context.User.Identity!.IsAuthenticated) 

    { 

        <li class="nav-item"> 

            <a class="nav-link text-dark" asp-area="" asp-page="/Logout">Logout</a> 

        </li> 

    } 

    else 

    { 

        <li class="nav-item"> 

            <a class="nav-link text-dark" asp-area="" asp-page="/Login">Login</a> 

        </li> 

    } 

</ul> 
 

Add the login and logout pages: 

 

Login.cshtml: 

CODE
@page 

@model LoginModel 
 

Login.cshtml.cs: 

CODE
[AllowAnonymous] 

public class LoginModel : PageModel 

{ 

    [BindProperty(SupportsGet = true)] 

    public string? ReturnUrl { get; set; } 

 

    public async Task OnGetAsync() 

    { 

        var properties = GetAuthProperties(ReturnUrl); 

        await HttpContext.ChallengeAsync(properties); 

    } 

 

    private static AuthenticationProperties GetAuthProperties(string? returnUrl) 

    { 

        const string pathBase = "/"; 

 

        // Prevent open redirects. 

        if (string.IsNullOrEmpty(returnUrl)) 

        { 

            returnUrl = pathBase; 

        } 

        else if (!Uri.IsWellFormedUriString(returnUrl, UriKind.Relative)) 

        { 

            returnUrl = new Uri(returnUrl, UriKind.Absolute).PathAndQuery; 

        } 

        else if (returnUrl[0] != '/') 

        { 

            returnUrl = $"{pathBase}{returnUrl}"; 

        } 

 

        return new AuthenticationProperties { RedirectUri = returnUrl }; 

    } 

} 
 

Logout.cshtml: 

CODE
@page 

@model LogoutModel 
 

Logout.cshtml.cs: 

CODE
[Authorize] 

public class LogoutModel : PageModel 

{ 

    public IActionResult OnGetAsync() 

    { 

        return SignOut(new AuthenticationProperties 

        { 

            RedirectUri = "/" 

        }, 

        // Clear auth cookie 

        CookieAuthenticationDefaults.AuthenticationScheme); 

    } 

} 
 

Run the site, and check that Login takes you to OpenAthens, where you can log in with a personal account (create one in the Admin area https://admin.openathens.net/ if you haven't already done so). 

Add a protected page that displays user claims 

Claims.cshtml: 

CODE
@page 

@model ClaimsModel 

@{ 

    ViewData["Title"] = "User Claims"; 

} 

 

<h1>Claims associated with current User</h1> 

 

<table class="table"> 

    <thead> 

        <tr> 

            <th>Claim</th> 

            <th>Value</th> 

        </tr> 

    </thead> 

    <tbody> 

        @foreach (var claim in User.Claims) 

        { 

            <tr> 

                <td>@claim.Type</td> 

                <td>@claim.Value</td> 

            </tr> 

        } 

    </tbody> 

</table> 
 

Claims.cshtml.cs: 

CODE
[Authorize] 

public class ClaimsModel : PageModel 

{ 

} 
 

In _Layout.cshtml, add a Claims link to the authenticated template before the Logout link: 

CODE
@if (Context.User.Identity!.IsAuthenticated) 

{ 

    <li class="nav-item"> 

        <a class="nav-link text-dark" asp-area="" asp-page="/Claims">Claims</a> 

    </li> 

    <li class="nav-item"> 

        <a class="nav-link text-dark" asp-area="" asp-page="/Logout">Logout</a> 

    </li> 

} 
 

Re-run the application, log out and in again, and access the claims page. You should see a list of claims. 

The claims you will be most interested in are: 

  • urn:oid:1.3.6.1.4.1.5923.1.1.1.9, also known as scopedAffiliation. Use this to decide whether the allow the user into your application, based on your customer database. 

  • urn:oid:1.3.6.1.4.1.5923.1.1.1.10, also known as targetedID. This is a persistent, pseudonymous identifier for the user. Use this as the key for storing user personalisation. 

In the real world you would use the scopedAffiliation to decide whether to grant access to protected content. 

Be aware that claims can be multi-valued and may be returned as either a string or an array, depending on the number of values available for the user. For example, urn:oid:1.3.6.1.4.1.5923.1.1.1.9 could be returned in either of these forms: 

  • "member@<domain>" 

  • ["member@<domain>", "student@<domain>"] 

You will need to allow for this when parsing the claims. 

For more information about the attributes available from OpenAthens, and how to map them to OpenID Connect claims see:

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.