February 26, 2018

If this sounds like something that will save you time on your projects, please do me a favour and check it out

I've been working on a new project called StellarAdmin that helps ASP.NET Core developers like you rapidly create admin screens for your application's Admin and Support users.

OpenIddict is an open source framework for ASP.NET Core which allows you to easily implement an OpenID Connect server. In this blog post series, we will look at how you can implement social login with GitHub in your OpenIddict authorization server and create a simple Angular application which uses the Implicit Flow to authenticate a user.

Please note that even though some of the official OpenIddict samples implement social login, they do so as part of using ASP.NET Identity as the underlying authentication mechanism. This series will demonstrate how you can do this independently of ASP.NET Identity.

Create a new application

Let’s start off with building a basic OpenIddict server. Create a new ASP.NET Core Web Application and ensure that you select the Empty template:

Once the application has been created, add the following NuGet packages to your application which are required by OpenIddict:

dotnet add package AspNet.Security.OAuth.Validation --version 2.0.0-rc2-final dotnet add package OpenIddict --version 2.0.0-rc2-final dotnet add package OpenIddict.Mvc --version 2.0.0-rc2-final dotnet add package OpenIddict.EntityFrameworkCore --version 2.0.0-rc2-final

Add the MVC middleware

The Empty template creates a pretty barren ASP.NET Core application. We will need to configure the MVC middleware as OpenIddict is dependent on that, so go ahead and open the Startup class. You will need to update the ConfigureServices method to register the MVC services, and also update the Configure method to add the MVC middleware. This is what my Startup class looks like at this point:

public class Startup { public Startup ( IConfiguration configuration ) { Configuration = configuration ; } public IConfiguration Configuration { get ; } public void ConfigureServices ( IServiceCollection services ) { services . AddMvc ( ) ; } public void Configure ( IApplicationBuilder app , IHostingEnvironment env ) { if ( env . IsDevelopment ( ) ) { app . UseDeveloperExceptionPage ( ) ; } app . UseMvcWithDefaultRoute ( ) ; } }

Create a database context

OpenIddict will use Entity Framework to store certain information regarding its settings and operations, so create a database context with a constructor as per the sample snippet below:

public class AuthorizationDbContext : DbContext { public AuthorizationDbContext ( DbContextOptions options ) : base ( options ) { } }

Register the EF services for the database context, configuring SQL Server as the underlying database. Also, notice the call to UseOpenIddict() which will add the entities required by OpenIddict to the database context:

public void ConfigureServices ( IServiceCollection services ) { services . AddDbContext < AuthorizationDbContext > ( options => { options . UseSqlServer ( Configuration . GetConnectionString ( "AuthorizationDbContext" ) ) ; options . UseOpenIddict ( ) ; } ) ; services . AddMvcCore ( ) ; }

In the code sample above, the call to UseSqlServer() passed a connection string for the SQL Database, so be sure to add an appsettings.json file with a setting for the connection string specified:

{ "ConnectionStrings" : { "AuthorizationDbContext" : "Server=(localdb)\\MSSQLLocalDB;Database=OIDC_Authorization_Server;Trusted_Connection=True;MultipleActiveResultSets=true" } }

Create the database

We will need to create the actual database for the database context. For that we will first need to create the database migrations:

dotnet ef migrations add CreateOpenIddictModels

And then execute the migrations:

dotnet ef database update

Once that is done you will notice a set of OpenIddict tables added to your database which OpenIddict will use for its operation:

Register the middleware

Now, we will need to add all the service and middleware registrations required by OpenIddict. First, update the ConfigureServices method to register the authentication services. We will configure cookie authentication, as we will eventually store the user’s GitHub authentication information inside a cookie. We also register the OAuth2 validation handler by calling AddOAuthValidation() . This will validate the tokens issued by OpenIddict.

The second part is to register all the OpenIddict services by calling AddOpenIddict() . Note the comments in the code snippet below for further explanations on what each of those lines do. The important bit, for now, is to note that I have configured the OIDC Implicit Flow which since I will implement an Angular application later in this series.

Also, notice that I specified the path to the authorization endpoint as /connect/authorize :

public void ConfigureServices ( IServiceCollection services ) { services . AddDbContext < AuthorizationDbContext > ( options => { } ) ; services . AddMvc ( ) ; services . AddAuthentication ( CookieAuthenticationDefaults . AuthenticationScheme ) . AddCookie ( ) . AddOAuthValidation ( ) ; services . AddOpenIddict ( options => { options . AddEntityFrameworkCoreStores < AuthorizationDbContext > ( ) ; options . AddMvcBinders ( ) ; options . EnableAuthorizationEndpoint ( "/connect/authorize" ) ; options . AllowImplicitFlow ( ) ; options . DisableHttpsRequirement ( ) ; options . AddEphemeralSigningKey ( ) ; } ) ; }

At this point we can run the application and have a look at the OpenId Connect Configuration information document:

Create an authorization controller

The authorization server will not work at this point since we have not implemented the actual authorization endpoint. As noted before, we will eventually authenticate the users with their GitHub account, but for now, to close off this initial blog post, let’s simply return a hard-coded user.

Create a Controllers folder and add an AuthorizationController class. We will create one action called Authorize which will handle that /connect/authorize route which we specified as the authorization endpoint for OpenIddict:

public class AuthorizationController : Controller { [ HttpGet ( "~/connect/authorize" ) ] public async Task < IActionResult > Authorize ( OpenIdConnectRequest request ) { } }

Since we are returning a hard-coded user (or subject in OIDC terms), the code will be very simple. We will create an AuthenticationTicket and return a SignInResult :

public class AuthorizationController : Controller { [ HttpGet ( "~/connect/authorize" ) ] public async Task < IActionResult > Authorize ( OpenIdConnectRequest request ) { var claims = new List < Claim > ( ) ; claims . Add ( new Claim ( OpenIdConnectConstants . Claims . Subject , "user-0001" ) ) ; var identity = new ClaimsIdentity ( claims , "OpenIddict" ) ; var principal = new ClaimsPrincipal ( identity ) ; var ticket = new AuthenticationTicket ( principal , new AuthenticationProperties ( ) , OpenIdConnectServerDefaults . AuthenticationScheme ) ; return SignIn ( ticket . Principal , ticket . Properties , ticket . AuthenticationScheme ) ; } }

Configuring OpenIddict

One final bit to do is to configure a client in OpenIddict, as we will require a client_id when calling the authorization endpoint. For the client we will specify an ID, Name, as well as some redirect URIs.

I just created an InitializeAsync method in my Startup class which will seed the OpenIddict tables with the correct values, and then call this InitializeAsync method from the Configure method:

public class Startup { public void Configure ( IApplicationBuilder app , IHostingEnvironment env ) { if ( env . IsDevelopment ( ) ) { app . UseDeveloperExceptionPage ( ) ; } app . UseAuthentication ( ) ; app . UseMvcWithDefaultRoute ( ) ; InitializeAsync ( app . ApplicationServices , CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ; } private async Task InitializeAsync ( IServiceProvider services , CancellationToken cancellationToken ) { using ( var scope = services . GetRequiredService < IServiceScopeFactory > ( ) . CreateScope ( ) ) { var manager = scope . ServiceProvider . GetRequiredService < OpenIddictApplicationManager < OpenIddictApplication > > ( ) ; if ( await manager . FindByClientIdAsync ( "angular-app" , cancellationToken ) == null ) { var descriptor = new OpenIddictApplicationDescriptor { ClientId = "angular-app" , DisplayName = "Angular Application" , PostLogoutRedirectUris = { new Uri ( "https://oidcdebugger.com/debug" ) } , RedirectUris = { new Uri ( "https://oidcdebugger.com/debug" ) } } ; await manager . CreateAsync ( descriptor , cancellationToken ) ; } } } }

Testing it out

OK, this blog post is getting a bit on the long side, so let’s test this out. Nate Barbettini created an OIDC Debugger website which will come in handy at this point. Head on over to https://oidcdebugger.com/ and specify the settings for your OIDC authorization server:

You may have noticed in the database seeding code from the previous section, that I specified the https://oidcdebugger.com/debug endpoint for my redirect URIs. This is the debugging endpoint for the OIDC Debugger and will allow us to inspect the tokens that were issued.

Click the Send Request button at the bottom of the page:

This will initiate the OIDC authorization flow and head off to our new authorization server. Since we hard coded the user, there is no actual screen at the moment which requires the user to sign in. Instead, our authorization will immediately call back to the redirect URI with the tokens, which can be seen in the screenshot below:

In the next blog post we’ll look at allowing the user to sign in with their GitHub account.

Source code

Source code for this series can be found at https://github.com/jerriepelser-blog/openiddict-social-login