Search code examples
c#.net-6.0identityserver4httpclientfactoryhttpinterceptor

Can't get access token from Identity Server 4 when using IHttpClientFactory and Intercept HttpClient GetAsync


I am trying to get access token from Identity Server 4 in my .Net 6 solution. I have 3 projects are running : Identity Server to get access token from it Movies.API to access secured resource Movies.Client to calling the api and get the secured resource using the access token that I got it from Identity Server using http message handler

here are my code for identity server config (related Clients):

public class Config
{
    public static IEnumerable<Client> Clients =>
        new Client[]
        {
            new Client
               {
                    ClientId = "movieClient",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "movieAPI" } // as the name in ApiScopes static list 
               },
            new Client
               {
                   ClientId = "movies_mvc_client",
                   ClientName = "Movies MVC Web App",
                   AllowedGrantTypes = GrantTypes.Code,
                   RequirePkce = false,
                   AllowRememberConsent = false,
                   RedirectUris = new List<string>()
                   {
                       "https://localhost:5002/signin-oidc"
                   },
                   PostLogoutRedirectUris = new List<string>()
                   {
                       "https://localhost:5002/signout-callback-oidc"
                   },
                   ClientSecrets = new List<Secret>
                   {
                       new Secret("secret".Sha256())
                   },
                   AllowedScopes = new List<string>
                   {
                       IdentityServerConstants.StandardScopes.OpenId,
                       IdentityServerConstants.StandardScopes.Profile,

                   }
            }
        };



    public static IEnumerable<ApiScope> ApiScopes =>
       new ApiScope[]
       {
           new ApiScope("movieAPI","Movie API")
       };

// rest of the code ...

and here are the injected IHttpClientFactory in the service GetMovies() :

public async Task<IEnumerable<Movie>> GetMovies()
    {
        // ** OPTION 1 **
        var httpClient = _httpClientFactory.CreateClient("MovieAPIClient");
    
        var request = new HttpRequestMessage(HttpMethod.Get, "/api/movies/");

        // using the handler
        var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
    
        response.EnsureSuccessStatusCode();
    
        var content = await response.Content.ReadAsStringAsync();
    
        var movieList = JsonConvert.DeserializeObject<List<Movie>>(content);
    
        return movieList;
        ...

and here are the implementation of the request handler : enter image description here

and finally here are the registration of the HttpClient in Movie.Client Program.cs :

// 1- create an httpclient used for accessing in Movies.API
builder.Services.AddTransient<AuthenticationDelegatingHandler>();

builder.Services.AddHttpClient("MovieAPIClient", client =>
{
    client.BaseAddress = new Uri("https://localhost:5001/");
    client.DefaultRequestHeaders.Clear();
    client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<AuthenticationDelegatingHandler>();


// 2- create an httpclient used for accessing in the IDP
builder.Services.AddHttpClient("IDPClient", client =>
{
    client.BaseAddress = new Uri("https://localhost:5005/");
    client.DefaultRequestHeaders.Clear();
    client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<AuthenticationDelegatingHandler>();


builder.Services.AddSingleton(new ClientCredentialsTokenRequest
{
    Address = "https://localhost:5005/connect/token",
    ClientId = "movieClient",
    ClientSecret = "secret",
    Scope = "movieAPI"
});

now in the debugging : every time I hit continue button I get back again to SendAsync() in the handler.

Could anyone give me a hand to fix this issue, Thank you.


Solution

  • I find the problem !, it is because I am using the handler on "IDPClient" which it should be handled by Identity Server 4 side.

    before

        // 2- create an httpclient used for accessing in the IDP
        builder.Services.AddHttpClient("IDPClient", client =>
        {
            client.BaseAddress = new Uri("https://localhost:5005/");
            client.DefaultRequestHeaders.Clear();
            client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
        }).AddHttpMessageHandler<AuthenticationDelegatingHandler>(); // remove the handler
    

    after

       // 2- create an httpclient used for accessing in the IDP
       builder.Services.AddHttpClient("IDPClient", client =>
       {
            client.BaseAddress = new Uri("https://localhost:5005/");
            client.DefaultRequestHeaders.Clear();
            client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
       });
    

    and it will works!