OAuth2 Walkthrough using Identity Server and ASP.NET

Creating an MVC Client using Resource Authorization

Index

Creating an identity server using Aspnet Identity and Entity Framework storage
Extending Identity Server with Identity Manager (pre-release)
Configuring and loading client and scope data with the CLI
Creating an MVC Client using Resource Authorization

Adding and configuring the project

Add an Empty MVC project named OAuth2Demo.MvcClient client to the solution, configuring it for SLL (see here for details). Add the following packages using Nuget Package Manager

get-project OAuth2Demo.MvcClient | install-package Microsoft.Owin.Host.SystemWeb
get-project OAuth2Demo.MvcClient | install-package Microsoft.Owin.Security.Cookies
get-project OAuth2Demo.MvcClient | install-package Microsoft.Owin.Security.OpenIdConnect
get-project OAuth2Demo.MvcClient | install-package Thinktecture.IdentityModel.Owin.ResourceAuthorization.Mvc

Add and configure the OWIN Startup class

Add an OWIN Startup class to the App_Start foler, configuring the OpenId connect options as follows. Using the (default) Implicit flow, IdentityServer verifies the client's identity is by the redirect uri(s)

using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens;

[assembly: OwinStartup(typeof(OAuth2Demo.MvcClient.App_Start.Startup))]

namespace OAuth2Demo.MvcClient.App_Start {
    public class Startup {
        public void Configuration(IAppBuilder app) {

            JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>(); 

            app.UseCookieAuthentication(new CookieAuthenticationOptions {
                AuthenticationType = "Cookies"
            });

            var clientId = (string)ConfigurationManager.AppSettings["oauth2.clientid"];
            var authority = (string)ConfigurationManager.AppSettings["oauth2.authority"];
            var redirectUri = (string)ConfigurationManager.AppSettings["oauth2.redirect"];

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
                Authority = authority,
                ClientId = clientId,
                RedirectUri = redirectUri,
                ResponseType = "id_token token",
                Scope = "openid roles demo-website",

                SignInAsAuthenticationType = "Cookies",
            });

        }
    }
}

with the web.config appSettings

    <add key="oauth2.authority" value="https://localhost:44305/Identity" />
    <add key="oauth2.clientid" value="demo-website" />
    <add key="oauth2.redirect" value="https://localhost:44308/" />

 

Claim Mapping

Assigning the JwtSecurityTokenHandler.InboundClaimTypeMap to a new Dictionary<string, string>() prevents the .net framework from automatically mapping the claims to the System.Security.Claims.ClaimTypes class which produces long names. This re-initialization becomes necesary when using the ResourceAuthorize filter. The difference can be seen below

claim names with default mapping

Claims with the default mapping

claim names with empty mapping

Claims with the newly initialised empty mapping

Adding the ResourceAuthorizationManager

The ResourceAuthorizationManager, which abstracts the security from the business logic, authorises a resource based on values assigned to the context by way of a ResourceAuthorize filter. Typically a resource is an endpoint that you are securing, by providing a name and an action e.g. read, write, etc. These are then compared against the claims for an authenciated user.

Using a simple claims controller as an example, the action is read against the claims resource

using System.Linq;
using System.Security.Claims;
using System.Web.Mvc;
using Thinktecture.IdentityModel.Mvc;

namespace OAuth2Demo.MvcClient.Controllers
{
    public class ClaimsController : BaseController {

        [ResourceAuthorize("read", "claims")]
        public ActionResult Index() {
            return Json(
                ((ClaimsIdentity)User.Identity).Claims.Select(x => new { Type = x.Type, Value = x.Value })
            );
        }
    }
}

For this filter to work, we need to add a ResourceAuthorizationManager

using System.Linq;
using System.Threading.Tasks;
using Thinktecture.IdentityModel.Owin.ResourceAuthorization;

namespace OAuth2Demo.MvcClient.Infrastructure {
    public class AuthorizationManager : ResourceAuthorizationManager {

        public override Task CheckAccessAsync(ResourceAuthorizationContext context) {

            switch (context.Resource.First().Value) {
                case "claims":
                    return AuthorizeClaims(context);
                default:
                    return Nok();
            }
        }

        Task AuthorizeClaims(ResourceAuthorizationContext context) {
            switch (context.Action.First().Value) {
                case "read":
                    return Eval(context.Principal.HasClaim("role", "website-claims"));
                default:
                    return Nok();
            }
        }
    }
}

and finally register it with the application in the startup class (line 22)

using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using OAuth2Demo.MvcClient.Infrastructure;
using Owin;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens;

[assembly: OwinStartup(typeof(OAuth2Demo.MvcClient.App_Start.Startup))]

namespace OAuth2Demo.MvcClient.App_Start {
    public class Startup {
        public void Configuration(IAppBuilder app) {

            JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>(); 

            app.UseCookieAuthentication(new CookieAuthenticationOptions {
                AuthenticationType = "Cookies"
            });
            
            app.UseResourceAuthorization(new AuthorizationManager());

            var clientId = (string)ConfigurationManager.AppSettings["oauth2.clientid"];
            var authority = (string)ConfigurationManager.AppSettings["oauth2.authority"];
            var redirectUri = (string)ConfigurationManager.AppSettings["oauth2.redirect"];

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
                Authority = authority,
                ClientId = clientId,
                RedirectUri = redirectUri,
                ResponseType = "id_token token",
                Scope = "openid roles demo-website",

                SignInAsAuthenticationType = "Cookies",
            });

        }
    }
}

Testing the controller

On starting the solution, and ensuring you have Client, Scopes and User(s) setup in the database, when navigating to the claims view, you should now be redirected to the IdentityServer login screen as below

Upon logging in you'll be presented with the consent screen. Note the demo-websiteresource scope is presenting itself under Application Access

IdentityServer consent view

Upon accepting the permissions, you are now redirected to the secured claims view.

mvc claims view

Authenticated users can view and revoke your permissions on the identity server by navigating to /identity/permissions

Identity Server permissions view

Resources

Getting Started: MVC Authentication & Web APIs
https://github.com/IdentityModel/Thinktecture.IdentityModel/tree/master/samples/OWIN/ResourceAuthorization

Source Code

git clone https://github.com/mindfulsoftware/oauth2Demo.git