I've been following this Azure sample for securing an AspNet WebApp to a WebApi we own and secure with our own Azure Active Directory organisation
We have an exisitng AspNet site that is already secured with Azure Active Directory so I am really just trying to insert our equivalent of the sample's TodoListService.
The sample uses MSAL so we have moved the site over to use that. The ConfigureServices method in the WebSite Startup is
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.AddMicrosoftIdentityWebAppAuthentication(this.Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { "https://ourdomain/app.our-service/user_impersonation" })
.AddDownstreamWebApi("OurService", this.Configuration.GetSection("OurServiceApi"))
.AddInMemoryTokenCaches();
// Add Apis
services.AddOurService(this.Configuration);
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim(
System.Security.Claims.ClaimsIdentity.DefaultRoleClaimType,
"Team_Administrators")
.Build();
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
}
The sample says that the scopes need to be in the format api://<client_id>/scope_name but as you can see our scope name is the AD tenant domain plus the scope. Attempts to use the client id resulted in this error
OpenIdConnectProtocolException: Message contains error: 'invalid_resource', error_description: 'AADSTS500011: The resource principal named api://4f3ca2ab-d7dc-401a-a514-37744ab3555f was not found in the tenant named 1300f116-f07e-427f-b2ef-c66643994577. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
With domain name format instead we have we are able to authenticate with the website.
We can successfully call the PrepareAuthenticatedClient
method as per the sample to get an accessToken
private async Task PrepareAuthenticatedClient()
{
var accessToken = await this.tokenAcquisition.GetAccessTokenForUserAsync(new[] { this.clinicsSettings.Scopes });
System.Diagnostics.Debug.WriteLine($"access token-{accessToken}");
this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
this.httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
Calling tokenAcquisition.GetAccessTokenForUserAsync
results in a redirect request to be made in the browser to AAD and then the user is redirected back. Subsequent calls to get GetAccessTokenForUserAsync
all succeed with no redirect.
One we have the AccessToken we attempt to call our web service. That call is rejected from the Web Service as 401 Unauthorised. The specific response is
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: {
Transfer-Encoding: chunked Server: Microsoft-IIS/10.0
WWW-Authenticate: Bearer error="invalid_token", error_description="The audience 'https://OURDOMAIN.co.uk/app.our-service' is invalid" X-Powered-By: ASP.NET Date: Wed, 21 Oct 2020 17:16:01 GMT }}
The Startup class looks like this for the Web Service
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
// Since IdentityModel version 5.2.1 (or since Microsoft.AspNetCore.Authentication.JwtBearer version 2.2.0),
// PII hiding in log files is enabled by default for GDPR concerns.
// For debugging/development purposes, one can enable additional detail in exceptions by setting IdentityModelEventSource.ShowPII to true.
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Can anyone help explain why the accessToken is not enough to get a successful call to the webservice?