In this tutorial we are going to implement Duende IdentityServer in ASP.NET Core app with ASP.NET Core Identity to provide authentication and authorization features. So users will be able to perform login and logout on their Identity account which will be secured with Duende IdentityServer.
Page Contents
Here we will be creating 2 apps one is Server app and other is Client app.
The Server app called DuendeIS is an ASP.NET Core app, here we have installed the Duende IdentityServer and ASP.NET Core Identity packages that are needed for performing Authentication and Authorization tasks. These packages are:
These packages are shown in the below image:
For providing ASP.NET Core Identity support we have added Login, Logout, Register and other needed razor view files inside the Areas > Identity folder. We have shown this in the below image.
The Identity database connection string is defined in the appsettings.json file. The identity database used is MSSQLLocalDB type, this you can change as your will.
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=DISIdentity;Trusted_Connection=True;MultipleActiveResultSets=true"
}
The DB Context file called ApplicationDbContext.cs is located inside the “Models” folder and is defined as shown below.
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Finally, on the Program.cs class we have registed Identity Services as shown below.
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddIdentity<IdentityUser, IdentityRole>().AddDefaultUI().AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
Duende IdentityServer integration is added to the Program.cs and is shown below.
builder.Services.AddIdentityServer()
.AddInMemoryClients(new Client[] {
new Client
{
ClientId = "duendeclient",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = { "https://localhost:7028/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:7028/signout-callback-oidc" },
FrontChannelLogoutUri = "https://localhost:7028/signout-oidc",
AllowedScopes = { "openid", "profile", "email", "phone" }
}
})
.AddInMemoryIdentityResources(new IdentityResource[] {
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResources.Phone(),
})
.AddAspNetIdentity<IdentityUser>();
app.UseIdentityServer();
Clients represent applications that can request JWT Tokens from your identityserver. We have defined an in-memory client with:
.AddInMemoryClients(new Client[] {
new Client
{
ClientId = "duendeclient",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = { "https://localhost:7028/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:7028/signout-callback-oidc" },
FrontChannelLogoutUri = "https://localhost:7028/signout-oidc",
AllowedScopes = { "openid", "profile", "email", "phone" }
}
})
The above code include:
We also added Identity resources to the memory.
.AddInMemoryIdentityResources(new IdentityResource[] {
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResources.Phone(),
})
With this our Server app is complete. So now when a client tries to access secured resources, it is redirected to the Server app where the client has to perform login procedure with ASP.NET Core Identity. On successful login, the IdentityServer provides a JWT token to the client and the client is redireced back to the secured resource URL where it can now access it.
This process is explained in the below image.
Let’s run Entity Framework Core migration commands to create the Identity database. So run the below commands one by one.
add-migration Migration1
Update-Database
After this run the app and click the Register link given on the top menu. Here create a new Identity user with email – “[email protected]” and password – “Admin@123”. After the user is created click the “Logout” link and close the app. This user will be used for demonstrating the working of the client later on.
The below image shows we are creating this user.
The client app called DuendeISClient, is a simple ASP.NET Core MVC app, that contains secured areas. These area are protected by Duende Identity Server.
Inside the HomeController.cs, we added a secured action method. This method has [Authorize] attribute and is shown below.
[Authorize]
public IActionResult Secure()
{
return View();
}
We are going to prevent anonymous access to this action method with Duende Identity Server. User will have to perform login to this ASP.NET Core Identity account before they can access this action method.
The razor view for this action method (named Secure.cshtml) will show the claims of the user and is given below.
@{
ViewData["Title"] = "Secure";
}
<div class="text-center">
<h1 class="display-4">Claims</h1>
<dl>
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>
</div>
In the Client app, install the package called Microsoft.AspNetCore.Authentication.OpenIdConnect. We then have to configure OpenID Connect(OIDC) authentication workflow in the program class.
In the Program.cs, we add the following code for OIDC.
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:7128";
options.ClientId = "duendeclient";
options.MapInboundClaims = false;
options.SaveTokens = true;
});
app.UseAuthentication();
app.UseAuthorization();
We added cookie (with .AddCookie(“cookies”)) and OIDC (with .AddOpenIdConnect(“oidc”, options =>{…})) authentication to the app. The cookie will store the JWT token after a user is successfully login. This cookie will be transferred to the server app on every request, thus the server will be able to recognize the client and the client does not need to re-login again and again.
The authority is assigned to https://localhost:7128 which is the url of the server.
Also note the ClientId is assigned to “duendeclient” i.e. options.ClientId = “duendeclient”;. Kindly recall that in the server app we have also added this same value on it’s program class.
Also add the UseAuthentication and UseAuthorization middlewares.
In the _Layout.cshtml, we added the the logout button.
<form class="form-inline" method="post" asp-controller="Home" asp-action="Logout">
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
The action Logout is defined on the HomeController.cs file.
[HttpPost]
public IActionResult Logout()
{
return SignOut("cookies", "oidc");
}
Also once the user is login, we are showing hello message with his username from the claims variable as given below.
if (User.Identity.IsAuthenticated)
{
string name="Hello "+ User.Claims.Where(c => c.Type == "name").Select(c => c.Value).SingleOrDefault();
@name;
}
Congrats, we just completed the integration part. We can now test the working of this app.
Run both the Server and Client apps in Visual Studio. In the client app click the “Secure” link on the menu.
We will be redirected to the server app where login form will be presented to us.
Here enter, email – “[email protected]” and password – “Admin@123” and click login button. Recall that this is the account we created earlier.
On successful login we will be redirected to the “Secure” url and we will be shown all our claims. Check the below image.
The Duende Identity Server is working properly.
In this tutorial we implement Duende Identity Server authentication and authorization features on ASP.NET Core app that has ASP.NET Core Identity added to it. We also implemented OpenID Connect and login, logout, register pages. If you have any questions then use the comments section below also don’t forget to download the GitHub repository.