C# tutorials > Frameworks and Libraries > Other Important Libraries > IdentityServer4/Duende IdentityServer for security (OpenID Connect, OAuth 2.0)
IdentityServer4/Duende IdentityServer for security (OpenID Connect, OAuth 2.0)
IdentityServer4/Duende IdentityServer for Security (OpenID Connect, OAuth 2.0)
IdentityServer4 (now Duende IdentityServer) is a popular open-source framework for ASP.NET Core that implements OpenID Connect and OAuth 2.0 protocols. These protocols provide a standardized way to authenticate users and authorize applications to access resources on their behalf. It is a crucial component for securing modern web applications, APIs, and mobile apps. This tutorial will guide you through the fundamental concepts and demonstrate how to use IdentityServer4/Duende IdentityServer in your C# projects.
Introduction to OpenID Connect (OIDC) and OAuth 2.0
OAuth 2.0: An authorization framework that enables a third-party application to obtain limited access to an HTTP service on behalf of a resource owner. It defines roles like Resource Owner, Client, Authorization Server, and Resource Server. OpenID Connect (OIDC): An authentication layer built on top of OAuth 2.0. It allows clients to verify the identity of the end-user based on the authentication performed by an authorization server, as well as to obtain basic profile information about the end-user in an interoperable and standardized manner. It introduces the concept of an 'ID Token' to provide user authentication information. Key Concepts:Understanding the Protocols
Setting up a Duende IdentityServer Project
First, create a new ASP.NET Core Web API project. This will serve as our IdentityServer. Install the necessary NuGet package. Note that IdentityServer4 has transitioned to Duende IdentityServer, requiring a commercial license for production use after a grace period. For development and testing purposes, it's perfectly acceptable to start with the free development license.Creating a New ASP.NET Core Project
Installing the Duende IdentityServer NuGet Package
dotnet add package Duende.IdentityServer
Configuring IdentityServer
In the In the Startup Configuration
ConfigureServices
method of your Startup.cs
file, add the IdentityServer services. This involves:
AddIdentityServer()
.Configure
method, make sure to use the IdentityServer middleware using app.UseIdentityServer()
, and ensure it is placed correctly within the middleware pipeline. Crucially, it needs to be placed after UseRouting()
and before UseAuthorization()
.
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddInMemoryClients(Config.Clients)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddTestUsers(Config.TestUsers);
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
Defining Configuration (Clients, Resources, Scopes, Users)
Create a Configuring Clients, Identity Resources, API Scopes, and Users
Config.cs
file to define the configurations for IdentityServer. Here's a breakdown of what's being configured:
IdentityResources.OpenId()
is mandatory for OpenID Connect. IdentityResources.Profile()
adds standard profile claims like name and email.api1
is a scope that grants access to your API.
ClientId
: A unique identifier for the client.AllowedGrantTypes
: Specifies the flow that the client will use (e.g., ClientCredentials
for machine-to-machine communication, Code
for interactive users).ClientSecrets
: A secret used to authenticate the client with IdentityServer.AllowedScopes
: The scopes that the client is allowed to request.RedirectUris
: Where the client will be redirected after a successful authentication. Used only for flows where the client interacts with the user (e.g. code flow).PostLogoutRedirectUris
: Where the client will be redirected after a logout. Used only for flows where the client interacts with the user (e.g. code flow).
// Config.cs
using Duende.IdentityServer.Models;
using IdentityModel;
using System.Collections.Generic;
using Duende.IdentityServer;
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("api1", "My API")
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = { "api1" }
},
new Client
{
ClientId = "mvc",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = { "https://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" }
}
};
public static List<TestUser> TestUsers =>
new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password"
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password"
}
};
}
Protecting an API
To protect your API, you'll need to configure it to validate the access tokens issued by IdentityServer. With this configuration, any request to the protected API endpoint will require a valid access token issued by IdentityServer. The API will validate the token and extract the user's identity from it.Securing Your API with OAuth 2.0
Startup.cs
, add JWT bearer authentication:
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001"; // URL of your IdentityServer
options.RequireHttpsMetadata = false; // Only for development!
options.Audience = "api1"; // The API scope
});
services.AddAuthorization();
[Authorize]
Attribute: Add the [Authorize]
attribute to your API controllers or actions that you want to protect.
// API Controller
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace Api.Controllers
{
[ApiController]
[Route("[controller]")]
[Authorize]
public class IdentityController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
}
Acquiring Tokens (Client Credentials Flow)
The client credentials grant type is suitable for machine-to-machine communication where a client application needs to access resources on its own behalf, without user interaction.Obtaining Access Tokens (Client Credentials Grant)
IdentityModel
NuGet package to your client application:dotnet add package IdentityModel
GetToken
method to request an access token from IdentityServer.
// Example using IdentityModel library
using IdentityModel.Client;
using System.Net.Http;
using System.Threading.Tasks;
public static async Task<string> GetToken()
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return null;
}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return null;
}
return tokenResponse.AccessToken;
}
Calling the API
Once you have obtained an access token, you can use it to make requests to your protected API.Calling Your Protected API
Authorization
header to your HTTP request with the Bearer
scheme and the access token.
// Example calling the API with the token
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public static async Task CallApi(string accessToken)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.GetAsync("https://localhost:6001/identity"); // URL of your API
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
Real-Life Use Case Section
Scenario: Consider a microservices architecture where multiple services need to communicate with each other securely. IdentityServer4/Duende IdentityServer can be used to authenticate and authorize these services. Each service acts as a client, requesting tokens from IdentityServer and using those tokens to access other services. Benefits:
Best Practices
Security Considerations:
Interview Tip
Common Interview Question: Explain the difference between authentication and authorization. Answer: Authentication is the process of verifying the identity of a user or application. Authorization is the process of determining what resources a user or application has access to. IdentityServer4/Duende IdentityServer handles both authentication (via OpenID Connect) and authorization (via OAuth 2.0).
When to use them
Memory footprint
The memory footprint of IdentityServer4/Duende IdentityServer depends on several factors, including the number of clients, users, and scopes, the size of the configuration data, and the number of concurrent requests. To minimize the memory footprint, consider the following:
alternatives
Here are some alternatives to IdentityServer4/Duende IdentityServer for implementing security in your applications:
pros
Here are some advantages of using IdentityServer4/Duende IdentityServer for implementing security in your applications:
cons
Here are some disadvantages of using IdentityServer4/Duende IdentityServer for implementing security in your applications:
FAQ
-
What is the difference between IdentityServer4 and Duende IdentityServer?
IdentityServer4 was the original open-source framework. Duende IdentityServer is the commercial successor, offering enhanced features, support, and requiring licensing for production use. -
Is IdentityServer4 free to use?
IdentityServer4 is open source and free to use. However, it is no longer actively maintained. Duende IdentityServer requires a commercial license for production deployments. -
What are the different grant types in OAuth 2.0?
OAuth 2.0 defines several grant types, including authorization code, implicit, resource owner password credentials, and client credentials. Each grant type is suitable for different scenarios. -
How do I secure my API with IdentityServer4/Duende IdentityServer?
You can secure your API by configuring it to validate the access tokens issued by IdentityServer4/Duende IdentityServer. This typically involves adding JWT bearer authentication to your API and applying the [Authorize] attribute to your controllers or actions.