Integrate IdentityServer with ASP.NET Core (Part 1 - Server Configuration)

IdentityServer is an
implementation of OpenID Connect and OAuth2 specifications that ASP.NET Core
developers can use to secure their applications and APIs. Beginners often find
it bit tricky to setup and configure the applications to use this popular
framework. To that end this article series attempts to provide a quick tour of
setup and configuration required to put IdentityServer to use. I am not going to
explain the basic OAuth2 and OpenID Connect terminology here. A lot of good
resources are available on the internet explaining those basics. You can also go
through the
IdentityServer documentation to familiarize yourself with the framework.
In this multipart article you will develop three Visual Studio projects
namely IdentityServerDemo.Server, IdentityServerDemo.WebApi, and
IdentityServerDemo.Client. The following figure shows these projects upon
completion.

The IdentityServerDemo.Server project contains the authorization server. It
is the component that issues security tokens and deals with user authentication,
access control, and user profile. The IdentityServerDemo.WebApi project contains
a Web API - Employees Web API. This Web API is protected using JWT based
authentication and also uses role based security. The Employees Web API can be
called directly using HttpClient or a JavaScript client or it can be consumed by
an MVC web application protected using IdentityServer. The
IdentityServerDemo.Client is the MVC application
that obtains the security token and forwards it to the Employees Web API. So, you
will learn to protect Web APIs as well as MVC web applications using IdentityServer.
In this part of the series you will develop the IdentityServerDemo.Server
project. You will revisit this project in the next parts also but this part adds
many of the configuration settings to the IdentityServer.
Let's get started.
Begin by creating a new ASP.NET Core web application named
IdentityServerDemo.Server using Visual Studio. You can either pick MVC template
or Empty template while creating the project. Here, I am using Empty project
template.

Once the project is created, switch set the web server from IIS Express to
Kestrel development server. You can do that using toolbar as shown below:

You can keep using IIS Express if you want but switching to Kestrel helps us
tweak with the port number easily. Moreover, you get to see the console logs and
messages easily.
Then open project's properties page, go to the Debug | Web Server settings
section and note down the URL carefully. You need this URL during configuring
IdentityServer related features.

Next, go to the GitHub repository of IdentityServer's QuickStart UI
here and download the whole content (QuickStart, Views, and wwwroot folders)
on your local machine. Add these folders to your IdentityServerDemo.Server
project folder. Upon adding these folders, your Solution Explorer should show
these folders and files. These files contain the default UI elements such as
login page that you can easily reuse while developing your application.

Then add the IdentityServer4 NuGet package in your project.

Now we will add various pieces of IdentityServer configuration. These pieces
can come from an in-memory store or they can come from some persistent store
such as SQL Server database. For now we will use in-memory configuration.
Add a new class in the IdentityServerDemo.Server project called
ServerConfiguration. The following code shows the skeleton of
ServerConfiguration class.
public static class ServerConfiguration
{
public static List<IdentityResource> IdentityResources {...}
public static List<ApiScope> ApiScopes {...}
public static List<ApiResource> ApiResources {...}
public static List<Client> Clients {...}
public static List<TestUser> TestUsers {...}
}
As you can see, ServerConfiguration contains five static properties namely
IdentityResources, ApiResources, ApiScopes, Clients, and TestUsers. All these
properties return a List containing the respective objects. For example,
IdentityResources property returns a List of IdentityResource objects. The
classes such as IdentityResource, ApiResource, ApiScope, Client, and TestUser
are provided by IdentityServer.
Now that you know the overall structure of ServerConfiguration, let's add
code to these properties one-by-one.
The IdentityResources property is shown below:
public static List<IdentityResource> IdentityResources
{
get
{
List<IdentityResource> idResources =
new List<IdentityResource>();
idResources.Add(new IdentityResources.OpenId());
idResources.Add(new IdentityResources.Profile());
idResources.Add(new IdentityResources.Email());
idResources.Add(new IdentityResources.Phone());
idResources.Add(new IdentityResources.Address());
idResources.Add(new IdentityResource("roles",
"User roles", new List<string> { "role" }));
return idResources;
}
}
Identity resources are pieces of information about a user such as user's Open
ID (subject id or sub), profile (given_name and family_name), email, phone,
address, and roles. Here I am adding all these IdentityResource objects just for
the sake of testing. You should add only those that you really need in the
application. Notice how IdentityResource objects are obtained using methods such
as OpenId(), Profile(), and Email(). We are going to use role based security for
our Web API and MVC application. So, we also added roles resource.
The ApiScopes property is shown below:
public static List<ApiScope> ApiScopes
{
get
{
List<ApiScope> apiScopes =
new List<ApiScope>();
apiScopes.Add(new ApiScope
("employeesWebApi", "Employees Web API"));
return apiScopes;
}
}
A scope indicates the limit of access that can be granted to an application.
An application can request one or more scopes. If a user grants access to these
scopes, the application receives a security token that can be used to access
only these scopes. Here, we create a single ApiResource called employeesWebApi.
The second parameter to the constructor indicates the display name of the scope
on the user consent page (you will see that page later).
The ApiResources property is shown below:
public static List<ApiResource> ApiResources
{
get
{
ApiResource apiResource1 = new
ApiResource("employeesWebApiResource",
"Employees Web API")
{
Scopes = { "employeesWebApi" },
UserClaims = { "role",
"given_name",
"family_name",
"email",
"phone",
"address"
}
};
List<ApiResource> apiResources = new
List<ApiResource>();
apiResources.Add(apiResource1);
return apiResources;
}
}
An API resource indicates the Web API you want to protect. The ApiResources
property creates a single ApiResource called employeesWebApiResource. Scopes and
UserClaims properties of the ApiResource being created are also set to
employeesWebApi scope created earlier and a set of identity pieces such as role,
given_name, and family_name.
The Clients property is shown below:
public static List<Client> Clients
{
get
{
Client client1 = new Client
{
ClientId = "client1",
ClientName = "Client 1",
ClientSecrets = new[] {
new Secret("client1_secret_code".Sha512()) },
AllowedGrantTypes = GrantTypes.
ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = {
"openid",
"roles",
"employeesWebApi"
}
};
List<Client> clients = new List<Client>();
clients.Add(client1);
return clients;
}
}
We create new Client object and set properties such as ClientId, ClientName,
ClientSecrets, AllowedGrantTypes, and AllowedScopes. This client is used to talk
directly to the Employees Web API and so the GrantType is set to
PasswordOwnerAndClientCredentials. In order to get a security token we will
supply ClientId, ClientSecret, UserName, and Password from the client code. The
AllowedScopes property indicates the scopes allowed for this client - openid,
roles, and employeesWebApi. The client secret is SHA512 hash value of the secret
code (client1_secret_code in this case).
We will add another client in the above code when we develop MVC web
application in the later parts of this article series.
The TestUsers property is shown below:
public static List<TestUser> TestUsers
{
get
{
TestUser usr1 = new TestUser()
{
SubjectId = "2f47f8f0-bea1-4f0e-ade1-88533a0eaf57",
Username = "user1",
Password = "password1",
Claims = new List<Claim>
{
new Claim("given_name", "firstName1"),
new Claim("family_name", "lastName1"),
new Claim("address", "USA"),
new Claim("email","user1@localhost"),
new Claim("phone", "123"),
new Claim("role", "Admin")
}
};
TestUser usr2 = new TestUser()
{
SubjectId = "5747df40-1bff-49ee-aadf-905bacb39a3a",
Username = "user2",
Password = "password2",
Claims = new List<Claim>
{
new Claim("given_name", "firstName2"),
new Claim("family_name", "lastName2"),
new Claim("address", "UK"),
new Claim("email","user2@localhost"),
new Claim("phone", "456"),
new Claim("role", "Operator")
}
};
List<TestUser> testUsers = new List<TestUser>();
testUsers.Add(usr1);
testUsers.Add(usr2);
return testUsers;
}
}
The TestUsers property creates two TestUser objects. A TestUser represents a
user account that you can use during development for testing purposes. Each user
has SubjectId, Username, Password, and a list of Claims. The SubjectId is a
GUID. The Username and Password are user's login credentials. The Claims list is
a set of claims belonging to that user. You can retrieve these claims in your
Web API or MVC application if required. Here, we add given_name, family_name,
address, email, phone, and role claims. Especially the role claim is important
for our example because we will be implementing role based security in the
Employees Web API and the MVC web application. The user1 belongs to Admin role
and user2 belongs to Operator role.
This completes the ServerConfiguration class. Now we can use it in the
Startup class. Let's go ahead and do just that.
Open the Startup.cs file and add the following code to its ConfigureServices()
method.
public void ConfigureServices
(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddIdentityServer()
.AddInMemoryIdentityResources
(ServerConfiguration.IdentityResources)
.AddInMemoryApiResources
(ServerConfiguration.ApiResources)
.AddInMemoryApiScopes
(ServerConfiguration.ApiScopes)
.AddInMemoryClients
(ServerConfiguration.Clients)
.AddTestUsers
(ServerConfiguration.TestUsers)
.AddDeveloperSigningCredential();
}
First, we register MVC related types with the DI container using
AddControllersWithViews() method. Then we register IdentityServer related types
using AddIdentityServer() method. Further, a series of methods starting from
AddInMemotyApiResources() and ending with AddTestUsers() add the in-memory
configuration for IdentityResources, ApiResources, ApiScopes, Clients, and
TestUsers. Finally, AddDeveloperSigningCredential() method adds a development
time digital signature to the configuration. In a more realistic case you will
use AddSigningCredential() method to specify a custom signature.
Now go to the Configure() method and add the following code.
public void Configure(IApplicationBuilder app,
IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}
/{action=Index}/{id?}");
});
}
Notice the code in bold letters. It wires the IdentityServer middleware into
the request pipeline.
Build and run the IdentityServerDemo.Server project. If all goes well, you
will see this in the browser:

In the next part of this series we will develop the Employees Web API
project.
That's it for now! Keep coding!!