Partage via


Authentification multifacteur dans ASP.NET Core

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.

Avertissement

Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

Pour la version actuelle, consultez la version .NET 8 de cet article.

Par Damien Bowden

Afficher ou télécharger un exemple de code (référentiel GitHub damienbod/AspNetCoreHybridFlowWithApi)

L’authentification MFA (Azure Multi-Factor Authentication) est un processus dans lequel un utilisateur est invité, au cours d’un événement de connexion, à utiliser des formes d’identification supplémentaires. Il peut s'agir de saisir un code à partir d'un téléphone portable, d'utiliser une clé FIDO2 ou de scanner une empreinte digitale. Lorsque vous demandez un deuxième formulaire d’authentification, la sécurité est améliorée. Le facteur supplémentaire n’est pas facilement obtenu ou dupliqué par un cyberattaquant.

Cet article aborde les zones suivantes :

  • Qu’est-ce que l’authentification multifacteur et quels flux MFA sont recommandés
  • Configurer l’authentification multifacteur pour les pages d’administration à l’aide de ASP.NET Core Identity
  • Envoyer une exigence de connexion MFA au serveur OpenID Connect
  • Forcer ASP.NET Core client OpenID Connect à exiger l’authentification multifacteur

MFA, 2FA

L’authentification multifacteur nécessite au moins deux types de preuves pour une identity, telles que quelque chose que vous connaissez, quelque chose que vous possédez ou une validation biométrique pour que l’utilisateur s’authentifie.

L’authentification à deux facteurs (2FA) est un type d’authentification multifacteur, mais la principale différence réside dans le fait que l’authentification multifacteur peut nécessiter au moins deux éléments pour vérifier l’identity.

L’authentification 2 est prise en charge par défaut lors de l’utilisation de ASP.NET Core Identity. Pour activer ou désactiver la 2FA pour un utilisateur spécifique, définissez la propriété IdentityUser<TKey>.TwoFactorEnabled. L’interface utilisateur par défaut ASP.NET Core Identity inclut des pages pour la configuration de l’authentification 2FA.

TOTP MFA (Algorithme de mot de passe à usage unique basé sur le temps)

L’authentification multifacteur à l’aide de TOTP est prise en charge par défaut lors de l’utilisation de ASP.NET Core Identity. Cette approche peut être utilisée avec n’importe quelle application d’authentificateur conforme, notamment :

  • Microsoft Authenticator
  • Google Authenticator

Pour plus d’informations sur l’implémentation, consultez Activer la génération de code QR pour les applications d’authentificateur TOTP dans ASP.NET Core.

Pour désactiver la prise en charge de MFA TOTP, configurez l’authentification à l’aide de AddIdentity au lieu de AddDefaultIdentity. AddDefaultIdentity appelle AddDefaultTokenProviders en interne, ce qui inscrit plusieurs fournisseurs de jetons, dont un pour MFA TOTP. Pour inscrire uniquement des fournisseurs de jetons spécifiques, appelez AddTokenProvider pour chaque fournisseur requis. Pour plus d’informations sur les fournisseurs de jetons disponibles, consultez la source AddDefaultTokenProviders sur GitHub.

Clés d’accès MFA/FIDO2 ou sans mot de passe

Les clés d’accès/FIDO2 sont actuellement :

  • Le moyen le plus sûr d’obtenir l’authentification multifacteur.
  • Authentification multifacteur qui offre une protection contre les attaques par hameçonnage. (Ainsi que l’authentification par certificat et Windows pour les entreprises)

Actuellement, ASP.NET Core ne prend pas directement en charge FIDO2. Des clés d’accès/FIDO2 peut être utilisé pour les flux d’authentification multifacteur (MFA) ou sans mot de passe.

Microsoft Entra ID prend en charge les flux sans mot de passe/FIDO2 et sans mot de passe. Pour plus d’informations, consultez options d’authentification sans mot de passe.

D’autres formes d’authentification multifacteur sans mot de passe n’offrent pas de protection ou risquent de ne pas offrir de protection contre le hameçonnage.

MFA SMS

L’authentification multifacteur avec SMS augmente massivement la sécurité par rapport à l’authentification par mot de passe (facteur unique). Toutefois, l’utilisation des SMS comme deuxième facteur n’est plus recommandée. Trop de vecteurs d’attaque connus existent pour ce type d’implémentation.

Recommandations NIST

Configurer l’authentification multifacteur pour les pages d’administration à l’aide de ASP.NET Core Identity

L’authentification multifacteur peut être forcée sur les utilisateurs à accéder aux pages sensibles au sein d’une application ASP.NET CoreIdentity. Cela peut être utile pour les applications où différents niveaux d’accès existent pour les différentes identités. Par exemple, les utilisateurs peuvent être en mesure d’afficher les données de profil à l’aide d’une connexion par mot de passe, mais un administrateur doit utiliser l’authentification multifacteur pour accéder aux pages d’administration.

Étendre la connexion avec une revendication MFA

Le code de démonstration est configuré à l’aide de ASP.NET Core avec Pages Identity et Razor. La méthode AddIdentity est utilisée à la place de la méthode AddDefaultIdentity, de sorte qu’une implémentation IUserClaimsPrincipalFactory peut être utilisée pour ajouter des réclamations à l’identity après une connexion réussie.

Avertissement

Cet article montre l’utilisation de chaîne de connexion s. Avec une base de données locale, l’utilisateur n’a pas besoin d’être authentifié, mais en production, les chaîne de connexion incluent parfois un mot de passe pour s’authentifier. Les informations d’identification de mot de passe du propriétaire de la ressource (ROPC) constituent un risque de sécurité qui doit être évité dans les bases de données de production. Les applications de production doivent utiliser le flux d’authentification le plus sécurisé disponible. Pour plus d’informations sur l’authentification des applications déployées pour tester ou produire des environnements, consultez Flux d’authentification sécurisés.

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(
        Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
		options.SignIn.RequireConfirmedAccount = false)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddSingleton<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, 
    AdditionalUserClaimsPrincipalFactory>();

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled", x => x.RequireClaim("amr", "mfa")));

builder.Services.AddRazorPages();

La classe AdditionalUserClaimsPrincipalFactory ajoute la revendication amr aux revendications utilisateur uniquement après une connexion réussie. La valeur de la revendication est lue à partir de la base de données. La revendication est ajoutée ici, car l’utilisateur ne doit accéder à la vue protégée supérieure que si l’identity s’est connectée avec l’authentification multifacteur. Si la vue de base de données est lue directement à partir de la base de données au lieu d’utiliser la revendication, il est possible d’accéder à la vue sans authentification multifacteur directement après l’activation de l’authentification multifacteur.

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : 
        UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;

            var claims = new List<Claim>();

            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("amr", "mfa"));
            }
            else
            {
                claims.Add(new Claim("amr", "pwd"));
            }

            identity.AddClaims(claims);
            return principal;
        }
    }
}

Étant donné que la configuration du service Identity a changé dans la classe Startup, les dispositions de Identity doivent être mises à jour. Structurez les pages Identity dans l’application. Définissez la disposition dans le fichier Identity/Account/Manage/_Layout.cshtml.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Affectez également la mise en page pour toutes les pages de gestion des pages Identity :

@{
    Layout = "_Layout.cshtml";
}

Valider l’exigence MFA dans la page d’administration

La page d’administration Razor vérifie que l’utilisateur s’est connecté à l’aide de l’authentification multifacteur. Dans la méthode OnGet, l’identity est utilisée pour accéder aux revendications de l’utilisateur. La revendication amr est vérifiée pour la valeur mfa. Si l’identity est manquante dans cette revendication ou est false, la page redirige vers la page d’activation de l’authentification multifacteur. Cela est possible, car l’utilisateur s’est déjà connecté, mais sans MFA.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityStandaloneMfa
{
    public class AdminModel : PageModel
    {
        public IActionResult OnGet()
        {
            var claimTwoFactorEnabled = 
                User.Claims.FirstOrDefault(t => t.Type == "amr");

            if (claimTwoFactorEnabled != null && 
                "mfa".Equals(claimTwoFactorEnabled.Value))
            {
                // You logged in with MFA, do the administrative stuff
            }
            else
            {
                return Redirect(
                    "/Identity/Account/Manage/TwoFactorAuthentication");
            }

            return Page();
        }
    }
}

Logique d’interface utilisateur pour activer/désactiver les informations de connexion utilisateur

Une stratégie d’autorisation a été ajoutée au démarrage. La stratégie nécessite la revendication amr avec la valeur mfa.

services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Cette stratégie peut ensuite être utilisée dans la vue _Layout pour afficher ou masquer le menu Administration avec l’avertissement :

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Si l’identity s’est connectée à l’aide de l’authentification multifacteur, le menu Administrateur s’affiche sans l’avertissement d’info-bulle. Lorsque l’utilisateur s’est connecté sans MFA, le menu Administration (Non activé) s’affiche avec l’info-bulle qui l’informe (expliquant l’avertissement).

@if (SignInManager.IsSignedIn(User))
{
    @if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin" 
               id="tooltip-demo"  
               data-toggle="tooltip" 
               data-placement="bottom" 
               title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
                Admin (Not Enabled)
            </a>
        </li>
    }
}

Si l’utilisateur se connecte sans MFA, l’avertissement s’affiche :

Authentification MFA administrateur

L’utilisateur est redirigé vers l’affichage d’activation de l’authentification multifacteur lorsqu’il clique sur le lien Administration :

L’administrateur active l’authentification MFA

Envoyer une exigence de connexion MFA au serveur OpenID Connect

Le paramètre acr_values peut être utilisé pour passer la valeur mfa requise du client au serveur dans une demande d’authentification.

Remarque

Le paramètre acr_values doit être géré sur le serveur OpenID Connect pour que cela fonctionne.

Client OpenID Connect ASP.NET Core

L’application cliente ASP.NET Core Razor Pages OpenID Connect utilise la méthode AddOpenIdConnect pour se connecter au serveur OpenID Connect. Le paramètre acr_values est défini avec la valeur mfa et envoyé avec la demande d’authentification. Le OpenIdConnectEvents est utilisé pour ajouter ceci.

Pour connaître les valeurs de paramètre recommandées acr_values, consultez Valeurs de référence de méthode d’authentification.

build.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "<OpenID Connect server URL>";
	options.RequireHttpsMetadata = true;
	options.ClientId = "<OpenID Connect client ID>";
	options.ClientSecret = "<>";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
	options.AdditionalAuthorizationParameters.Add("acr_values", "mfa");
});

Exemple de serveur OpenID Connect Duende IdentityServer avec ASP.NET Core Identity

Sur le serveur OpenID Connect, qui est implémenté à l’aide d’ASP.NET Core Identity avec des Razor pages, une nouvelle page nommée ErrorEnable2FA.cshtml est créée. Cette vie :

  • Affiche si Identity provient d’une application qui nécessite l’authentification multifacteur, mais que l’utilisateur n’a pas activé cette option dans Identity.
  • Informe l’utilisateur et ajoute un lien pour l’activer.
@{
    ViewData["Title"] = "ErrorEnable2FA";
}

<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>

<br />

You can enable MFA to login here:

<br />

<a href="~/Identity/Account/Manage/TwoFactorAuthentication">Enable MFA</a>

Dans la méthode Login, l’implémentation IIdentityServerInteractionService de l’interface _interaction est utilisée pour accéder aux paramètres de requête OpenID Connect. Le paramètre acr_values est accessible à l’aide de la propriété AcrValues. Comme le client l’a envoyé avec mfa défini, cela peut ensuite être vérifié.

Si l’authentification multifacteur est requise et que l’authentification multifacteur est activée pour l’utilisateur dans ASP.NET CoreIdentity, la connexion continue. Lorsque l’utilisateur n’a pas d’authentification multifacteur activée, l’utilisateur est redirigé vers la vue ErrorEnable2FA.cshtmlpersonnalisée . Ensuite, ASP.NET Core Identity connecte l’utilisateur.

Fido2Store sert à vérifier si l’utilisateur a activé l’authentification multifacteur à l’aide d’un fournisseur de jetons FIDO2 personnalisé.

public async Task<IActionResult> OnPost()
{
	// check if we are in the context of an authorization request
	var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);

	var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

	var user = await _userManager.FindByNameAsync(Input.Username);
	if (user != null && !user.TwoFactorEnabled && requires2Fa)
	{
		return RedirectToPage("/Home/ErrorEnable2FA/Index");
	}

	// code omitted for brevity

	if (ModelState.IsValid)
	{
		var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
		if (result.Succeeded)
		{
			// code omitted for brevity
		}
		if (result.RequiresTwoFactor)
		{
			var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
			if (fido2ItemExistsForUser.Count > 0)
			{
				return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
			}

			return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
		}

		await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
		ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
	}

	// something went wrong, show form with error
	await BuildModelAsync(Input.ReturnUrl);
	return Page();
}

Si l’utilisateur est déjà connecté, l’application cliente :

  • Valide toujours la revendication amr.
  • Peut configurer l’authentification multifacteur avec un lien vers la vue ASP.NET CoreIdentity.

acr_values-1 image

Forcer ASP.NET Core client OpenID Connect à exiger l’authentification multifacteur

Cet exemple montre comment une application page ASP.NET CoreRazor, qui utilise OpenID Connect pour se connecter, peut exiger que les utilisateurs se soient authentifiés à l’aide de l’authentification multifacteur.

Pour valider l’exigence MFA, une exigence IAuthorizationRequirement est créée. Cela sera ajouté aux pages à l’aide d’une stratégie qui nécessite l’authentification multifacteur.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

Un AuthorizationHandler est implémenté qui utilise la revendication amr et case activée pour la valeur mfa. Le amr est retourné dans le id_token d’une authentification réussie et peut avoir de nombreuses valeurs différentes telles que définies dans la spécification valeurs de référence de la méthode d’authentification .

La valeur retournée dépend de la façon dont l’identity s’est authentifiée et de l’implémentation du serveur OpenID Connect.

Le AuthorizationHandler utilise l’exigence RequireMfa et valide la revendication amr. Le serveur OpenID Connect peut être implémenté à l’aide du serveur Duende Identity avec ASP.NET Core Identity. Lorsqu’un utilisateur se connecte à l’aide de TOTP, la revendication amr est retournée avec une valeur MFA. Si vous utilisez une autre implémentation de serveur OpenID Connect ou un autre type d’authentification multifacteur, la amr revendication aura ou peut avoir une valeur différente. Le code doit être étendu pour l’accepter également.

public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
	protected override Task HandleRequirementAsync(
		AuthorizationHandlerContext context, 
		RequireMfa requirement)
	{
		if (context == null)
			throw new ArgumentNullException(nameof(context));
		if (requirement == null)
			throw new ArgumentNullException(nameof(requirement));

		var amrClaim =
			context.User.Claims.FirstOrDefault(t => t.Type == "amr");

		if (amrClaim != null && amrClaim.Value == Amr.Mfa)
		{
			context.Succeed(requirement);
		}

		return Task.CompletedTask;
	}
}

Dans le fichier programme, la méthode AddOpenIdConnectest utilisée comme schéma de contestation par défaut. Le gestionnaire d’autorisations, utilisé pour vérifier la revendication amr, est ajouté au conteneur Inversion of Control. Une stratégie est ensuite créée qui ajoute l’exigence RequireMfa.

builder.Services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

builder.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "https://localhost:44352";
	options.RequireHttpsMetadata = true;
	options.ClientId = "AspNetCoreRequireMfaOidc";
	options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
	options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
	{
		policyIsAdminRequirement.Requirements.Add(new RequireMfa());
	});
});

builder.Services.AddRazorPages();

Cette stratégie est ensuite utilisée dans la page Razor en fonction des besoins. La stratégie peut également être ajoutée globalement pour l’ensemble de l’application.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Si l’utilisateur s’authentifie sans MFA, la revendication amr aura probablement une valeur pwd. La demande ne sera pas autorisée à accéder à la page. À l’aide des valeurs par défaut, l’utilisateur est redirigé vers la page Account/AccessDenied. Ce comportement peut être modifié ou vous pouvez implémenter votre propre logique personnalisée ici. Dans cet exemple, un lien est ajouté afin que l’utilisateur valide puisse configurer l’authentification multifacteur pour son compte.

@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
    ViewData["Title"] = "AccessDenied";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>AccessDenied</h1>

You require MFA to login here

<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>

Désormais, seuls les utilisateurs qui s’authentifient auprès de l’authentification multifacteur peuvent accéder à la page ou au site web. Si différents types d’authentification multifacteur sont utilisés ou si 2FA est acceptable, la revendication amr aura des valeurs différentes et doit être traitée correctement. Différents serveurs OpenID Connect retournent également des valeurs différentes pour cette revendication et peuvent ne pas suivre la spécification des valeurs de référence de la méthode d’authentification.

Lors de la connexion sans authentification multifacteur (par exemple, en utilisant simplement un mot de passe) :

  • Le amr a la pwd valeur :

    amr a la valeur pwd

  • Accès refusé :

    Accès refusé

Vous pouvez également vous connecter à l’aide de l’otP avec Identity :

Connexion à l’aide de l’otP avec Identity

Personnalisation des paramètres OAuth et OIDC

L’option AdditionalAuthorizationParameters des gestionnaires d’authentification OIDC et OAuth permet la personnalisation des paramètres de messages d’autorisation qui sont généralement inclus dans le cadre de la chaîne de requête de redirection :

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
    options.AdditionalAuthorizationParameters.Add("prompt", "login");
    options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});

Ressources supplémentaires

Par Damien Bowden

Afficher ou télécharger un exemple de code (référentiel GitHub damienbod/AspNetCoreHybridFlowWithApi)

L’authentification MFA (Azure Multi-Factor Authentication) est un processus dans lequel un utilisateur est invité, au cours d’un événement de connexion, à utiliser des formes d’identification supplémentaires. Il peut s'agir de saisir un code à partir d'un téléphone portable, d'utiliser une clé FIDO2 ou de scanner une empreinte digitale. Lorsque vous demandez un deuxième formulaire d’authentification, la sécurité est améliorée. Le facteur supplémentaire n’est pas facilement obtenu ou dupliqué par un cyberattaquant.

Cet article aborde les zones suivantes :

  • Qu’est-ce que l’authentification multifacteur et quels flux MFA sont recommandés
  • Configurer l’authentification multifacteur pour les pages d’administration à l’aide de ASP.NET Core Identity
  • Envoyer une exigence de connexion MFA au serveur OpenID Connect
  • Forcer ASP.NET Core client OpenID Connect à exiger l’authentification multifacteur

MFA, 2FA

L’authentification multifacteur nécessite au moins deux types de preuves pour une identity, telles que quelque chose que vous connaissez, quelque chose que vous possédez ou une validation biométrique pour que l’utilisateur s’authentifie.

L’authentification à deux facteurs (2FA) est un type d’authentification multifacteur, mais la principale différence réside dans le fait que l’authentification multifacteur peut nécessiter au moins deux éléments pour vérifier l’identity.

L’authentification 2 est prise en charge par défaut lors de l’utilisation de ASP.NET Core Identity. Pour activer ou désactiver la 2FA pour un utilisateur spécifique, définissez la propriété IdentityUser<TKey>.TwoFactorEnabled. L’interface utilisateur par défaut ASP.NET Core Identity inclut des pages pour la configuration de l’authentification 2FA.

TOTP MFA (Algorithme de mot de passe à usage unique basé sur le temps)

L’authentification multifacteur à l’aide de TOTP est prise en charge par défaut lors de l’utilisation de ASP.NET Core Identity. Cette approche peut être utilisée avec n’importe quelle application d’authentificateur conforme, notamment :

  • Microsoft Authenticator
  • Google Authenticator

Pour plus d’informations sur l’implémentation, consultez Activer la génération de code QR pour les applications d’authentificateur TOTP dans ASP.NET Core.

Pour désactiver la prise en charge de MFA TOTP, configurez l’authentification à l’aide de AddIdentity au lieu de AddDefaultIdentity. AddDefaultIdentity appelle AddDefaultTokenProviders en interne, ce qui inscrit plusieurs fournisseurs de jetons, dont un pour MFA TOTP. Pour inscrire uniquement des fournisseurs de jetons spécifiques, appelez AddTokenProvider pour chaque fournisseur requis. Pour plus d’informations sur les fournisseurs de jetons disponibles, consultez la source AddDefaultTokenProviders sur GitHub.

Clés d’accès MFA/FIDO2 ou sans mot de passe

Les clés d’accès/FIDO2 sont actuellement :

  • Le moyen le plus sûr d’obtenir l’authentification multifacteur.
  • Authentification multifacteur qui offre une protection contre les attaques par hameçonnage. (Ainsi que l’authentification par certificat et Windows pour les entreprises)

Actuellement, ASP.NET Core ne prend pas directement en charge FIDO2. Des clés d’accès/FIDO2 peut être utilisé pour les flux d’authentification multifacteur (MFA) ou sans mot de passe.

Microsoft Entra ID prend en charge les flux sans mot de passe/FIDO2 et sans mot de passe. Pour plus d’informations, consultez options d’authentification sans mot de passe.

D’autres formes d’authentification multifacteur sans mot de passe n’offrent pas de protection ou risquent de ne pas offrir de protection contre le hameçonnage.

MFA SMS

L’authentification multifacteur avec SMS augmente massivement la sécurité par rapport à l’authentification par mot de passe (facteur unique). Toutefois, l’utilisation des SMS comme deuxième facteur n’est plus recommandée. Trop de vecteurs d’attaque connus existent pour ce type d’implémentation.

Recommandations NIST

Configurer l’authentification multifacteur pour les pages d’administration à l’aide de ASP.NET Core Identity

L’authentification multifacteur peut être forcée sur les utilisateurs à accéder aux pages sensibles au sein d’une application ASP.NET CoreIdentity. Cela peut être utile pour les applications où différents niveaux d’accès existent pour les différentes identités. Par exemple, les utilisateurs peuvent être en mesure d’afficher les données de profil à l’aide d’une connexion par mot de passe, mais un administrateur doit utiliser l’authentification multifacteur pour accéder aux pages d’administration.

Étendre la connexion avec une revendication MFA

Le code de démonstration est configuré à l’aide de ASP.NET Core avec Pages Identity et Razor. La méthode AddIdentity est utilisée à la place de la méthode AddDefaultIdentity, de sorte qu’une implémentation IUserClaimsPrincipalFactory peut être utilisée pour ajouter des réclamations à l’identity après une connexion réussie.

Avertissement

Cet article montre l’utilisation de chaîne de connexion s. Avec une base de données locale, l’utilisateur n’a pas besoin d’être authentifié, mais en production, les chaîne de connexion incluent parfois un mot de passe pour s’authentifier. Les informations d’identification de mot de passe du propriétaire de la ressource (ROPC) constituent un risque de sécurité qui doit être évité dans les bases de données de production. Les applications de production doivent utiliser le flux d’authentification le plus sécurisé disponible. Pour plus d’informations sur l’authentification des applications déployées pour tester ou produire des environnements, consultez Flux d’authentification sécurisés.

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(
        Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
		options.SignIn.RequireConfirmedAccount = false)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddSingleton<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, 
    AdditionalUserClaimsPrincipalFactory>();

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled", x => x.RequireClaim("amr", "mfa")));

builder.Services.AddRazorPages();

La classe AdditionalUserClaimsPrincipalFactory ajoute la revendication amr aux revendications utilisateur uniquement après une connexion réussie. La valeur de la revendication est lue à partir de la base de données. La revendication est ajoutée ici, car l’utilisateur ne doit accéder à la vue protégée supérieure que si l’identity s’est connectée avec l’authentification multifacteur. Si la vue de base de données est lue directement à partir de la base de données au lieu d’utiliser la revendication, il est possible d’accéder à la vue sans authentification multifacteur directement après l’activation de l’authentification multifacteur.

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : 
        UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;

            var claims = new List<Claim>();

            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("amr", "mfa"));
            }
            else
            {
                claims.Add(new Claim("amr", "pwd"));
            }

            identity.AddClaims(claims);
            return principal;
        }
    }
}

Étant donné que la configuration du service Identity a changé dans la classe Startup, les dispositions de Identity doivent être mises à jour. Structurez les pages Identity dans l’application. Définissez la disposition dans le fichier Identity/Account/Manage/_Layout.cshtml.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Affectez également la mise en page pour toutes les pages de gestion des pages Identity :

@{
    Layout = "_Layout.cshtml";
}

Valider l’exigence MFA dans la page d’administration

La page d’administration Razor vérifie que l’utilisateur s’est connecté à l’aide de l’authentification multifacteur. Dans la méthode OnGet, l’identity est utilisée pour accéder aux revendications de l’utilisateur. La revendication amr est vérifiée pour la valeur mfa. Si l’identity est manquante dans cette revendication ou est false, la page redirige vers la page d’activation de l’authentification multifacteur. Cela est possible, car l’utilisateur s’est déjà connecté, mais sans MFA.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityStandaloneMfa
{
    public class AdminModel : PageModel
    {
        public IActionResult OnGet()
        {
            var claimTwoFactorEnabled = 
                User.Claims.FirstOrDefault(t => t.Type == "amr");

            if (claimTwoFactorEnabled != null && 
                "mfa".Equals(claimTwoFactorEnabled.Value))
            {
                // You logged in with MFA, do the administrative stuff
            }
            else
            {
                return Redirect(
                    "/Identity/Account/Manage/TwoFactorAuthentication");
            }

            return Page();
        }
    }
}

Logique d’interface utilisateur pour activer/désactiver les informations de connexion utilisateur

Une stratégie d’autorisation a été ajoutée au démarrage. La stratégie nécessite la revendication amr avec la valeur mfa.

services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Cette stratégie peut ensuite être utilisée dans la vue _Layout pour afficher ou masquer le menu Administration avec l’avertissement :

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Si l’identity s’est connectée à l’aide de l’authentification multifacteur, le menu Administrateur s’affiche sans l’avertissement d’info-bulle. Lorsque l’utilisateur s’est connecté sans MFA, le menu Administration (Non activé) s’affiche avec l’info-bulle qui l’informe (expliquant l’avertissement).

@if (SignInManager.IsSignedIn(User))
{
    @if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin" 
               id="tooltip-demo"  
               data-toggle="tooltip" 
               data-placement="bottom" 
               title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
                Admin (Not Enabled)
            </a>
        </li>
    }
}

Si l’utilisateur se connecte sans MFA, l’avertissement s’affiche :

Authentification MFA administrateur

L’utilisateur est redirigé vers l’affichage d’activation de l’authentification multifacteur lorsqu’il clique sur le lien Administration :

L’administrateur active l’authentification MFA

Envoyer une exigence de connexion MFA au serveur OpenID Connect

Le paramètre acr_values peut être utilisé pour passer la valeur mfa requise du client au serveur dans une demande d’authentification.

Remarque

Le paramètre acr_values doit être géré sur le serveur OpenID Connect pour que cela fonctionne.

Client OpenID Connect ASP.NET Core

L’application cliente ASP.NET Core Razor Pages OpenID Connect utilise la méthode AddOpenIdConnect pour se connecter au serveur OpenID Connect. Le paramètre acr_values est défini avec la valeur mfa et envoyé avec la demande d’authentification. Le OpenIdConnectEvents est utilisé pour ajouter ceci.

Pour connaître les valeurs de paramètre recommandées acr_values, consultez Valeurs de référence de méthode d’authentification.

build.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "<OpenID Connect server URL>";
	options.RequireHttpsMetadata = true;
	options.ClientId = "<OpenID Connect client ID>";
	options.ClientSecret = "<>";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
	options.Events = new OpenIdConnectEvents
	{
		OnRedirectToIdentityProvider = context =>
		{
			context.ProtocolMessage.SetParameter("acr_values", "mfa");
			return Task.FromResult(0);
		}
	};
});

Exemple de serveur OpenID Connect Duende IdentityServer avec ASP.NET Core Identity

Sur le serveur OpenID Connect, qui est implémenté à l’aide d’ASP.NET Core Identity avec des Razor pages, une nouvelle page nommée ErrorEnable2FA.cshtml est créée. Cette vie :

  • Affiche si Identity provient d’une application qui nécessite l’authentification multifacteur, mais que l’utilisateur n’a pas activé cette option dans Identity.
  • Informe l’utilisateur et ajoute un lien pour l’activer.
@{
    ViewData["Title"] = "ErrorEnable2FA";
}

<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>

<br />

You can enable MFA to login here:

<br />

<a href="~/Identity/Account/Manage/TwoFactorAuthentication">Enable MFA</a>

Dans la méthode Login, l’implémentation IIdentityServerInteractionService de l’interface _interaction est utilisée pour accéder aux paramètres de requête OpenID Connect. Le paramètre acr_values est accessible à l’aide de la propriété AcrValues. Comme le client l’a envoyé avec mfa défini, cela peut ensuite être vérifié.

Si l’authentification multifacteur est requise et que l’authentification multifacteur est activée pour l’utilisateur dans ASP.NET CoreIdentity, la connexion continue. Lorsque l’utilisateur n’a pas d’authentification multifacteur activée, l’utilisateur est redirigé vers la vue ErrorEnable2FA.cshtmlpersonnalisée . Ensuite, ASP.NET Core Identity connecte l’utilisateur.

Fido2Store sert à vérifier si l’utilisateur a activé l’authentification multifacteur à l’aide d’un fournisseur de jetons FIDO2 personnalisé.

public async Task<IActionResult> OnPost()
{
	// check if we are in the context of an authorization request
	var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);

	var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

	var user = await _userManager.FindByNameAsync(Input.Username);
	if (user != null && !user.TwoFactorEnabled && requires2Fa)
	{
		return RedirectToPage("/Home/ErrorEnable2FA/Index");
	}

	// code omitted for brevity

	if (ModelState.IsValid)
	{
		var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
		if (result.Succeeded)
		{
			// code omitted for brevity
		}
		if (result.RequiresTwoFactor)
		{
			var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
			if (fido2ItemExistsForUser.Count > 0)
			{
				return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
			}

			return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
		}

		await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
		ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
	}

	// something went wrong, show form with error
	await BuildModelAsync(Input.ReturnUrl);
	return Page();
}

Si l’utilisateur est déjà connecté, l’application cliente :

  • Valide toujours la revendication amr.
  • Peut configurer l’authentification multifacteur avec un lien vers la vue ASP.NET CoreIdentity.

acr_values-1 image

Forcer ASP.NET Core client OpenID Connect à exiger l’authentification multifacteur

Cet exemple montre comment une application page ASP.NET CoreRazor, qui utilise OpenID Connect pour se connecter, peut exiger que les utilisateurs se soient authentifiés à l’aide de l’authentification multifacteur.

Pour valider l’exigence MFA, une exigence IAuthorizationRequirement est créée. Cela sera ajouté aux pages à l’aide d’une stratégie qui nécessite l’authentification multifacteur.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

Un AuthorizationHandler est implémenté qui utilise la revendication amr et case activée pour la valeur mfa. Le amr est retourné dans le id_token d’une authentification réussie et peut avoir de nombreuses valeurs différentes telles que définies dans la spécification valeurs de référence de la méthode d’authentification .

La valeur retournée dépend de la façon dont l’identity s’est authentifiée et de l’implémentation du serveur OpenID Connect.

Le AuthorizationHandler utilise l’exigence RequireMfa et valide la revendication amr. Le serveur OpenID Connect peut être implémenté à l’aide du serveur Duende Identity avec ASP.NET Core Identity. Lorsqu’un utilisateur se connecte à l’aide de TOTP, la revendication amr est retournée avec une valeur MFA. Si vous utilisez une autre implémentation de serveur OpenID Connect ou un autre type d’authentification multifacteur, la amr revendication aura ou peut avoir une valeur différente. Le code doit être étendu pour l’accepter également.

public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
	protected override Task HandleRequirementAsync(
		AuthorizationHandlerContext context, 
		RequireMfa requirement)
	{
		if (context == null)
			throw new ArgumentNullException(nameof(context));
		if (requirement == null)
			throw new ArgumentNullException(nameof(requirement));

		var amrClaim =
			context.User.Claims.FirstOrDefault(t => t.Type == "amr");

		if (amrClaim != null && amrClaim.Value == Amr.Mfa)
		{
			context.Succeed(requirement);
		}

		return Task.CompletedTask;
	}
}

Dans le fichier programme, la méthode AddOpenIdConnectest utilisée comme schéma de contestation par défaut. Le gestionnaire d’autorisations, utilisé pour vérifier la revendication amr, est ajouté au conteneur Inversion of Control. Une stratégie est ensuite créée qui ajoute l’exigence RequireMfa.

builder.Services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

builder.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "https://localhost:44352";
	options.RequireHttpsMetadata = true;
	options.ClientId = "AspNetCoreRequireMfaOidc";
	options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
	options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
	{
		policyIsAdminRequirement.Requirements.Add(new RequireMfa());
	});
});

builder.Services.AddRazorPages();

Cette stratégie est ensuite utilisée dans la page Razor en fonction des besoins. La stratégie peut également être ajoutée globalement pour l’ensemble de l’application.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Si l’utilisateur s’authentifie sans MFA, la revendication amr aura probablement une valeur pwd. La demande ne sera pas autorisée à accéder à la page. À l’aide des valeurs par défaut, l’utilisateur est redirigé vers la page Account/AccessDenied. Ce comportement peut être modifié ou vous pouvez implémenter votre propre logique personnalisée ici. Dans cet exemple, un lien est ajouté afin que l’utilisateur valide puisse configurer l’authentification multifacteur pour son compte.

@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
    ViewData["Title"] = "AccessDenied";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>AccessDenied</h1>

You require MFA to login here

<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>

Désormais, seuls les utilisateurs qui s’authentifient auprès de l’authentification multifacteur peuvent accéder à la page ou au site web. Si différents types d’authentification multifacteur sont utilisés ou si 2FA est acceptable, la revendication amr aura des valeurs différentes et doit être traitée correctement. Différents serveurs OpenID Connect retournent également des valeurs différentes pour cette revendication et peuvent ne pas suivre la spécification des valeurs de référence de la méthode d’authentification.

Lors de la connexion sans authentification multifacteur (par exemple, en utilisant simplement un mot de passe) :

  • Le amr a la pwd valeur :

    amr a la valeur pwd

  • Accès refusé :

    Accès refusé

Vous pouvez également vous connecter à l’aide de l’otP avec Identity :

Connexion à l’aide de l’otP avec Identity

Ressources supplémentaires

Par Damien Bowden

Afficher ou télécharger un exemple de code (référentiel GitHub damienbod/AspNetCoreHybridFlowWithApi)

L’authentification MFA (Azure Multi-Factor Authentication) est un processus dans lequel un utilisateur est invité, au cours d’un événement de connexion, à utiliser des formes d’identification supplémentaires. Il peut s'agir de saisir un code à partir d'un téléphone portable, d'utiliser une clé FIDO2 ou de scanner une empreinte digitale. Lorsque vous demandez un deuxième formulaire d’authentification, la sécurité est améliorée. Le facteur supplémentaire n’est pas facilement obtenu ou dupliqué par un cyberattaquant.

Cet article aborde les zones suivantes :

  • Qu’est-ce que l’authentification multifacteur et quels flux MFA sont recommandés
  • Configurer l’authentification multifacteur pour les pages d’administration à l’aide de ASP.NET Core Identity
  • Envoyer une exigence de connexion MFA au serveur OpenID Connect
  • Forcer ASP.NET Core client OpenID Connect à exiger l’authentification multifacteur

MFA, 2FA

L’authentification multifacteur nécessite au moins deux types de preuves pour une identity, telles que quelque chose que vous connaissez, quelque chose que vous possédez ou une validation biométrique pour que l’utilisateur s’authentifie.

L’authentification à deux facteurs (2FA) est un type d’authentification multifacteur, mais la principale différence réside dans le fait que l’authentification multifacteur peut nécessiter au moins deux éléments pour vérifier l’identity.

TOTP MFA (Algorithme de mot de passe à usage unique basé sur le temps)

MFA utilisant TOTP est une implémentation prise en charge à l’aide de ASP.NET Core Identity. Ceci peut être utilisé avec n’importe quelle application d’authentificateur conforme, notamment :

  • Application Microsoft Authenticator
  • Application Google Authenticator

Pour plus d’informations sur l’implémentation, consultez le lien suivant :

Activer la génération de QR Code pour les applications d’authentification TOTP dans ASP.NET Core

Clés d’accès MFA/FIDO2 ou sans mot de passe

Les clés d’accès/FIDO2 sont actuellement :

  • Le moyen le plus sûr d’obtenir l’authentification multifacteur.
  • Authentification multifacteur qui offre une protection contre les attaques par hameçonnage. (Ainsi que l’authentification par certificat et Windows pour les entreprises)

Actuellement, ASP.NET Core ne prend pas directement en charge FIDO2. Des clés d’accès/FIDO2 peut être utilisé pour les flux d’authentification multifacteur (MFA) ou sans mot de passe.

Microsoft Entra ID prend en charge les flux sans mot de passe/FIDO2 et sans mot de passe. Pour plus d’informations, consultez options d’authentification sans mot de passe.

D’autres formes d’authentification multifacteur sans mot de passe n’offrent pas de protection ou risquent de ne pas offrir de protection contre le hameçonnage.

MFA SMS

L’authentification multifacteur avec SMS augmente massivement la sécurité par rapport à l’authentification par mot de passe (facteur unique). Toutefois, l’utilisation des SMS comme deuxième facteur n’est plus recommandée. Trop de vecteurs d’attaque connus existent pour ce type d’implémentation.

Recommandations NIST

Configurer l’authentification multifacteur pour les pages d’administration à l’aide de ASP.NET Core Identity

L’authentification multifacteur peut être forcée sur les utilisateurs à accéder aux pages sensibles au sein d’une application ASP.NET CoreIdentity. Cela peut être utile pour les applications où différents niveaux d’accès existent pour les différentes identités. Par exemple, les utilisateurs peuvent être en mesure d’afficher les données de profil à l’aide d’une connexion par mot de passe, mais un administrateur doit utiliser l’authentification multifacteur pour accéder aux pages d’administration.

Étendre la connexion avec une revendication MFA

Le code de démonstration est configuré à l’aide de ASP.NET Core avec Pages Identity et Razor. La méthode AddIdentity est utilisée à la place de la méthode AddDefaultIdentity, de sorte qu’une implémentation IUserClaimsPrincipalFactory peut être utilisée pour ajouter des réclamations à l’identity après une connexion réussie.

Avertissement

Cet article montre l’utilisation de chaîne de connexion s. Avec une base de données locale, l’utilisateur n’a pas besoin d’être authentifié, mais en production, les chaîne de connexion incluent parfois un mot de passe pour s’authentifier. Les informations d’identification de mot de passe du propriétaire de la ressource (ROPC) constituent un risque de sécurité qui doit être évité dans les bases de données de production. Les applications de production doivent utiliser le flux d’authentification le plus sécurisé disponible. Pour plus d’informations sur l’authentification des applications déployées pour tester ou produire des environnements, consultez Flux d’authentification sécurisés.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlite(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<IdentityUser, IdentityRole>(
            options => options.SignIn.RequireConfirmedAccount = false)
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddSingleton<IEmailSender, EmailSender>();
    services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, 
        AdditionalUserClaimsPrincipalFactory>();

    services.AddAuthorization(options =>
        options.AddPolicy("TwoFactorEnabled",
            x => x.RequireClaim("amr", "mfa")));

    services.AddRazorPages();
}

La classe AdditionalUserClaimsPrincipalFactory ajoute la revendication amr aux revendications utilisateur uniquement après une connexion réussie. La valeur de la revendication est lue à partir de la base de données. La revendication est ajoutée ici, car l’utilisateur ne doit accéder à la vue protégée supérieure que si l’identity s’est connectée avec l’authentification multifacteur. Si la vue de base de données est lue directement à partir de la base de données au lieu d’utiliser la revendication, il est possible d’accéder à la vue sans authentification multifacteur directement après l’activation de l’authentification multifacteur.

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityStandaloneMfa
{
    public class AdditionalUserClaimsPrincipalFactory : 
        UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
    {
        public AdditionalUserClaimsPrincipalFactory( 
            UserManager<IdentityUser> userManager,
            RoleManager<IdentityRole> roleManager, 
            IOptions<IdentityOptions> optionsAccessor) 
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
        {
            var principal = await base.CreateAsync(user);
            var identity = (ClaimsIdentity)principal.Identity;

            var claims = new List<Claim>();

            if (user.TwoFactorEnabled)
            {
                claims.Add(new Claim("amr", "mfa"));
            }
            else
            {
                claims.Add(new Claim("amr", "pwd"));
            }

            identity.AddClaims(claims);
            return principal;
        }
    }
}

Étant donné que la configuration du service Identity a changé dans la classe Startup, les dispositions de Identity doivent être mises à jour. Structurez les pages Identity dans l’application. Définissez la disposition dans le fichier Identity/Account/Manage/_Layout.cshtml.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Affectez également la mise en page pour toutes les pages de gestion des pages Identity :

@{
    Layout = "_Layout.cshtml";
}

Valider l’exigence MFA dans la page d’administration

La page d’administration Razor vérifie que l’utilisateur s’est connecté à l’aide de l’authentification multifacteur. Dans la méthode OnGet, l’identity est utilisée pour accéder aux revendications de l’utilisateur. La revendication amr est vérifiée pour la valeur mfa. Si l’identity est manquante dans cette revendication ou est false, la page redirige vers la page d’activation de l’authentification multifacteur. Cela est possible, car l’utilisateur s’est déjà connecté, mais sans MFA.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityStandaloneMfa
{
    public class AdminModel : PageModel
    {
        public IActionResult OnGet()
        {
            var claimTwoFactorEnabled = 
                User.Claims.FirstOrDefault(t => t.Type == "amr");

            if (claimTwoFactorEnabled != null && 
                "mfa".Equals(claimTwoFactorEnabled.Value))
            {
                // You logged in with MFA, do the administrative stuff
            }
            else
            {
                return Redirect(
                    "/Identity/Account/Manage/TwoFactorAuthentication");
            }

            return Page();
        }
    }
}

Logique d’interface utilisateur pour activer/désactiver les informations de connexion utilisateur

Une stratégie d’autorisation a été ajoutée dans le fichier programme. La stratégie nécessite la revendication amr avec la valeur mfa.

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Cette stratégie peut ensuite être utilisée dans la vue _Layout pour afficher ou masquer le menu Administration avec l’avertissement :

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Si l’identity s’est connectée à l’aide de l’authentification multifacteur, le menu Administrateur s’affiche sans l’avertissement d’info-bulle. Lorsque l’utilisateur s’est connecté sans MFA, le menu Administration (Non activé) s’affiche avec l’info-bulle qui l’informe (expliquant l’avertissement).

@if (SignInManager.IsSignedIn(User))
{
    @if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-page="/Admin" 
               id="tooltip-demo"  
               data-toggle="tooltip" 
               data-placement="bottom" 
               title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
                Admin (Not Enabled)
            </a>
        </li>
    }
}

Si l’utilisateur se connecte sans MFA, l’avertissement s’affiche :

Authentification MFA administrateur

L’utilisateur est redirigé vers l’affichage d’activation de l’authentification multifacteur lorsqu’il clique sur le lien Administration :

L’administrateur active l’authentification MFA

Envoyer une exigence de connexion MFA au serveur OpenID Connect

Le paramètre acr_values peut être utilisé pour passer la valeur mfa requise du client au serveur dans une demande d’authentification.

Remarque

Le paramètre acr_values doit être géré sur le serveur OpenID Connect pour que cela fonctionne.

Client OpenID Connect ASP.NET Core

L’application cliente ASP.NET Core Razor Pages OpenID Connect utilise la méthode AddOpenIdConnect pour se connecter au serveur OpenID Connect. Le paramètre acr_values est défini avec la valeur mfa et envoyé avec la demande d’authentification. Le OpenIdConnectEvents est utilisé pour ajouter ceci.

Pour connaître les valeurs de paramètre recommandées acr_values, consultez Valeurs de référence de méthode d’authentification.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme =
            OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.SignInScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.Authority = "<OpenID Connect server URL>";
        options.RequireHttpsMetadata = true;
        options.ClientId = "<OpenID Connect client ID>";
        options.ClientSecret = "<>";
        options.ResponseType = "code";
        options.UsePkce = true;	
        options.Scope.Add("profile");
        options.Scope.Add("offline_access");
        options.SaveTokens = true;
        options.Events = new OpenIdConnectEvents
        {
            OnRedirectToIdentityProvider = context =>
            {
                context.ProtocolMessage.SetParameter("acr_values", "mfa");
                return Task.FromResult(0);
            }
        };
    });

Exemple de serveur OpenID Connect IdentityServer avec ASP.NET Core Identity

Sur le serveur OpenID Connect, qui est implémenté à l’aide de ASP.NET Core Identity avec des vues MVC, une nouvelle vue nommée ErrorEnable2FA.cshtml est créée. Cette vie :

  • Affiche si Identity provient d’une application qui nécessite l’authentification multifacteur, mais que l’utilisateur n’a pas activé cette option dans Identity.
  • Informe l’utilisateur et ajoute un lien pour l’activer.
@{
    ViewData["Title"] = "ErrorEnable2FA";
}

<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>

<br />

You can enable MFA to login here:

<br />

<a asp-controller="Manage" asp-action="TwoFactorAuthentication">Enable MFA</a>

Dans la méthode Login, l’implémentation IIdentityServerInteractionService de l’interface _interaction est utilisée pour accéder aux paramètres de requête OpenID Connect. Le paramètre acr_values est accessible à l’aide de la propriété AcrValues. Comme le client l’a envoyé avec mfa défini, cela peut ensuite être vérifié.

Si l’authentification multifacteur est requise et que l’authentification multifacteur est activée pour l’utilisateur dans ASP.NET CoreIdentity, la connexion continue. Lorsque l’utilisateur n’a pas d’authentification multifacteur activée, l’utilisateur est redirigé vers la vue ErrorEnable2FA.cshtmlpersonnalisée . Ensuite, ASP.NET Core Identity connecte l’utilisateur.

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
    var returnUrl = model.ReturnUrl;
    var context = 
        await _interaction.GetAuthorizationContextAsync(returnUrl);
    var requires2Fa = 
        context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

    var user = await _userManager.FindByNameAsync(model.Email);
    if (user != null && !user.TwoFactorEnabled && requires2Fa)
    {
        return RedirectToAction(nameof(ErrorEnable2FA));
    }

    // code omitted for brevity

La méthode ExternalLoginCallback fonctionne comme la connexion locale Identity. La valeur AcrValues de la propriété mfa est vérifiée. Si la valeur mfa est présente, l’authentification multifacteur est forcée avant la fin de la connexion (par exemple, redirigée vers la ErrorEnable2FA vue).

//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(
    string returnUrl = null,
    string remoteError = null)
{
    var context =
        await _interaction.GetAuthorizationContextAsync(returnUrl);
    var requires2Fa =
        context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

    if (remoteError != null)
    {
        ModelState.AddModelError(
            string.Empty,
            _sharedLocalizer["EXTERNAL_PROVIDER_ERROR", 
            remoteError]);
        return View(nameof(Login));
    }
    var info = await _signInManager.GetExternalLoginInfoAsync();

    if (info == null)
    {
        return RedirectToAction(nameof(Login));
    }

    var email = info.Principal.FindFirstValue(ClaimTypes.Email);

    if (!string.IsNullOrEmpty(email))
    {
        var user = await _userManager.FindByNameAsync(email);
        if (user != null && !user.TwoFactorEnabled && requires2Fa)
        {
            return RedirectToAction(nameof(ErrorEnable2FA));
        }
    }

    // Sign in the user with this external login provider if the user already has a login.
    var result = await _signInManager
        .ExternalLoginSignInAsync(
            info.LoginProvider, 
            info.ProviderKey, 
            isPersistent: 
            false);

    // code omitted for brevity

Si l’utilisateur est déjà connecté, l’application cliente :

  • Valide toujours la revendication amr.
  • Peut configurer l’authentification multifacteur avec un lien vers la vue ASP.NET CoreIdentity.

acr_values-1 image

Forcer ASP.NET Core client OpenID Connect à exiger l’authentification multifacteur

Cet exemple montre comment une application page ASP.NET CoreRazor, qui utilise OpenID Connect pour se connecter, peut exiger que les utilisateurs se soient authentifiés à l’aide de l’authentification multifacteur.

Pour valider l’exigence MFA, une exigence IAuthorizationRequirement est créée. Cela sera ajouté aux pages à l’aide d’une stratégie qui nécessite l’authentification multifacteur.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc
{
    public class RequireMfa : IAuthorizationRequirement{}
}

Un AuthorizationHandler est implémenté qui utilise la revendication amr et case activée pour la valeur mfa. Le amr est retourné dans le id_token d’une authentification réussie et peut avoir de nombreuses valeurs différentes telles que définies dans la spécification valeurs de référence de la méthode d’authentification .

La valeur retournée dépend de la façon dont l’identity s’est authentifiée et de l’implémentation du serveur OpenID Connect.

Le AuthorizationHandler utilise l’exigence RequireMfa et valide la revendication amr. Le serveur OpenID Connect peut être implémenté en utilisant IdentityServer4 avec ASP.NET Core Identity Lorsqu’un utilisateur se connecte à l’aide de TOTP, la revendication amr est retournée avec une valeur MFA. Si vous utilisez une autre implémentation de serveur OpenID Connect ou un autre type d’authentification multifacteur, la amr revendication aura ou peut avoir une valeur différente. Le code doit être étendu pour l’accepter également.

public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
	protected override Task HandleRequirementAsync(
		AuthorizationHandlerContext context, 
		RequireMfa requirement)
	{
		if (context == null)
			throw new ArgumentNullException(nameof(context));
		if (requirement == null)
			throw new ArgumentNullException(nameof(requirement));

		var amrClaim =
			context.User.Claims.FirstOrDefault(t => t.Type == "amr");

		if (amrClaim != null && amrClaim.Value == Amr.Mfa)
		{
			context.Succeed(requirement);
		}

		return Task.CompletedTask;
	}
}

Dans la méthode Startup.ConfigureServices, la méthode AddOpenIdConnect est utilisée comme schéma de contestation par défaut. Le gestionnaire d’autorisations, utilisé pour vérifier la revendication amr, est ajouté au conteneur Inversion of Control. Une stratégie est ensuite créée qui ajoute l’exigence RequireMfa.

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

    services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme =
            OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.SignInScheme =
            CookieAuthenticationDefaults.AuthenticationScheme;
        options.Authority = "https://localhost:44352";
        options.RequireHttpsMetadata = true;
        options.ClientId = "AspNetCoreRequireMfaOidc";
        options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
        options.ResponseType = "code";
        options.UsePkce = true;	
        options.Scope.Add("profile");
        options.Scope.Add("offline_access");
        options.SaveTokens = true;
    });

    services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
        {
            policyIsAdminRequirement.Requirements.Add(new RequireMfa());
        });
    });

    services.AddRazorPages();
}

Cette stratégie est ensuite utilisée dans la page Razor en fonction des besoins. La stratégie peut également être ajoutée globalement pour l’ensemble de l’application.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Si l’utilisateur s’authentifie sans MFA, la revendication amr aura probablement une valeur pwd. La demande ne sera pas autorisée à accéder à la page. À l’aide des valeurs par défaut, l’utilisateur est redirigé vers la page Account/AccessDenied. Ce comportement peut être modifié ou vous pouvez implémenter votre propre logique personnalisée ici. Dans cet exemple, un lien est ajouté afin que l’utilisateur valide puisse configurer l’authentification multifacteur pour son compte.

@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
    ViewData["Title"] = "AccessDenied";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>AccessDenied</h1>

You require MFA to login here

<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>

Désormais, seuls les utilisateurs qui s’authentifient auprès de l’authentification multifacteur peuvent accéder à la page ou au site web. Si différents types d’authentification multifacteur sont utilisés ou si 2FA est acceptable, la revendication amr aura des valeurs différentes et doit être traitée correctement. Différents serveurs OpenID Connect retournent également des valeurs différentes pour cette revendication et peuvent ne pas suivre la spécification des valeurs de référence de la méthode d’authentification.

Lors de la connexion sans authentification multifacteur (par exemple, en utilisant simplement un mot de passe) :

  • Le amr a la pwd valeur :

    amr a la valeur pwd

  • Accès refusé :

    Accès refusé

Vous pouvez également vous connecter à l’aide de l’otP avec Identity :

Connexion à l’aide de l’otP avec Identity

Ressources supplémentaires