Chapter 5 - jayharris/workshop-oidc GitHub Wiki

Chapter 5: Identity Resources and Scope

5.1: Create another Console Client, repeating 2.1-2.5 for the new client

# From ./
dotnet new console --name IdentityResourceClient --output ./src/IdentityResourceClient/
dotnet sln add ./src/IdentityResourceClient/IdentityResourceClient.csproj

Install IdentityModel NuGet Packages

# From ./src/IdentityResourceClient
dotnet add package IdentityModel

Make Main Method Asynchronous.

Add the necessary Using statements. IdentityResourceClient\Program.cs

using System.Net.Http;
using System.Threading.Tasks;
using IdentityModel.Client;

Add an async Main method IdentityResourceClient\Program.cs replace contents of Program class with:

public static void Main(string[] args)
{
  Console.Title = "Identity Resource Console Client";

  MainAsync().GetAwaiter().GetResult();

  Console.Write("\nPress any key to continue... ");
  Console.ReadKey();
}

private static async Task MainAsync()
{
}

Add a constant for the Provider URL

IdentityResourceClient\Program.cs within the Program class:

private const string Authority = "http://localhost:5000";

Send a discovery request to the provider

IdentityResourceClient\Program.cs within the MainAsync method of the Program class:

var client = new HttpClient();
var discoveryResponse = await client.GetDiscoveryDocumentAsync(Authority);
if (discoveryResponse.IsError) throw new ApplicationException(discoveryResponse.Error);
Console.WriteLine("Successful endpoint discovery");

Run the Provider and test the Console

# From ./src/IdentityProvider/
dotnet run
# From ./src/IdentityResourceClient
dotnet run

# Expected result: "Successful endpoint discovery"

5.2: Add a new Identity Resource and Client configuration to the Identity Provider

Add Identity Resources to IdentityProfier\IdentityConfiguration.cs within the GetIdentityResources method:

return new List<IdentityResource>
{
    new IdentityResources.OpenId(),
    new IdentityResources.Profile()
};

Add a client to IdentityProfier\IdentityConfiguration.cs within the GetClients method:

new Client
{
  ClientId = "IdentityResourceClient",
  ClientName = "Identity Resource Console Client",
  ClientSecrets =
  {
      new Secret("secretKey".Sha256())
  },
  AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
  AllowedScopes = { "openid","profile" }
}

5.3: Add constants for the Client configuration

ConsoleClient\Program.cs within the Program class:

private const string ClientId = "IdentityResourceClient";
private const string ClientSecret = "secretKey";
private const string ClientScope = "openid profile";

private const string UserName = "[email protected]";
private const string UserPassword = "AwesomePassword4U!";

5.4: Send a Token request to the provider

ConsoleClient\Program.cs append to the MainAsync method of the Program class:

// request token
var tokenRequest = new PasswordTokenRequest {
  Address = discoveryResponse.TokenEndpoint,
  ClientId = ClientId,
  ClientSecret = ClientSecret,
  Scope = ClientScope,
  UserName = UserName,
  Password = UserPassword
};
var tokenResponse = await client.RequestPasswordTokenAsync(tokenRequest);
if (tokenResponse.IsError) throw new ApplicationException(tokenResponse.Error);
Console.WriteLine($"Identity Response Code: {(int) tokenResponse.HttpStatusCode} {tokenResponse.HttpStatusCode}");
Console.WriteLine($"Token Response:\n{tokenResponse.Json}\n\n");

5.5: Run the Provider and test the Console

# From ./src/IdentityProvider/
dotnet run
# From ./src/ConsoleClient
dotnet run

# Expected result: A returned Token Response, a JSON object with an `access_token`

5.6: Request User Claims

ConsoleClient\Program.cs append to the MainAsync method of the Program class:

// request userInfo
var userInfoRequest = new UserInfoRequest {
    Address = discoveryResponse.UserInfoEndpoint,
    Token = tokenResponse.AccessToken
};
var userInfoResponse = await client.GetUserInfoAsync(userInfoRequest);

Console.WriteLine($"UserInfo Response Code: {(int) userInfoResponse.HttpStatusCode} {userInfoResponse.HttpStatusCode}");
if (userInfoResponse.IsError)
{
    Console.WriteLine($"UserInfo Error Response: {userInfoResponse.Error}");
    throw new Exception(userInfoResponse.Error);
}

Console.WriteLine("User Claims:");
foreach (var claim in userInfoResponse.Claims)
{
    Console.WriteLine($"{claim.Type}: {claim.Value}");
}

5.7: Run the Provider and test the Console

# From ./src/IdentityProvider/
dotnet run
# From ./src/ConsoleClient
dotnet run

# Expected result: A returned User Info Response, 200 OK with a JSON object showing the user's identifying information.