Identity Server 4 with .NET core app
Tags: opensource, dotnet, identityserver4, tokenprovider
Published: 2020-07-15 09:19:07

**What is Identity Server?**

Identity Server 4 (IdS4) is an OpenID Connect and OAuth 2.0 framework for .NET core application. It's an authentication service that provides you centralized authentication logic for different types of applications(Web, Mobile, or Services).

**Implement IdS4 in ASP.NET Core Web app**

First, you need to create an empty ASP.NET Core web app using the below command.

```dotnet new web```

Alternatively, you can achieve the same from Visual Studio by choosing the `ASP.NET Core Web Application` project using an empty template.

Now, let's add IdS4 by installing the NuGet package.

```dotnet add package IdentityServer4```

You have successfully installed the IdS4 package, now open your project's `Startup.cs` file and add the below code in `ConfigureServices()` function. Please keep in mind that the below sample code is just referring to an empty list, so you need to make sure that you have a valid list of In-Memory resources and clients in your app.

```
services.AddIdentityServer()
                .AddInMemoryClients(new List<Client>())
                .AddInMemoryApiResources(new List<ApiResource>())
                .AddInMemoryIdentityResources(new List<IdentityResource>())
                .AddInMemoryPersistedGrants()
                .AddTestUsers(new List<TestUser>())
                .AddDeveloperSigningCredential();
```
The above code will include IdS4 dependency and now you need to update `Configure` method with below code snippet.

```
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {            
            app.UseHttpsRedirection();            
            app.UseIdentityServer();
        }
```

Let's understand the configuration, we have added IdS4 dependency using `.AddIdentityServer()` with default signing certificate using `.AddDeveloperSigningCredential()` extension method. Then we have updated `Configure()` method with `.UseIdentityServer()` method here we are actually enabling OpenIdConnect endpoints. Please see below list of endpoints provided by IdS4:

* `connect/token`
* `connect/authorize`
* `connect/userinfo`
* `connect/endsession`
* `connect/revocation`
* `connect/introspect`
* `connect/deviceauthorization`

You can get a list of available endpoints using `/.well-known/openid-configuration` endpoint.

The above list of endpoints are provided by IdS4/OpenIdConnect/OAuth2 framework. In case you need your own endpoint for your business need, yes you can definitely create your custom endpoint!

Follow below steps to add custom endpoint:

* Register custom endpoint:
```
services.AddIdentityServer(options =>
{
 //Adding custom endpoint in the discovery document          
 options.Discovery.ExpandRelativePathsInCustomEntries = true;
 options.Discovery.CustomEntries = new Dictionary<string, object>
             {
                { "myCustomEndpoint", "connect/myCustomEndpoint"}
              };
})
.AddEndpoint<MyCustomEndpoint>("myCustomEndpoint", "connect/myCustomEndpoint");
```

* Implement Handler:

The above code adds a custom endpoint in IdS4's endpoints, now you need to write a handler class for actual implementation.

```
using IdentityServer4.Hosting;

public class MyCustomEndpoint : IEndpointHandler
    {
       public async Task<IEndpointResult> ProcessAsync(HttpContext 
                                                        context)
        {
         // ToDo: Here you can add your custom business-specific 
                  logic
        }
    }
```
**How to use these endpoints?**

You can use these endpoints to get `access/refresh/identity` token from the IdS4 token provider, each endpoint serves a different purpose. e.g. `connect/authorize` endpoint used in a public-facing application where you can use IdS4 login screen for authentication (using implicit [grant type](https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html)). Similarly `connect/token` endpoint provides you access token programmatically (using password grant type).

Request for `Password/ResourceOwner` grant type:

```
POST /connect/token

Headers:
Content-Type: application/x-www-form-urlencoded

Body:
grant_type=password&scope=api1&client_id=testClient&client_secret=testSecret&username=test.user&password=testPassword
```

The above request will provide you accessToken, so now you can use this access token to pass along with your REST API request.

**How to protect your API using IdS4?**

You can protect your existing API or create a new one using `dotnet new webapi` command.

To protect your API you need to install below NuGet package.

```
dotnet add package IdentityServer4.AccessTokenValidation
```

This NuGet provides JWT and Reference token validation middleware, for reference token validation it provides caching as well. To validate your access token you will need to add below code in `ConfigureService` method:

```
services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication("Bearer", options =>
    {
        options.ApiName = "api1";
        options.Authority = "https://localhost:44385";
    });
```
Here `Authority` is the IdentityServer4's URL and `ApiName` is the `Audience` from the access token and API resource name from IdS4 configuration.

To add IdS4 authentication middleware you need to update your `Configure` method with the below code.

```
public void Configure(IApplicationBuilder app)
{
    app.UseAuthentication();
    app.UseAuthorization();
}
```

If you look at the business need Authenticated user isn't always authorized to access all resources. So to allow access to the authorized users you can implement [policy-based authorization](https://www.red-gate.com/simple-talk/dotnet/c-programming/policy-based-authorization-in-asp-net-core-a-deep-dive/). e.g. In case you have decided to authorize a user if the user's accessToken contains specific scope then you can create a simple policy using `.AddPolicy()` method. You need to update the `ConfigureServices` method with the below sample code.

`Startup.cs`
```
services.AddAuthorization(option =>
            {
                option.AddPolicy("MyPolicy", p =>
               {
                   p.RequireAuthenticatedUser();
                   p.RequireClaim(JwtClaimTypes.Scope, 
                      new List<string>
                        {
                         "api1.read",
                         "api1.write:
                        }
                      );
               });
            });
```

Now your API is protected by IdS4 authentication provider, so any endpoint decorated with `[Authorize]` attribute is protected. To call these REST endpoints you need to pass an accessToken using the `Authorization` header.

```
GET /api/v1.0/getuser

Headers:
Authorization: Bearer <AccessToken>
```

Note: We have implemented/integrated IdS4 in .Net project but we haven't introduced the user interface yet, so to add UI for the IdS4 project you can copy-paste code from [QuickStart](https://github.com/IdentityServer/IdentityServer4.Quickstart.UI) folder.

In this blog, I am using In-Memory clients and users, which is definitely not acceptable for a production app so you can use entity framework to update/get clients/users/resources from SQL DB or you can create your [own storage library](https://www.scottbrady91.com/Identity-Server/Creating-Your-Own-IdentityServer4-Storage-Library).

Happy coding!

Picture Source: [Identity Server 4 docs](identityserver4.readthedocs.io/en/latest/intro/terminology.html)