Retrieving, using and validating token from an IdentityServer

retrieving,-using-and-validating-token-from-an-identityserver

📮 Contact 🇧🇷 🇺🇸 🇫🇷

Twitter
LinkedIn

This is an example of how to a client application retrieves a token from a Identity Server and use it in an WebApi to consumes an endpoint.

How this project was created

This example was built based on Duende documentation

Identity Server

Identity provider for our sample.

Install Duende templates

dotnet new --install Duende.IdentityServer.Templates

Create an empty solution

dotnet new sln -n IdentityServerDemo

Create Identity server project

dotnet new isempty -n IdentityServer

Add IdendityServer projet to the blank solution

dotnet sln add .IdentityServerIdentityServer.csproj

Add scope to config.cs

public static IEnumerable<ApiScope> ApiScopes =>
    new ApiScope[]
        { new ApiScope(name: "campelo-api", displayName: "CampeloAPI") };

Add client to config.cs

public static IEnumerable<Client> Clients =>
    new Client[]
        { 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 = { "campelo-api" }
        } };

Openid-configuration

Now you can access openid-configuration from the identity server. Run the solution and go to https://localhost:5001/.well-known/openid-configuration. So you can retrieve something like this.

{
   "issuer":"https://localhost:5001",
   "jwks_uri":"https://localhost:5001/.well-known/openid-configuration/jwks",
   "authorization_endpoint":"https://localhost:5001/connect/authorize",
   "token_endpoint":"https://localhost:5001/connect/token",
   "userinfo_endpoint":"https://localhost:5001/connect/userinfo",
   "end_session_endpoint":"https://localhost:5001/connect/endsession",
   "check_session_iframe":"https://localhost:5001/connect/checksession",
   "revocation_endpoint":"https://localhost:5001/connect/revocation",
   "introspection_endpoint":"https://localhost:5001/connect/introspect",
   "device_authorization_endpoint":"https://localhost:5001/connect/deviceauthorization",
   "backchannel_authentication_endpoint":"https://localhost:5001/connect/ciba",
   "frontchannel_logout_supported":true,
   "frontchannel_logout_session_supported":true,
   "backchannel_logout_supported":true,
   "backchannel_logout_session_supported":true,
   "scopes_supported":[
      "openid",
      "campelo-api",
      "offline_access"
   ],
   "claims_supported":[
      "sub"
   ],
   "grant_types_supported":[
      "authorization_code",
      "client_credentials",
      "refresh_token",
      "implicit",
      "urn:ietf:params:oauth:grant-type:device_code",
      "urn:openid:params:grant-type:ciba"
   ],
   "response_types_supported":[
      "code",
      "token",
      "id_token",
      "id_token token",
      "code id_token",
      "code token",
      "code id_token token"
   ],
   "response_modes_supported":[
      "form_post",
      "query",
      "fragment"
   ],
   "token_endpoint_auth_methods_supported":[
      "client_secret_basic",
      "client_secret_post"
   ],
   "id_token_signing_alg_values_supported":[
      "RS256"
   ],
   "subject_types_supported":[
      "public"
   ],
   "code_challenge_methods_supported":[
      "plain",
      "S256"
   ],
   "request_parameter_supported":true,
   "request_object_signing_alg_values_supported":[
      "RS256",
      "RS384",
      "RS512",
      "PS256",
      "PS384",
      "PS512",
      "ES256",
      "ES384",
      "ES512",
      "HS256",
      "HS384",
      "HS512"
   ],
   "authorization_response_iss_parameter_supported":true,
   "backchannel_token_delivery_modes_supported":[
      "poll"
   ],
   "backchannel_user_code_parameter_supported":true
}

API project

Web API project.

Create a new web API project

dotnet new webapi -n WebApi

Adding webapi to the solution

dotnet sln add .WebApiWebApi.csproj

Adding JWT Bearer authentication

dotnet add .WebApiWebApi.csproj package Microsoft.AspNetCore.Authentication.JwtBearer

Set JWT Bearer as the default authentication scheme

Update the Program.cs or Startup.cs file.

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://localhost:5001";

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = false
        };
    });

/// UseAuthentication right before UseAuthorization
app.UseAuthentication();
app.UseAuthorization();

Add a new controller for testing

[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

Configure the WebAPI to listen on Port 6001

Edit the launchSettings.json file in Properties folder.

"applicationUrl": "https://localhost:6001"

Run the WebAPI project

Now you have a 401 (unauthorized) error response when trying to access https://localhost:6001/identity

Client project

Create a console client project

dotnet new console -n Client

Add the client project to the solution

dotnet sln add .ClientClient.csproj

Add IdentityModel to the client project

dotnet add .ClientClient.csproj package IdentityModel

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 = "campelo-api"
});

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.AccessToken);

Call WebAPI

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

var response = await apiClient.GetAsync("https://localhost:6001/identity");
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 }));
}

Run client

Be sure that the IdentityServer and WebAPI are running and run the Client project to see something like this
Image 1

Source code

Source code

Typos or suggestions?

If you’ve found a typo, a sentence that could be improved or anything else that should be updated on this blog post, you can access it through a git repository and make a pull request. If you feel comfortable with github, instead of posting a comment, please go directly to https://github.com/campelo/documentation and open a new pull request with your changes.

Total
1
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
the-b2b-saas-sales-funnel:-how-your-brand-can-optimize-it-to-boost-conversions

The B2B SaaS Sales Funnel: How Your Brand Can Optimize It to Boost Conversions

Next Post
top-7-featured-dev-posts-from-the-past-week

Top 7 Featured DEV Posts from the Past Week

Related Posts