J'ai créé un nouveau projet d'application Web ASP.NET Core dans VS17 en utilisant le modèle "Application Web (Model-View-Controller)" et ".Net Framework" + "ASP.NET Core 2" comme configuration. La configuration d'authentification est définie sur "Comptes d'utilisateurs individuels".
J'ai l'exemple de point de terminaison suivant:
[Produces("application/json")]
[Route("api/price")]
[Authorize(Roles = "PriceViwer", AuthenticationSchemes = "Cookies,Bearer")]
public class PriceController : Controller
{
public IActionResult Get()
{
return Ok(new Dictionary<string, string> { {"Galleon/Pound",
"999.999" } );
}
}
"Cookies,Bearer"
Est dérivé en concaténant CookieAuthenticationDefaults.AuthenticationScheme
Et JwtBearerDefaults.AuthenticationScheme
.
L'objectif est de pouvoir configurer l'autorisation pour le point final afin qu'il soit possible d'y accéder en utilisant à la fois les méthodes d'authentification par jeton et cookie.
Voici la configuration que j'ai pour l'authentification dans mon Startup.cs:
services.AddAuthentication()
.AddCookie(cfg => { cfg.SlidingExpiration = true;})
.AddJwtBearer(cfg => {
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters() {
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
};
});
Ainsi, lorsque j'essaie d'accéder au point de terminaison à l'aide d'un navigateur, j'obtiens la réponse 401 avec une page html vierge.
Ensuite, je me connecte et lorsque j'essaie d'accéder à nouveau au point de terminaison, j'obtiens la même réponse.
Ensuite, j'essaie d'accéder au point de terminaison en spécifiant le jeton du porteur. Et cela renvoie le résultat souhaité avec la réponse 200.
Donc, si je supprime [Authorize(AuthenticationSchemes = "Cookies,Bearer")]
, la situation devient l'inverse - l'authentification par cookie fonctionne et renvoie 200, mais la même méthode de jeton porteur que celle utilisée ci-dessus ne donne aucun résultat et redirige simplement vers la connexion AspIdentity par défaut page.
Je peux voir deux problèmes possibles ici:
1) ASP.NET Core n'autorise pas l'authentification "combinée". 2) "Cookies" n'est pas un nom de schéma valide. Mais alors quel est le bon à utiliser?
S'il vous plaît donnez votre avis. Je vous remercie.
Je pense que vous n'avez pas besoin de définir le système d'authentification sur votre contrôleur. Utilisez simplement l'utilisateur authentifié dans ConfigureServices comme ceci:
// requires: using Microsoft.AspNetCore.Authorization;
// using Microsoft.AspNetCore.Mvc.Authorization;
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
Pour la documentation de mes sources: registerAuthorizationHandlers
Pour la partie, si le schéma-Key n'était pas valide, vous pouvez utiliser une chaîne interpolée, pour utiliser les bonnes clés:
[Authorize(AuthenticationSchemes = $"{CookieAuthenticationDefaults.AuthenticationScheme},{JwtBearerDefaults.AuthenticationScheme}")]
Edit: J'ai fait d'autres recherches et suis arrivé à la conclusion suivante: il n'est pas possible d'autoriser une méthode avec deux schémas Or-Like, mais vous pouvez utiliser deux méthodes publiques, pour appeler une méthode privée comme celle-ci:
//private method
private IActionResult GetThingPrivate()
{
//your Code here
}
//Jwt-Method
[Authorize(AuthenticationSchemes = $"{JwtBearerDefaults.AuthenticationScheme}")]
[HttpGet("bearer")]
public IActionResult GetByBearer()
{
return GetThingsPrivate();
}
//Cookie-Method
[Authorize(AuthenticationSchemes = $"{CookieAuthenticationDefaults.AuthenticationScheme}")]
[HttpGet("cookie")]
public IActionResult GetByCookie()
{
return GetThingsPrivate();
}
Si je comprends bien la question, je pense qu'il existe une solution. Dans l'exemple suivant, j'utilise l'authentification par cookie ET au porteur dans une seule application. L'attribut [Authorize]
Peut être utilisé sans spécifier le schéma, et l'application réagira dynamiquement, selon la méthode d'autorisation utilisée.
services.AddAuthentication
Est appelé deux fois pour enregistrer les 2 schémas d'authentification. La clé de la solution est l'appel à services.AddAuthorization
À la fin de l'extrait de code, qui indique à ASP.NET d'utiliser les DEUX schémas .
J'ai testé cela et cela semble bien fonctionner.
(Basé sur Microsoft docs .)
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:4991";
options.RequireHttpsMetadata = false;
options.ClientId = "WebApp";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.Scope.Add("api");
options.SaveTokens = true;
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:4991";
options.RequireHttpsMetadata = false;
// name of the API resource
options.Audience = "api";
});
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
CookieAuthenticationDefaults.AuthenticationScheme,
JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
[~ # ~] modifier [~ # ~]
Cela fonctionne pour les utilisateurs authentifiés, mais renvoie simplement un 401 (non autorisé) si un utilisateur ne s'est pas encore connecté.
Pour vous assurer que les utilisateurs non autorisés sont redirigés vers la page de connexion, ajoutez le code suivant à la méthode Configure
dans votre classe de démarrage. Remarque: il est essentiel que le nouveau middleware soit placé après l'appel de la app.UseAuthentication()
.
app.UseAuthentication();
app.Use(async (context, next) =>
{
await next();
var bearerAuth = context.Request.Headers["Authorization"]
.FirstOrDefault()?.StartsWith("Bearer ") ?? false;
if (context.Response.StatusCode == 401
&& !context.User.Identity.IsAuthenticated
&& !bearerAuth)
{
await context.ChallengeAsync("oidc");
}
});
Si vous connaissez un moyen plus propre de réaliser cette redirection, veuillez poster un commentaire!