Reputation: 435
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 :
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.
Upvotes: 0
Views: 123
Reputation: 435
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!
Upvotes: 0
Reputation: 3
The problem you faced is an infinite loop because you're using the same httpClient
for your request to get movies and your request to get accessToken.
Each request using the httpClient will go through AuthenticationDelegatingHandler
.
To fix your problem, that's my solution.
public class AuthenticationDelegatingHandler : DelegatingHandler{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ClientCredentialsTokenRequest _tokenRequest;
public AuthenticationDelegatingHandler(IHttpClientFactory httpClientFactory, ClientCredentialsTokenRequest tokenRequest){
_httpClientFactory = httpClientFactory;
_tokenRequest = tokenRequest;
}
protected override async Task<IHttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){
// HttpClientFactory will create a new httpClient different from the one registered in Program.cs
var httpClient = _httpClientFactory.CreateClient();
var tokenResponse = await httpClient.RequestClientCredentialsTokenAsync(_tokenRequest);
if(tokenResponse.IsError){
throw new HttpRequestException("Something went wrong while requesting the access token");
}
request.SetBearerToken(tokenResponse.AccessToken);
return await base.SendAsync(request, cancellationToken);
}
}
Upvotes: 0