Autoriser avec un schéma spécifique dans ASP.NET Core
Pour une présentation des schémas d’authentification dans ASP.NET Core, consultez Schéma d’authentification.
Dans certains scénarios, tels que les applications monopage (SPAs), il est courant d’utiliser plusieurs méthodes d’authentification. Par exemple, l’application peut utiliser cookiel’authentification basée sur la connexion et l’authentification du porteur JWT pour les requêtes JavaScript. Dans certains cas, l’application peut avoir plusieurs instances d’un gestionnaire d’authentification. Par exemple, deux gestionnaires de cookie où l’un contient une identity de base et l’autre est créé lorsqu’une authentification multifacteur (MFA) a été déclenchée. L’authentification multifacteur peut être déclenchée parce que l’utilisateur a demandé une opération qui nécessite une sécurité supplémentaire. Pour plus d’informations sur l’application de l’authentification multifacteur lorsqu’un utilisateur demande une ressource qui nécessite l’authentification multifacteur, consultez la section Protection des problèmes GitHub avec MFA.
Un schéma d’authentification est nommé lorsque le service d’authentification est configuré pendant l’authentification. Par exemple :
using Microsoft.AspNetCore.Authentication;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication()
.AddCookie(options =>
{
options.LoginPath = "/Account/Unauthorized/";
options.AccessDeniedPath = "/Account/Forbidden/";
})
.AddJwtBearer(options =>
{
options.Audience = "http://localhost:5001/";
options.Authority = "http://localhost:5000/";
});
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
Dans le code précédent, deux gestionnaires d’authentification ont été ajoutés : un pour les cookies et un pour le porteur.
Remarque
La spécification du schéma par défaut entraîne la définition de la propriété HttpContext.User
sur cette identity. Si ce comportement n’est pas souhaité, désactivez-le en appelant la forme sans paramètre de AddAuthentication
.
Sélection du schéma avec l’attribut Autoriser
Au point d’autorisation, l’application indique le gestionnaire à utiliser. Sélectionnez le gestionnaire avec lequel l’application autorisera en transmettant une liste délimitée par des virgules de schémas d’authentification à [Authorize]
. L’attribut [Authorize]
spécifie le ou les schémas d’authentification à utiliser, qu’une valeur par défaut soit configurée ou non. Par exemple :
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
namespace AuthScheme.Controllers;
[Authorize(AuthenticationSchemes = AuthSchemes)]
public class MixedController : Controller
{
private const string AuthSchemes =
CookieAuthenticationDefaults.AuthenticationScheme + "," +
JwtBearerDefaults.AuthenticationScheme;
public ContentResult Index() => Content(MyWidgets.GetMyContent());
}
Dans l’exemple précédent, les gestionnaires et le porteur de cookie s’exécutent et ont la possibilité de créer et d’ajouter une identity pour l’utilisateur actuel. En spécifiant un seul schéma, le gestionnaire correspondant exécute :
[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]
public class Mixed2Controller : Controller
{
public ContentResult Index() => Content(MyWidgets.GetMyContent());
}
Dans le code précédent, seul le gestionnaire avec le schéma « Porteur » s’exécute. Toutes les identités basées cookie sont ignorées.
Sélection du schéma avec des stratégies
Si vous préférez spécifier les schémas souhaités dans la stratégie, vous pouvez définir la collection lors de l’ajout AuthenticationSchemes d’une stratégie :
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Over18", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.Requirements.Add(new MinimumAgeRequirement(18));
});
});
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
Dans l’exemple précédent, la stratégie « Over18 » s’exécute uniquement sur l’identity créée par le gestionnaire « Porteur ». Utilisez la stratégie en définissant la propriété [Authorize]
de l’attribut Policy
:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AuthScheme.Controllers;
[Authorize(Policy = "Over18")]
public class RegistrationController : Controller
{
// Do Registration
Utiliser plusieurs schémas d’authentification
Certaines applications peuvent avoir besoin de prendre en charge plusieurs types d’authentification. Par exemple, votre application peut authentifier des utilisateurs à partir d’Azure Active Directory et d’une base de données d’utilisateurs. Un autre exemple est une application qui authentifie les utilisateurs de Services ADFS et d’Azure Active Directory B2C. Dans ce cas, l’application doit accepter un jeton du porteur JWT de plusieurs émetteurs.
Ajoutez tous les schémas d’authentification que vous souhaitez accepter. Par exemple, le code suivant ajoute deux schémas d’authentification du porteur JWT avec différents émetteurs :
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://localhost:5000/identity/";
})
.AddJwtBearer("AzureAD", options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://login.microsoftonline.com/eb971100-7f436/";
});
// Authorization
builder.Services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
"AzureAD");
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
Notes
Une seule authentification du porteur JWT est inscrite avec le schéma d’authentification par défaut JwtBearerDefaults.AuthenticationScheme
. Une authentification supplémentaire doit être inscrite avec un schéma d’authentification unique.
Mettez à jour la stratégie d’autorisation par défaut pour accepter les deux schémas d’authentification. Par exemple :
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://localhost:5000/identity/";
})
.AddJwtBearer("AzureAD", options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://login.microsoftonline.com/eb971100-7f436/";
});
// Authorization
builder.Services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
"AzureAD");
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
Comme la stratégie d’autorisation par défaut est remplacée, il est possible d’utiliser l’attribut dans les contrôleurs [Authorize]
. Le contrôleur accepte ensuite les demandes avec JWT émises par le premier ou le deuxième émetteur.
Consultez ce problème GitHub sur l’utilisation de plusieurs schémas d’authentification.
L’exemple suivant utilise Azure Active Directory B2C et un autre locataire Azure Active Directory :
using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using System.IdentityModel.Tokens.Jwt;
var builder = WebApplication.CreateBuilder(args);
// Authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "B2C_OR_AAD";
options.DefaultChallengeScheme = "B2C_OR_AAD";
})
.AddJwtBearer("B2C", jwtOptions =>
{
jwtOptions.MetadataAddress = "B2C-MetadataAddress";
jwtOptions.Authority = "B2C-Authority";
jwtOptions.Audience = "B2C-Audience";
})
.AddJwtBearer("AAD", jwtOptions =>
{
jwtOptions.MetadataAddress = "AAD-MetadataAddress";
jwtOptions.Authority = "AAD-Authority";
jwtOptions.Audience = "AAD-Audience";
jwtOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("ValidAudiences").Get<string[]>(),
ValidIssuers = builder.Configuration.GetSection("ValidIssuers").Get<string[]>()
};
})
.AddPolicyScheme("B2C_OR_AAD", "B2C_OR_AAD", options =>
{
options.ForwardDefaultSelector = context =>
{
string authorization = context.Request.Headers[HeaderNames.Authorization];
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
{
var token = authorization.Substring("Bearer ".Length).Trim();
var jwtHandler = new JwtSecurityTokenHandler();
return (jwtHandler.CanReadToken(token) && jwtHandler.ReadJwtToken(token).Issuer.Equals("B2C-Authority"))
? "B2C" : "AAD";
}
return "AAD";
};
});
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute().RequireAuthorization();
app.MapRazorPages().RequireAuthorization();
app.MapFallbackToFile("index.html");
app.Run();
Dans le code précédent, ForwardDefaultSelector est utilisé pour sélectionner un schéma par défaut pour la demande actuelle à laquelle les gestionnaires d’authentification doivent transférer toutes les opérations d’authentification par défaut. La logique de transfert par défaut vérifie d’abord le paramètre le plus spécifique ForwardAuthenticate, ForwardChallenge, ForwardForbid, ForwardSignIn, etForwardSignOut, suivi de la vérification de ForwardDefaultSelector, suivi de ForwardDefault. Le premier résultat non null est utilisé comme schéma cible vers laquelle effectuer le transfert. Pour plus d’informations, consultez Schémas de politique dans ASP.NET Core.
Pour une présentation des schémas d’authentification dans ASP.NET Core, consultez Schéma d’authentification.
Dans certains scénarios, tels que les applications monopage (SPAs), il est courant d’utiliser plusieurs méthodes d’authentification. Par exemple, l’application peut utiliser cookiel’authentification basée sur la connexion et l’authentification du porteur JWT pour les requêtes JavaScript. Dans certains cas, l’application peut avoir plusieurs instances d’un gestionnaire d’authentification. Par exemple, deux gestionnaires de cookie où l’un contient une identity de base et l’autre est créé lorsqu’une authentification multifacteur (MFA) a été déclenchée. L’authentification multifacteur peut être déclenchée parce que l’utilisateur a demandé une opération qui nécessite une sécurité supplémentaire. Pour plus d’informations sur l’application de l’authentification multifacteur lorsqu’un utilisateur demande une ressource qui nécessite l’authentification multifacteur, consultez la section Protection des problèmes GitHub avec MFA.
Un schéma d’authentification est nommé lorsque le service d’authentification est configuré pendant l’authentification. Par exemple :
public void ConfigureServices(IServiceCollection services)
{
// Code omitted for brevity
services.AddAuthentication()
.AddCookie(options => {
options.LoginPath = "/Account/Unauthorized/";
options.AccessDeniedPath = "/Account/Forbidden/";
})
.AddJwtBearer(options => {
options.Audience = "http://localhost:5001/";
options.Authority = "http://localhost:5000/";
});
Dans le code précédent, deux gestionnaires d’authentification ont été ajoutés : un pour les cookies et un pour le porteur.
Remarque
La spécification du schéma par défaut entraîne la définition de la propriété HttpContext.User
sur cette identity. Si ce comportement n’est pas souhaité, désactivez-le en appelant la forme sans paramètre de AddAuthentication
.
Sélection du schéma avec l’attribut Autoriser
Au point d’autorisation, l’application indique le gestionnaire à utiliser. Sélectionnez le gestionnaire avec lequel l’application autorisera en transmettant une liste délimitée par des virgules de schémas d’authentification à [Authorize]
. L’attribut [Authorize]
spécifie le ou les schémas d’authentification à utiliser, qu’une valeur par défaut soit configurée ou non. Par exemple :
[Authorize(AuthenticationSchemes = AuthSchemes)]
public class MixedController : Controller
// Requires the following imports:
// using Microsoft.AspNetCore.Authentication.Cookies;
// using Microsoft.AspNetCore.Authentication.JwtBearer;
private const string AuthSchemes =
CookieAuthenticationDefaults.AuthenticationScheme + "," +
JwtBearerDefaults.AuthenticationScheme;
Dans l’exemple précédent, les gestionnaires et le porteur de cookie s’exécutent et ont la possibilité de créer et d’ajouter une identity pour l’utilisateur actuel. En spécifiant un seul schéma, le gestionnaire correspondant exécute.
[Authorize(AuthenticationSchemes =
JwtBearerDefaults.AuthenticationScheme)]
public class MixedController : Controller
Dans le code précédent, seul le gestionnaire avec le schéma « Porteur » s’exécute. Toutes les identités basées cookie sont ignorées.
Sélection du schéma avec des stratégies
Si vous préférez spécifier les schémas souhaités dans la stratégie, vous pouvez définir la collection lors de l’ajout AuthenticationSchemes
de votre stratégie :
services.AddAuthorization(options =>
{
options.AddPolicy("Over18", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.Requirements.Add(new MinimumAgeRequirement());
});
});
Dans l’exemple précédent, la stratégie « Over18 » s’exécute uniquement sur l’identity créée par le gestionnaire « Porteur ». Utilisez la stratégie en définissant la propriété [Authorize]
de l’attribut Policy
:
[Authorize(Policy = "Over18")]
public class RegistrationController : Controller
Utiliser plusieurs schémas d’authentification
Certaines applications peuvent avoir besoin de prendre en charge plusieurs types d’authentification. Par exemple, votre application peut authentifier des utilisateurs à partir d’Azure Active Directory et d’une base de données d’utilisateurs. Un autre exemple est une application qui authentifie les utilisateurs de Services ADFS et d’Azure Active Directory B2C. Dans ce cas, l’application doit accepter un jeton du porteur JWT de plusieurs émetteurs.
Ajoutez tous les schémas d’authentification que vous souhaitez accepter. Par exemple, le code suivant dans Startup.ConfigureServices
ajoute deux schémas d’authentification du porteur JWT avec différents émetteurs :
public void ConfigureServices(IServiceCollection services)
{
// Code omitted for brevity
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://localhost:5000/identity/";
})
.AddJwtBearer("AzureAD", options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://login.microsoftonline.com/eb971100-6f99-4bdc-8611-1bc8edd7f436/";
});
}
Notes
Une seule authentification du porteur JWT est inscrite avec le schéma d’authentification par défaut JwtBearerDefaults.AuthenticationScheme
. Une authentification supplémentaire doit être inscrite avec un schéma d’authentification unique.
L’étape suivante consiste à mettre à jour la stratégie d’autorisation par défaut pour accepter les deux schémas d’authentification. Par exemple :
public void ConfigureServices(IServiceCollection services)
{
// Code omitted for brevity
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
"AzureAD");
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
}
Comme la stratégie d’autorisation par défaut est remplacée, il est possible d’utiliser l’attribut dans les contrôleurs [Authorize]
. Le contrôleur accepte ensuite les demandes avec JWT émises par le premier ou le deuxième émetteur.
Consultez ce problème GitHub sur l’utilisation de plusieurs schémas d’authentification.