Perché Http 401 quando si chiama l'API AspNet Core protetta con AAD. La nostra WebApp, API Web e AD Org
Ho seguito questo esempio di Azure per proteggere un'app Web AspNet a una WebApi di nostra proprietà e protetta con la nostra organizzazione Azure Active Directory
Abbiamo un sito AspNet esistente che è già protetto con Azure Active Directory, quindi sto solo cercando di inserire il nostro equivalente del TodoListService di esempio.
L'esempio utilizza MSAL, quindi abbiamo spostato il sito per utilizzarlo. Il metodo ConfigureServices nell'avvio del sito Web è
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();
}
L'esempio dice che gli ambiti devono essere nel formato api: // <client_id> / scope_name ma, come puoi vedere, il nostro nome dell'ambito è il dominio tenant AD più l'ambito. I tentativi di utilizzare l'ID client hanno generato questo errore
OpenIdConnectProtocolException: il messaggio contiene l'errore: 'invalid_resource', error_description: 'AADSTS500011: l'entità risorsa denominata api: // 4f3ca2ab-d7dc-401a-a514-37744ab3555f non è stata trovata nel tenant denominato 1300f116-f07e-4274566-b2ef Ciò può accadere se l'applicazione non è stata installata dall'amministratore del tenant o non è stata autorizzata da alcun utente nel tenant. Potresti aver inviato la tua richiesta di autenticazione al tenant sbagliato.
Con il formato del nome di dominio invece abbiamo la possibilità di autenticarci con il sito web.
Possiamo chiamare con successo il PrepareAuthenticatedClientmetodo come da esempio per ottenere un 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"));
}
La chiamata tokenAcquisition.GetAccessTokenForUserAsynccomporta una richiesta di reindirizzamento da effettuare nel browser ad AAD e quindi l'utente viene reindirizzato. Chiamate successive per avere GetAccessTokenForUserAsyncsuccesso senza reindirizzamento.
Uno abbiamo l'AccessToken che proviamo a chiamare il nostro servizio web. La chiamata viene rifiutata dal servizio Web come 401 non autorizzata. La risposta specifica è
{StatusCode: 401, ReasonPhrase: "Unauthorized", Versione: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: {
Transfer-Encoding: chunked Server: Microsoft-IIS / 10.0
WWW-Authenticate: Bearer error = "invalid_token" , error_description = "Il pubblico" https://OURDOMAIN.co.uk/app.our-service "non è valido" X-Powered-By: ASP.NET Date: Wed, 21 Oct 2020 17:16:01 GMT}}
La classe Startup ha questo aspetto per il servizio Web
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();
});
}
Qualcuno può aiutare a spiegare perché accessToken non è sufficiente per ottenere una chiamata riuscita al servizio web?
Risposte
Per un token di accesso v2.0, il formato dell'ambito è "api: // {ClientIdOfWebApi} / scope" a meno che il tenant non disponga di un dominio verificato, nel qual caso sarà "tuodominio / ambito"
Puoi saperlo guardando l'URI AppId nel portale di registrazione dell'app.
A seconda della versione del token, Microsoft.Identity.Web controlla il pubblico della forma sopra, ma quando hai il tuo dominio, devi aggiungere:
"Pubblico": "tuodominio"
in appsettings.json della tua API Web.
Vedere: https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-configuration#case-where-you-used-a-custom-app-id-uri-for-your-web-api