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
This series aims to provide a practical walk through of a production ready setup of IdentityServer 3 and different .net clients (mvc, webApi and SPA's). It does not cover the OAuth2 or OpenID Connect specifications, so some prior know of these is assumed. The walk-through leverages a number of samples provided by Thinktecture, building out a solution that can be easily migrated to production
Thinktecture already have many walk-throughs and samples, the aim here is not to reproduce these, but see how they all tie together. For a detailed explanation, these samples are a great resource
Getting Started: Creating the simplest OAuth2 Authorization Server, Client and API
Getting Started: MVC Authentication & Web APIs
There's a lot going on with OAuth and OpenId Connect, IdentityServer combines the 2 specifications into a single server application, allowing a client to authenticate using the OpenId endpoints (for certain flows), and obtain authorisation for a specific resource. Before getting started, here's some common terminology when working with Identity Server.
In OAuth terms, the Resouce Owner can be thought of as the end user.
The clients are the consumers of the identity and authorisation endpoints. Theses are typical browser based or native applications and are categorised into public or confidential clients
Public clients incapable of maintaining the confidentiality of their credentials (e.g., clients executing on the device used by the resource owner, such as an installed native application or a web browser-based application), and incapable of secure client authentication via any other means
Confidential Clients capable of maintaining the confidentiality of their credentials (e.g., client implemented on a secure server with restricted access to the client credentials), or capable of secure client authentication using other means.
The flows (or grant types) a the different authorisation mechanisms for the given client. Care must be taken to ensure the correct flow is selected for the given client type, public or confidential. Care must be taken when selecting a flow type for a client, selecting flow type which is not intended for a client type can expose your client by being insecure
The table below surmises the different flow types available
Flow Type | Client Type | Identity Tokens | Refresh Tokens |
---|---|---|---|
Authorisation Code flow | confidential / public 1 | no | yes |
Implict flow | public | yes | no |
Resource Owner Password Credentials flow | confidential | no | yes |
Hybrid flow | public | yes | yes |
1 Optimised for confidential clients
Lets get our hands dirty, when dealing with any security based applications SSL should always be used. Setting up the project...
install-package Microsoft.Owin.Host.SystemWeb
install-package IdentityServer3
public class Startup { public void Configuration(IAppBuilder app) { app.Map("/identity", id => { id.UseIdentityServer(new IdentityServerOptions { SiteName = "Demo Identity Server", SigningCertificate = LoadCertificate() // Factory = TODO }); }); } X509Certificate2 LoadCertificate() { //Test certificate sourced from https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Certificates return new X509Certificate2( string.Format(@"{0}\bin\{1}", AppDomain.CurrentDomain.BaseDirectory, ConfigurationManager.AppSettings["signing-certificate.name"]), (string)ConfigurationManager.AppSettings["signing-certificate.password"]); } }This code injects the Identity Server application, configuring
<system.webServer> <modules runAllManagedModulesForAllRequests="true" /> </system.webServer>
The site will not start yet as we have to configure the IdentityServerServiceFactory, for this we are going to use the Aspnet Identity model and Entity Framework as the data acess
Using Nuget Package Manger, install the following packages
install-package Microsoft.AspNet.Identity.EntityFramework install-package IdentityServer3.AspNetIdentity install-package IdentityServer3.EntityFramework
Add a Entities.cs file to the solution, pasting in the following code
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; namespace OAuth2Demo.IdentityServer { public class Context : IdentityDbContext<IdentityUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>{ public Context(string connectionString) : base(connectionString) { } } public class UserStore : UserStoret<IdentityUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> { public UserStore(Context context) : base(context) { } } public class RoleStore : RoleStoret<IdentityRole> { public RoleStore(Context context) : base(context) { } } public class UserManager : UserManagert<IdentityUser, string> { public UserManager(UserStore userStore) : base(userStore) { } } public class RoleManager : RoleManagert<IdentityRole> { public RoleManager(RoleStore roleStore) : base(roleStore) { } } }
This code cofigures
Using the default Entity Framework Identity models. These models can be extended as required, as the whole ASP.NET Identity framework is extendable
Create a Services folder, adding the class IdentityUserService.cs. Extend the default aspnet identity service as per below (this is required for the dependency injection to work)
using IdentityServer3.AspNetIdentity; using Microsoft.AspNet.Identity.EntityFramework; namespace OAuth2Demo.IdentityServer.Services { public class IdentityUserService : AspNetIdentityUserService<IdentityUser, string> { public IdentityUserService(UserManager userManager) : base(userManager) { } } }
Add an Extensions folder to the project, adding the class IdentityServerServiceFactoryExtensions.cs. Add the extension method below
using IdentityServer3.Core.Services; using IdentityServer3.EntityFramework; using OAuth2Demo.IdentityServer; using OAuth2Demo.IdentityServer.Services; namespace IdentityServer3.Core.Configuration { public static class IdentityServerServiceFactoryExtensions { public static IdentityServerServiceFactory Configure(this IdentityServerServiceFactory factory, string connectionString) { var serviceOptions = new EntityFrameworkServiceOptions { ConnectionString = connectionString }; factory.RegisterOperationalServices(serviceOptions); factory.RegisterConfigurationServices(serviceOptions); factory.Register(new Registration<Context>(resolver => new Context(connectionString))); factory.Register(new Registration<UserStore>()); factory.Register(new Registration<UserManager>()); factory.UserService = new Registration<IUserService, IdentityUserService>(); return factory; } } }
These extension methods register the stores and the mangers with the dependency injection container and wire up the services. The methods RegisterOperationalServices and RegisterConfigurationServices. These 2 methods register the stores required for operational (AuthorizationCodeStore, TokenHandleStore, ConsentStore, RefreshTokenStore) and configuration (ClientStore, ScopeStore) services
public void Configuration(IAppBuilder app) { string connectionString = ConfigurationManager.ConnectionStrings["cnn"].ConnectionString; app.Map("/identity", id => { id.UseIdentityServer(new IdentityServerOptions { SiteName = "Demo Identity Server", SigningCertificate = LoadCertificate() Factory = new IdentityServerServiceFactory().Configure(connectionString), }); }); }
Compile and run the project, navigating to /Identity and you should be presented with the screen below..
git clone https://github.com/mindfulsoftware/oauth2Demo.git