Skip to main content
Skip table of contents

.NET Framework 4.5+ WebForms OpenID Connect example

The standard library for OpenID Connect published by the Microsoft for .NET Framework 4 is not compatible with OpenAthens Keystone. OpenAthens has published an updated library that makes it possible to connect to OpenAthens Keystone in .NET 4.5 or later. Earlier versions are not supported.

It is assumed that user has knowledge of developing applications using ASP.NET in Visual Studio. This example is based on the ASP.NET WebForms Web Application template.

A completed sample web application created using these instructions is available at https://github.com/openathens/keystone-dotnet-45-webforms-sample.

Goal in this example

Authenticate a user and display all the received claims on a page. In the real world you would read the claims and feed them into your authorisation / user-session management process.

Instructions

Create project

Create a new project in Visual Studio, selecting the template “ASP.NET Web Application (.NET  Framework)” - select framework version 4.5 or higher. At the second stage, select the Web Forms template option. Don’t select any authentication options at this stage.

Configure the website to run on https by default:

  1. In the solution explorer, select the project folder, then in the properties pane, change SSL Enabled to true. Copy the SSL URL that is created.
  2. Right click the project and select properties, and in the Web tab, paste the https copied above into the Project Url field in place of the existing http URL.

Create a new Application record in the OpenAthens Service Provider dashboard, using the URL you copied in step 1 as the Application URL (with no trailing /). Copy the same URL (including the port number) to the Redirect URL, this time include the trailing /.

Add the Owin and OpenID Connect dependencies

Use NuGet package manager to add the following packages:

CODE
Microsoft.AspNet.Identity.Owin
Microsoft.Owin.Host.SystemWeb
OpenAthens.Owin.Security.OpenIdConnect

Configuration

Add the following appSettings to Web.config:

CODE
   <add key="oidc:Authority" value="https://connect.openathens.net" />
   <add key="oidc:ClientId" value="{clientId from the OIDC application record created in the publisher dashboard}" />
   <add key="oidc:ClientSecret" value="{clientSecret from the OIDC application created in the publisher dashboard}" />
   <add key="oidc:RedirectUrl" value="{redirectUrl set in the publisher dashboard}" />


For added security to avoid accidentally checking secure strings into source control, you could place these settings in a separate config file outside the project root and include a reference to, e.g.:

CODE
 <appSettings file="..\..\EnvironmentSettings.config">
   <add … />
 </appSetting>


Add a Startup class that initialises the OpenID Connect authentication via an Owin assembly binding. Following the convention used in other samples from Microsoft, this is a partial class split across two files. Firstly, Startup.cs in the project root:

CODE
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(WebApplication1.Startup))]
namespace WebApplication1
{
   public partial class Startup
   {
       public void Configuration(IAppBuilder app)
       {
           ConfigureAuth(app);
       }
   }
}


and secondly, Startup.Auth.cs in the App_Start folder. (you’ll need to remove “.App_Start” from the namespace in the created file so it matches the namespace in Startup.cs):

CODE
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using OpenAthens.Owin.Security.OpenIdConnect;
using Owin;
using System.Configuration;

namespace WebApplication1
{
   public partial class Startup
   {
       public static string OidcAuthority = ConfigurationManager.AppSettings["oidc:Authority"];
       public static string OidcRedirectUrl = ConfigurationManager.AppSettings["oidc:RedirectUrl"];
       public static string OidcClientId = ConfigurationManager.AppSettings["oidc:ClientId"];
       public static string OidcClientSecret = ConfigurationManager.AppSettings["oidc:ClientSecret"];

       public void ConfigureAuth(IAppBuilder app)
       {
           app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
           app.UseCookieAuthentication(new CookieAuthenticationOptions());

           var oidcOptions = new OpenIdConnectAuthenticationOptions
           {
               Authority = OidcAuthority,
               ClientId = OidcClientId,
               ClientSecret = OidcClientSecret,
               GetClaimsFromUserInfoEndpoint = true,
               PostLogoutRedirectUri = OidcRedirectUrl,
               RedirectUri = OidcRedirectUrl,
               ResponseType = OpenIdConnectResponseType.Code,
               Scope = OpenIdConnectScope.OpenId
           };

           app.UseOpenIdConnectAuthentication(oidcOptions);
       }
   }
}

Configure global security settings

If your .NET runtime version is less than 4.6.1, you'll need to add a line to Application_Start in Global.asax.cs to enable TLS 1.2:

CODE
using System.IdentityModel.Claims;
using System.Web.Helpers;
...
        void Application_Start(object sender, EventArgs e)
        {
        ...
		    ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
        }
...

Add login and logout

Add login and logout links by adding the following code block immediately after the existing <ul>...</ul> navigation links in Site.Master:

CODE
                <asp:LoginView runat="server" ViewStateMode="Disabled">
                    <AnonymousTemplate>
                        <ul class="nav navbar-nav navbar-right">
                            <li>
                                <a runat="server"
                                    href="Site.Master"
                                    onserverclick="login_Click">Login</a>
                            </li>
                        </ul>
                    </AnonymousTemplate>
                    <LoggedInTemplate>
                        <ul class="nav navbar-nav navbar-right">
                            <li>
                                <asp:LoginStatus runat="server"
                                    LogoutAction="Redirect"
                                    LogoutText="Logout"
                                    LogoutPageUrl="~/"
                                    OnLoggingOut="Unnamed_LoggingOut" />
                            </li>
                        </ul>
                    </LoggedInTemplate>
                </asp:LoginView>


and the following code behind in Site.Master.cs:

CODE
using Microsoft.Owin.Security.Cookies;
using System;
...
        protected void login_Click(object sender, EventArgs e)
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.Current.GetOwinContext().Authentication.Challenge();
            }
        }

        protected void Unnamed_LoggingOut(object sender, EventArgs e)
        {
            if (Request.IsAuthenticated)
            {
                Context.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
            }
        }
...


Login and logout should now be working. 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). Logout should work as well.

Add Claims page

The last step is to add a protected page that shows the OpenID Connect claims for the logged in user. Add a new page called Claims with the following content:

CODE
<%@ Page Title="Claims" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Claims.aspx.cs" Inherits="KeystoneDotNet45WebFormsSample.Claims" %>
<%@ Import Namespace="System.Security.Claims" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h2>OpenID Connect Claims</h2>
    <dl>
        <asp:DataList runat="server" ID="dlClaims">
            <ItemTemplate>
                <dt><%# ((Claim) Container.DataItem).Type %></dt>
                <dd><%# ((Claim) Container.DataItem).Value %></dd>
            </ItemTemplate>
        </asp:DataList>
    </dl>
</asp:Content>


and the following in its code behind file:

CODE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace KeystoneDotNet45WebFormsSample
{
    public partial class Claims : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.Current.GetOwinContext().Authentication.Challenge();
            }

            var claims = ClaimsPrincipal.Current.Claims;
            dlClaims.DataSource = claims;
            dlClaims.DataBind();
        }
    }
}


Add a link to the Claims page inside the LoggedInTemplate you added to Site.Master earlier:

CODE
                                <li>
                                    <a runat="server" href="~/Claims">View claims</a>
                                </li>


Now you can log in and view the claims.

Example of accessing specific claims

The claim you will be most interested in is eduPersonScopedAffiliation, which can be accessed as follows:

CODE
var personScopedAffiliation = (User.Identity as ClaimsIdentity)?.Claims
    .FirstOrDefault(c => c.Type == "eduPersonScopedAffiliation")?.Value;

In a real world application you would access this in a master page and use it to decide whether to grant access to protected content in pages based on that master.


JavaScript errors detected

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

If this problem persists, please contact our support.