1. About this Post
In this post on IdentityServer4, we will continue our study from my last post “Creating ASP.NET Core Identity Using IdentityServer4 Visual Studio 2017” and the IdentityServer4 official documentation. We are going to discuss using user passwords with IdentityServer4.
We are going to discuss using user passwords with IdentityServer4 based on our existing projects from my last post. You can find the solution source at my Github Repository.
2. Modify Identity Server Application
Before modifying our code, let’s check the authentication type in our last post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public static IEnumerable GetClients() { return new List { new Client { ClientId = "client", // no interactive user, use the clientid/secret for authentication AllowedGrantTypes = GrantTypes.ClientCredentials, // secret for authentication ClientSecrets = { new Secret("secret".Sha256()) }, // scopes that client has access to AllowedScopes = { "api1" } } }; } |
We set the grant type as Client Credentials, This is the simplest grant type and is used for server to server communication. In IdentityServer4, there are many other powerful types supported, please click here to have a check.
In this update, we create new method “GetClients2”, using grant type as “ResourceOwnerPassword“. (Hold, we will update the code later.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public static IEnumerable GetClients2() { return new List { new Client { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; } |
Compared with Client Credentials, ResourceOwnerPassword has a username & password checking based on client secret checking. In order to login into the Identity Server, firstly, you need tell the server what “client” you are. For example, we need to login to Facebook from Facebook Mobile App, we need tell Facebook Server we are logging by mobile app client, instead of web app client. By giving the client id and client secret, the server will understand the client identity and deal with the request with certain patterns.
In order to login into the Identity Server, firstly, you need tell the server what “client” you are. For example, we need to login to Facebook from Facebook Mobile App, we need tell Facebook Server we are logging by mobile app client, instead of web app client. By giving the client id and client secret, the server will understand the client identity and deal with the request with certain patterns. What is more, IdentityServer can specify the allow scope for the client connection, which can benefit the application user groups, like the normal users, admin users.
After the server shakes hand with a client with the right client id and secret, the client needs to give the correct username and password to get the connection. This password step is very easy to understand.
Now, let’s update our Server code, the code has no big difference from the official website, you can read more in the link in the top of this page.
Add these code to your Config.cs file in your IdentyServer4_Server porject. We are adding the new users with password and the client set up.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public static IEnumerable GetClients2() { return new List { new Client { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; } public static List GetUsers() { return new List { new TestUser { SubjectId = "1", Username = "alice", Password = "password" }, new TestUser { SubjectId = "2", Username = "bob", Password = "password" } }; } |
Now, in your Startup.cs file, modify the ConfigureServices method.
1 2 3 4 5 6 7 8 9 10 11 |
public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) //.AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()) .AddInMemoryClients(Config.GetClients2()); services.AddMvc(); } |
Notice here, we comment out the
1 |
AddInMemoryClients(Config.GetClients()) |
instead, we are using the our new client setup.
1 |
.AddInMemoryClients(Config.GetClients2()); |
1 |
I have tried to leave both of the client setups there, and it seems not working!. The later setup will overwrite the former one! |
3. Modify Client Console App
Open the Program.cs file in the console client. Modify your Main method and add another CallAPIAsyncUsingPassword method. It should be like the codes below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
using IdentityModel.Client; using Newtonsoft.Json.Linq; using System; using System.Net.Http; namespace ClientAPP { class Program { static void Main(string[] args) { CallAPIAsyncUsingPassword(); Console.ReadLine(); } static async void CallAPIAsync() { var discoveryClient = new DiscoveryClient("http://localhost:65404"); var disco = await discoveryClient.GetAsync(); // request token var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret"); var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1"); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:58436/identity"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } static async void CallAPIAsyncUsingPassword() { var discoveryClient = new DiscoveryClient("http://localhost:65404"); var disco = await discoveryClient.GetAsync(); // request token var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret"); var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1"); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n"); } } } |
In this file, we use the new method to call the server using the user name password. As we discussed above, before using the password, we need to use the client id and secret to talk with the server to get the token.
Then, start your application, it should get this token. Cool!
You can copy your token to https://jwt.io/ to view the token value.