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.
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:
Understanding IdentityServer architecture
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:
2. Scope:
3. Supported Protocols:
4. Token-Based Authentication:
5. Scenarios:
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:
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 .
Full Stack .NET Developer | C# | Asp.net Core | Angular | SQL | Software Developer | Web Development | Backend Developer
6 个月Mohamed Samy
Software Engineer | ITI Graduate
1 年Awesome work.. keep Going, Turkey ????
software Engineer | .Net Developer
1 年Amazing work ??
Software Development Manager | Senior Technical Team Lead | Assistant Lecturer
1 年Great work, keep going ??
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 ??