Identity Server Duende: One Password for Many Doors

Identity Server Duende: One Password for Many Doors


Introduction to Identity Server

Duende IdentityServer is a powerful engine that specializes in implementing OpenID Connect and OAuth 2.0 protocols. In this article, we'll break down the key components and functionalities of Duende IdentityServer in a simple and engaging manner.

  • Roles and Terminology: In the realm of Duende IdentityServer, you might come across various terms like security token service, identity provider, authorization server, and more. Essentially, they all serve the same purpose: they are software components that issue security tokens to clients.
  • Core Functions: Duende IdentityServer offers a range of essential functions, including:

  1. Managing access to resources.
  2. Authenticating users through local accounts or external identity providers.
  3. Providing session management and single sign-on capabilities.
  4. Handling client management and authentication.
  5. Issuing identity and access tokens to clients.


Identity Server's Impact on Daily Life

The Identity Server is like a superhero tool, ready to step in and make things easier and safer in different situations. Let's explore some exciting scenarios where the Identity Server takes the spotlight:

  1. Supercharge Your Web Experience with Single Sign-On (SSO): Imagine logging in once and gaining access to all your favorite web apps without the hassle of re-entering your credentials. That's the magic of Identity Server's Single Sign-On. It's like having a golden ticket to the digital world, making your online journey smoother and more enjoyable.
  2. API Authentication and Authorization: APIs are like secret passages to valuable information. The Identity Server steps in to make sure only the right people get in. It issues special tokens (think of them as secret handshakes) that the API checks before granting access. This keeps your data locked up tight and only accessible to the right folks.

  1. Federated Identity Management: Ever wish you could use your favorite social media account to log in to other apps? Identity Server makes it happen. It's like having a master key that lets you use credentials from Google, Microsoft, or even your social media accounts to log in and access what you need. It's super convenient and keeps things secure.

  1. Identity and Access Management (IAM) for Microservices: Microservices are like a team of specialized experts, each with their own skills. The Identity Server steps in as the chief coordinator, making sure they all work together smoothly. It's like the conductor of an orchestra, ensuring that each microservice knows who's allowed to do what, and keeping communication secure.

  1. Multi-Tenant Applications: Imagine an app that serves different groups of people, each with their own preferences and access rights. Identity Server is like a skilled juggler, keeping all the balls in the air. It manages user identities, roles, and permissions across different groups, making sure everyone gets what they need without any mix-ups.

  1. Security for IoT (Internet of Things) Devices: Imagine a world where your smart devices can securely communicate with each other. Identity Server makes it happen. It issues special codes that IoT devices use to talk securely with backend services. It's like having a digital bouncer making sure only the right devices get access.


Understanding IdentityServer architecture

  • User: A user is a human who uses a registered client to access resources securely.
  • Client: A client is a software application that requests tokens from your IdentityServer. This request can be for authenticating a user (identity token) or accessing a resource (access token). Clients must be registered with your IdentityServer before making token requests.
  • Resources: Resources represent the entities you want to protect with your IdentityServer, It may be identity data of users or APIs. Each resource has a unique name, and clients use this name to specify which resources they need access to. Identity Data: This includes claims about a user, such as their name or email address. APIs: Represent the functionalities a client wants to access, usually modeled as Web APIs, but not limited to them.
  • Access Tokens: Access tokens act as keys to access API resources. Clients request access tokens and pass them on to the respective API. These tokens contain crucial information about the client and the user (if applicable). APIs use this information to authorize access to their data and functionalities.


Comparing Identity Server and ASP.NET Identity

When it comes to authentication and authorization, Identity Server and ASP.NET Identity are like two different tools in a toolbox, each with its own unique purpose. Let's break down the key distinctions between the two:

1. Purpose:

  • Identity Server: Imagine Identity Server as the grand master of authentication and authorization. It's a versatile framework that specializes in providing centralized services for managing identities, issuing special codes (tokens), and ensuring secure access for various types of applications like web, mobile, and APIs. It's the go-to choice when you need a dedicated Identity Provider, especially for things like Single Sign-On (SSO) and protecting APIs.

  • ASP.NET Identity: ASP.NET Identity is a membership system for adding login functionality to web applications. It provides user management features like user registration, login, password recovery, and role-based authorization within ASP.NET applications. It's perfect for handling user authentication and authorization within a single application.

2. Scope:

  • Identity Server: Identity Server is like a free bird. It can stand alone and provide identity services for multiple applications. It's not tied down to any specific technology, which means it can spread its wings in applications built with ASP.NET , Java, Node.js, and more.

  • ASP.NET Identity: ASP.NET Identity is like a loyal companion specifically designed for ASP.NET web applications. It's tailored to work seamlessly within the ASP.NET framework.

3. Supported Protocols:

  • Identity Server: Identity Server is all about the latest and greatest protocols like OAuth 2.0 and OpenID Connect. These are like secret handshakes that secure APIs and make Single Sign-On (SSO) a breeze.

  • ASP.NET Identity: ASP.NET Identity focuses on the classics. It's well-versed in traditional authentication methods like username/password, cookie-based authentication, and managing identities based on claims.

4. Token-Based Authentication:

  • Identity Server: Identity Server is like a token-producing factory. It specializes in creating special codes (tokens) that act as keys to access resources and APIs securely.

  • ASP.NET Identity: ASP.NET Identity is more of a cookie lover. While it does support token-based authentication, its main focus is on cookie-based authentication for web applications.

5. Scenarios:

  • Identity Server: Imagine Identity Server as the captain of the ship in scenarios that demand Single Sign-On (SSO), protecting APIs, dealing with federated identities, and managing identities centrally across multiple applications and services.

  • ASP.NET Identity: ASP.NET Identity is best suited for web applications that require user authentication and authorization within a single application.

In a nutshell, Identity Server and ASP.NET Identity are like two superheroes with their own unique powers. While Identity Server thrives in scenarios like Single Sign-On and securing APIs, ASP.NET Identity shines in providing user management for web applications. So, depending on the adventure you're embarking on, choose your trusted companion wisely!

Note: IdentityServer can seamlessly integrate with ASP.NET Identity, combining the flexibility of IdentityServer for authentication and authorization with the user management features of ASP.NET Identity. This powerful combination allows developers to create secure, feature-rich applications with a streamlined user experience.

Now let's delve into the realm of clients and explore the various aspects in detail

Client Overview:

A client is a crucial piece of software that seeks tokens from your IdentityServer, whether it's for authenticating a user (requesting an identity token) or accessing a resource (requesting an access token). Registration of a client with your IdentityServer is a prerequisite before it can make token requests. Clients come in diverse types, including web applications, native mobile or desktop applications, SPAs, and server processes. These represent the primary flows in grant types for clients.

1. Authorization Code Flow:

The authorization code grant type stands out as a redirection-based flow, tailored for confidential clients. It's the go-to choice for obtaining both access tokens and refresh tokens. For this flow to operate seamlessly, the client must be capable of interacting with the resource owner's user-agent, typically a web browser. Additionally, it should handle incoming requests through redirection from the authorization server. This scenario finds common application in web applications, single-page applications (SPAs), or native/mobile apps that require interactive user experiences, such as authentication or consent. In protocol terms, this is known as the Authorization Code Flow.

How It Works:

The client guides the resource owner to an authorization server, redirecting them rather than directly requesting authorization. The resource owner is then redirected back to the client with an authorization code, which the client captures and exchanges for an access token in the background. Due to the redirection-based nature of this flow, the client must proficiently interact with the resource owner's user-agent and handle incoming requests (via redirection) from the authorization server.


2. Resource Owner Password Credentials Grant:

This grant type is applicable in situations where there exists a high level of trust between the resource owner and the client, such as with a service's own mobile client. It is also employed when the client can obtain the resource owner's credentials. Instead of redirecting the user to the authorization server, the client prompts the user for their username and password. Subsequently, the client forwards both the user's credentials and its own to the authorization server. The authorization server then responds by providing the access token.


3. Machine to Machine Flow (Client Credentials Flow):

In scenarios lacking an interactive user presence, like background processes, batch jobs, or server daemons, the machine-to-machine communication flow takes center stage. In this setup, two machines communicate directly with each other. To authorize this communication, your IdentityServer issues a token to the caller, whether it be a machine or a service. In protocol terms, this scenario is identified as the Client Credentials Flow.


Demo section

Certainly! Let's walk through a step-by-step demo for both the Client Credentials Flow and the Resource Owner Password Grant in IdentityServer.

Demo: Client Credentials Flow

Step 1: Set Up the IdentityServer:

1- First, create a project of type (ASP.NET Core Empty) for Identity Server.

2- Install "duende.IdentityServer" from NuGet Package Manager.

3- Create a new static class named "Config.cs," which will contain the list of clients and scopes

public static class Config
{
}        

4- Add some scopes to the Config class.

 public static class Config
{
	public static IEnumerable<ApiScope> ApiScopes =>
		new List<ApiScope>
		{
			new ApiScope("api1", "My API1"),

			new ApiScope("api2", "My API2")
		};
}        

5- Add a client to the Config class.

public static class Config
{
	public static IEnumerable<ApiScope> ApiScopes =>
		new List<ApiScope>
		{
			new ApiScope("api1", "My API1"),

			new ApiScope("api2", "My API2")
		};

	public static IEnumerable<Client> Clients =>
		new List<Client>
		{
			new Client
			{
				ClientId = "client",
				ClientSecrets = { new Secret("secret".Sha256()) },

				AllowedGrantTypes = GrantTypes.ClientCredentials,
                // scopes that client has access to
                AllowedScopes = { "api1" , "api2" }
			}
		};
}        

As you see the client should have Client Id ,Secret , Grant Type and Allowed Scopes

Allowed Scope is just defined as the scope of access that the client requests

6- In the "program.cs," use the clients and scopes defined.

builder.Services.AddIdentityServer()
      .AddInMemoryApiScopes(Config.ApiScopes)
      .AddInMemoryClients(Config.Clients);        

7- Add Identity Server to the pipeline.

app.UseIdentityServer();        

Step 2: Configure the Authorized API Resource:

1- Create a new project of type "ASP.NET Core Web API."

2- Install "Microsoft.AspNetCore.Authentication.JwtBearer" from NuGet Package Manager.

3- In "program.cs," add the following code.

Identity Server Url : "https://localhost:5001"
builder.Services.AddAuthentication("Bearer")
	.AddJwtBearer(options =>
	{
		options.Authority = "https://localhost:5001";
		options.TokenValidationParameters.ValidateAudience = false;
	});        

4- Add authentication middleware in "program.cs"

app.UseAuthentication();
app.UseAuthorization();        

5- Add [Authorize] on any controller or action.

[Authorize]        

Step 3: Configure the Client:

In your client application, configure it to use the Client Credentials Flow. Provide the necessary client credentials (client ID and secret) obtained during client registration.

1- Create a "Console App."

2- Install "IdentityModel" from NuGet Package Manager.

3- Make a request to Identity Server to get the access token and provide the client credentials.

Identity Server Url : "https://localhost:5001 "
// discover endpoints from metadata
var client = new HttpClient();

var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError)
{
	Console.WriteLine(disco.Error);
	return;
}

// 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);
	Console.WriteLine(tokenResponse.ErrorDescription);
	return;
}

Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");        

4- Receive and Use the Token. Once the request is successful, your client will receive an access token. Utilize this token to access the protected resources or APIs. Ensure to include the token in the authorization header of your HTTP requests.

// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);

var response = await apiClient.GetAsync("https://localhost:6001/WeatherForecast");
if (!response.IsSuccessStatusCode)
{
	Console.WriteLine(response.StatusCode);
}
else
{
	var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
	Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
}
Console.ReadKey();        
Authorized Resource (Web Api) Url : "https://localhost:6001"

Step 4: Run 3 Projects

You should run the client after the Identity Server and API projects.

It works fine!


Note: You can use Postman to get the access token as follows.
Token Url : https://localhost:5001/Connect/Token

Demo: Resource Owner Password Credentials Grant

Step 1: Configure IdentityServer:

Ensure IdentityServer is configured to allow the Resource Owner Password Credentials Grant. This involves enabling the grant type and defining the necessary client.

1- Add a New Client in the Config Class in Identity Server with GrantTypes ResourceOwnerPassword:

public static class Config
{
	public static IEnumerable<ApiScope> ApiScopes =>
		new List<ApiScope>
		{
			new ApiScope("api1", "My API1"),

			new ApiScope("api2", "My API2")
		};

	public static IEnumerable<Client> Clients =>
		new List<Client>
		{
			new Client
			{
				ClientId = "client",
				ClientSecrets = { new Secret("secret".Sha256()) },

				AllowedGrantTypes = GrantTypes.ClientCredentials,
                // scopes that client has access to
                AllowedScopes = { "api1" , "api2" }
			},
			new Client
			{
				ClientId = "ResourceOwnerPasswordClient",
				ClientSecrets = { new Secret("secret".Sha256()) },

				AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                // scopes that client has access to
                AllowedScopes = { "api1" }
			}
		};
}        

2- Add a List of Users with Usernames and Password in the Config Class:

		public static List<TestUser> TestUsers =>
			new List<TestUser>
			{
				new TestUser
				{
					SubjectId = "00000000-0000-0000-0000-000000000001",
					Username = "AhmedTurky",
					Password = "123456"
				}
			};        

3- In "program.cs," Use This List of Users:

builder.Services.AddIdentityServer()
	  .AddInMemoryApiScopes(Config.ApiScopes)
	  .AddInMemoryClients(Config.Clients)
	  .AddTestUsers(Config.TestUsers);        


Step 2: Requesting Tokens:

Implement logic in your client to request tokens from IdentityServer using the Resource Owner Password Credentials Grant. This involves making a POST request to the token endpoint with the resource owner's credentials.

1- Create the Client as Before:

Note: Remove this block of code:

var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
	Address = disco.TokenEndpoint,
	ClientId = "client",
	ClientSecret = "secret",
	Scope = "api1"
});        

Add this code instead:

var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
	Address = disco.TokenEndpoint,
	ClientId = "ResourceOwnerPasswordClient",
	ClientSecret = "secret",
	Scope = "api1",
	UserName = "AhmedTurky",
	Password = "123456"
});        

Nice, it works!

You should run the client after the Identity Server and API projects.
Note: As before, you can use Postman to get the access token.
Token Url : https://localhost:5001/Connect/Token

Important Notes:

  • Security Considerations: Keep in mind that using the Resource Owner Password Credentials Grant involves handling sensitive information (user credentials) in the client application. Ensure proper security measures and only use this grant type when necessary.
  • Token Expiry: Tokens obtained in both flows have a limited lifespan. Implement token refresh logic if prolonged access is required.

By following these steps, you'll have a functional demonstration of both the Client Credentials Flow and the Resource Owner Password Credentials Grant in IdentityServer, showcasing the flexibility and adaptability of this powerful authentication and authorization framework.


In conclusion, this article has provided you with a comprehensive understanding of Identity Server concepts. However, it's important to note that there is much more to explore within the realm of Identity Server.

Thanks for reading. I hope this was helpful!

The example code is available on GitHub .


Mohamed Sameh

Full Stack .NET Developer | C# | Asp.net Core | Angular | SQL | Software Developer | Web Development | Backend Developer

6 个月
回复
Tarek Eslam

Software Engineer | ITI Graduate

1 年

Awesome work.. keep Going, Turkey ????

Mohamed salem

software Engineer | .Net Developer

1 年

Amazing work ??

Ahmed Abdel-Hameed Khalifa

Software Development Manager | Senior Technical Team Lead | Assistant Lecturer

1 年

Great work, keep going ??

Ahmed Hassan

Software Engineer @ Link Development | ITI Graduate | Full-Stack .NET Core Developer | C# | Microservices | Clean Architecture | Microsoft Power Platform | Dynamics 365 CRM

1 年

Awesome work.. keep Going, Turkey ??

要查看或添加评论,请登录

社区洞察

其他会员也浏览了