I have my ASP.NET Core (.NET5) project with API controllers. I want to secure my APIs with Identity Server. My goal is to give to some clients access to the APIs based on client_id and client_secret and based on that define what APIs they can call. For that reason, I added in the Startup.cs the following code
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAuthentication(
IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = apiSettings.Authority;
options.ApiName = apiSettings.ApiName;
options.ApiSecret = apiSettings.ApiSecret;
});
}
So, the in each controller I added the [Authorize] attribute. Now, I want to call this APIs from a Console Application or Web Application using HttpClient.
private static async Task<string> GetAccessToken()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
// We want the response to be JSON.
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// Build up the data to POST.
List<KeyValuePair<string, string>> postData =
new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("scope", "myscope"));
postData.Add(new KeyValuePair<string, string>("client_id", clientId));
postData.Add(new KeyValuePair<string, string>("client_secret",
clientSecret));
FormUrlEncodedContent content = new FormUrlEncodedContent(postData);
// Post to the Server and parse the response.
HttpResponseMessage response = await
client.PostAsync("/api/v1/Test", content);
string jsonString = await response.Content.ReadAsStringAsync();
object responseData = JsonConvert.DeserializeObject(jsonString);
// return the Access Token.
return ((dynamic)responseData).data;
}
}
The call always returns 401Unauthorized. What is the correct way to call the APIs with client_id and client_secret? Is scope required?
Update
To clarify, I think a scenario machine-to-machine. So, there is not user involved. When the HttpClient calls the API, it has to pass the authentication. In the machine-to-machine scenario, I want to check if the request has a particular scope, for example api_read, api_write, api_full and based on that the call has or has not access to a function.
For example, in Blazor, I created a function to pass the scope but not the clinet_id and client_secret.
public class MyAuthorizationMessageHandler :
AuthorizationMessageHandler
{
public MyAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigation, IConfiguration configuration) :
base(provider, navigation)
{
string apiEndpoint = configuration["Api:EndpointsUrl"];
string apiScope = configuration["Api:Scope"];
ConfigureHandler(authorizedUrls: new[] { apiEndpoint },
scopes: new[] { apiScope });
}
}