Authentification et autorisation dans Blazor : un guide pratique
Si vous avez travaillé avec ASP.NET MVC ou Razor Pages, vous avez probablement un modèle mental sur le fonctionnement de l’authentification : le middleware intercepte la demande, vérifie un cookie ou un jeton, remplit HttpContext.User et c’est parti pour les courses. Blazor modifie ce modèle mental de manière subtile mais importante – et si vous ne comprenez pas ces différences dès le début, vous finirez par déboguer des problèmes d’authentification qui semblent impossibles.
Dans cet article, je souhaite expliquer comment l’authentification et l’autorisation fonctionnent réellement dans Blazor, couvrant à la fois les modèles d’hébergement Server et WebAssembly. Nous passerons des principes fondamentaux jusqu’aux fournisseurs personnalisés, à OAuth externe et aux pièges que j’ai vus qui font trébucher même les développeurs .NET expérimentés.
Pourquoi l’authentification dans Blazor est différente
Dans ASP.NET traditionnel, chaque interaction utilisateur est une requête HTTP. Le serveur valide les informations d’identification, définit un cookie et chaque demande ultérieure transporte ce cookie. Le pipeline d’authentification est linéaire et prévisible.
Blazor Server fonctionne sur une connexion SignalR persistante. Une fois la requête HTTP initiale chargée, toutes les interactions ultérieures se produisent via WebSockets. Il n’y a pas de nouvelle requête HTTP pour chaque clic sur un bouton, donc le middleware ne se réexécute pas à chaque interaction. Le HttpContext est disponible lors de la connexion initiale, mais s’appuyer sur lui tout au long de la durée de vie d’un circuit est une recette pour les bugs.
Blazor WebAssembly s’exécute entièrement dans le navigateur. Il n’y a aucun HttpContext côté serveur. L’état d’authentification doit être récupéré à partir d’une API, stocké côté client et géré via des jetons (généralement des JWT). Le serveur ne fait confiance au client qu’en ce qui concerne la validation du jeton.
Cela signifie que Blazor a besoin de sa propre abstraction pour l’état d’authentification, qui fonctionne quel que soit le modèle d’hébergement. Cette abstraction est le AuthenticationStateProvider.
État d’authentification : La Fondation
Au cœur du système d’authentification de Blazor se trouve AuthenticationStateProvider. Il s’agit d’une classe abstraite qui expose une seule méthode critique :
public abstract Task<AuthenticationState> GetAuthenticationStateAsync();
L’objet AuthenticationState enveloppe un ClaimsPrincipal — le même modèle d’identité utilisé dans .NET. Les composants ne communiquent pas directement avec les cookies ou les jetons ; ils demandent au AuthenticationStateProvider l’état actuel.
Pour rendre cet état disponible pour l’ensemble de votre arborescence de composants, Blazor fournit CascadingAuthenticationState. Vous enveloppez généralement votre routeur avec celui-ci dans App.razor ou dans votre mise en page :
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<p>You're not authorized to view this page.</p>
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Page not found.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
Le AuthorizeRouteView fait ici une double tâche : il vérifie si l’utilisateur est authentifié et autorisé avant de restituer le composant de page correspondant, et il fournit une interface utilisateur de secours lorsqu’il ne l’est pas.
Dans .NET 8 et versions ultérieures avec le modèle Blazor unifié, vous configurerez cela dans votre App.razor et le framework gère automatiquement le paramètre en cascade lorsque vous utilisez AddCascadingAuthenticationState() dans votre enregistrement de service.
Intégration d’identité ASP.NET
Pour la plupart des projets, vous n’avez pas besoin de créer une authentification à partir de zéro. ASP.NET Identity vous offre une gestion des utilisateurs, un hachage de mot de passe, une authentification à deux facteurs et une confirmation de compte prête à l’emploi.La configuration avec Blazor commence dans Program.cs :
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
});
Avec le modèle Blazor Web App dans .NET 8+, l’interface utilisateur d’identité échafaudée utilise directement les composants Razor. Vous obtenez des pages de connexion, d’inscription et de gestion de compte qui s’intègrent naturellement au reste de votre application Blazor – plus de mélange gênant de composants Razor Pages et Blazor.
Le ApplicationDbContext hérite de IdentityDbContext et vous devrez exécuter des migrations pour créer les tables d’identité :
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
}
dotnet ef migrations add InitialIdentity
dotnet ef database update
Le composant AuthorizeView
Une fois que l’état d’authentification circule dans votre arborescence de composants, AuthorizeView vous permet d’afficher l’interface utilisateur de manière conditionnelle :
<AuthorizeView>
<Authorized>
<p>Welcome, @context.User.Identity?.Name!</p>
<a href="/account/manage">Manage Account</a>
<form method="post" action="/account/logout">
<button type="submit">Log Out</button>
</form>
</Authorized>
<NotAuthorized>
<a href="/account/login">Log In</a>
<a href="/account/register">Register</a>
</NotAuthorized>
</AuthorizeView>
Le paramètre context à l’intérieur de <Authorized> vous donne accès au AuthenticationState, afin que vous puissiez inspecter les revendications, les rôles et l’identité de l’utilisateur directement dans votre balisage.
Vous pouvez également utiliser AuthorizeView avec des rôles et des stratégies :
<AuthorizeView Roles="Admin,Moderator">
<Authorized>
<button @onclick="DeletePost">Delete Post</button>
</Authorized>
</AuthorizeView>
<AuthorizeView Policy="CanEditArticles">
<Authorized>
<button @onclick="EditArticle">Edit</button>
</Authorized>
</AuthorizeView>
Une chose à garder à l’esprit : AuthorizeView est un problème d’interface utilisateur. Il masque ou affiche des éléments, mais ne protège pas la logique sous-jacente. Si quelqu’un peut appeler votre point de terminaison API ou appeler votre méthode directement, il contourne entièrement AuthorizeView. Appliquez toujours l’autorisation côté serveur également.
L’attribut [Autoriser]
Pour protéger une page entière, appliquez l’attribut [Authorize] :
@page "/admin/dashboard"
@attribute [Authorize(Roles = "Admin")]
<h1>Admin Dashboard</h1>
<p>Only administrators can see this page.</p>
Lorsqu’un utilisateur non authentifié accède à cette page, le AuthorizeRouteView entre en jeu et restitue le modèle <NotAuthorized> que vous avez défini précédemment. Vous pouvez plutôt rediriger vers une page de connexion en gérant le cas NotAuthorized avec la navigation :
<NotAuthorized>
@if (!context.User.Identity?.IsAuthenticated ?? true)
{
<RedirectToLogin />
}
else
{
<p>You don't have permission to access this page.</p>
}
</NotAuthorized>
Un simple composant RedirectToLogin pourrait ressembler à :
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
var returnUrl = Uri.EscapeDataString(Navigation.Uri);
Navigation.NavigateTo($"/account/login?returnUrl={returnUrl}", forceLoad: true);
}
}
Le forceLoad: true est important ici : vous souhaitez une véritable navigation HTTP afin que le middleware d’authentification côté serveur puisse gérer correctement le flux de connexion.
Autorisation basée sur les rôles et basée sur les règles
Les rôles constituent le modèle le plus simple : affectez les utilisateurs à des groupes tels que “Administrateur” ou “Éditeur”, puis vérifiez l’adhésion. Mais les politiques vous offrent beaucoup plus de flexibilité.
Enregistrez les stratégies dans Program.cs :
builder.Services.AddAuthorizationCore(options =>
{
options.AddPolicy("CanPublish", policy =>
policy.RequireClaim("Permission", "Publish"));
options.AddPolicy("MinimumAge", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(18)));
options.AddPolicy("PremiumUser", policy =>
policy.RequireRole("Premium")
.RequireClaim("Subscription", "Active"));
});
Les exigences personnalisées nécessitent un gestionnaire :
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge) => MinimumAge = minimumAge;
}
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var dateOfBirthClaim = context.User.FindFirst("DateOfBirth");
if (dateOfBirthClaim is null)
return Task.CompletedTask;
var dateOfBirth = DateOnly.Parse(dateOfBirthClaim.Value);
var age = DateOnly.FromDateTime(DateTime.Today).Year - dateOfBirth.Year;
if (age >= requirement.MinimumAge)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
Enregistrez le gestionnaire :
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
Dans les composants, vous pouvez également vérifier l’autorisation par programme lorsque vous avez besoin d’une logique dynamique :
@inject IAuthorizationService AuthorizationService
@inject AuthenticationStateProvider AuthStateProvider
@code {
private bool canPublish;
protected override async Task OnInitializedAsync()
{
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var result = await AuthorizationService.AuthorizeAsync(
authState.User, "CanPublish");
canPublish = result.Succeeded;
}
}
Fournisseurs OAuth externes
La prise en charge de « Connectez-vous avec Google » ou « Connectez-vous avec GitHub » est simple avec le middleware d’authentification ASP.NET. Ceux-ci sont configurés côté serveur puisque le flux OAuth nécessite des redirections HTTP.
builder.Services.AddAuthentication()
.AddGoogle(options =>
{
options.ClientId = builder.Configuration["Auth:Google:ClientId"]!;
options.ClientSecret = builder.Configuration["Auth:Google:ClientSecret"]!;
options.Scope.Add("profile");
})
.AddMicrosoftAccount(options =>
{
options.ClientId = builder.Configuration["Auth:Microsoft:ClientId"]!;
options.ClientSecret = builder.Configuration["Auth:Microsoft:ClientSecret"]!;
})
.AddGitHub(options =>
{
options.ClientId = builder.Configuration["Auth:GitHub:ClientId"]!;
options.ClientSecret = builder.Configuration["Auth:GitHub:ClientSecret"]!;
});
Pour GitHub, vous aurez besoin du package NuGet AspNet.Security.OAuth.GitHub, car il n’est pas inclus dans les bibliothèques ASP.NET par défaut.
L’interface utilisateur de connexion fournit ensuite des liens qui déclenchent le défi externe :
@page "/account/external-login"
<h2>Sign in with an external provider</h2>
<form method="post" action="/api/auth/external-login">
<button type="submit" name="provider" value="Google">
Sign in with Google
</button>
<button type="submit" name="provider" value="Microsoft">
Sign in with Microsoft
</button>
<button type="submit" name="provider" value="GitHub">
Sign in with GitHub
</button>
</form>
Le point de terminaison de l’API déclenche le défi et gère le rappel :
app.MapPost("/api/auth/external-login", (string provider, HttpContext context) =>
{
var properties = new AuthenticationProperties
{
RedirectUri = "/api/auth/external-callback"
};
return Results.Challenge(properties, [provider]);
});
L’authentification externe dans Blazor nécessite toujours une navigation sur une page entière : vous ne pouvez pas effectuer une redirection OAuth à l’intérieur d’un circuit SignalR ou d’une application WebAssembly sans passer par le serveur.
Authentification basée sur des jetons dans Blazor WebAssemblyBlazor WebAssembly s’exécute sur le client, donc l’authentification basée sur les cookies ne s’applique pas de la même manière. Au lieu de cela, vous utilisez généralement des JWT stockés en mémoire et attachés aux requêtes HTTP sortantes.
Le framework fournit AuthorizationMessageHandler pour attacher automatiquement les jetons :
builder.Services.AddHttpClient("API",
client => client.BaseAddress = new Uri("https://api.example.com"))
.AddHttpMessageHandler<AuthorizationMessageHandler>();
builder.Services.AddScoped(sp =>
sp.GetRequiredService<IHttpClientFactory>().CreateClient("API"));
Pour les applications Blazor WASM autonomes qui s’authentifient auprès de leur propre API, vous implémenterez un AuthenticationStateProvider personnalisé qui analyse le JWT :
public class JwtAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly ILocalStorageService _localStorage;
private readonly HttpClient _httpClient;
public JwtAuthenticationStateProvider(
ILocalStorageService localStorage,
HttpClient httpClient)
{
_localStorage = localStorage;
_httpClient = httpClient;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var token = await _localStorage.GetItemAsync<string>("authToken");
if (string.IsNullOrWhiteSpace(token))
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var claims = ParseClaimsFromJwt(token);
var identity = new ClaimsIdentity(claims, "jwt");
return new AuthenticationState(new ClaimsPrincipal(identity));
}
public void NotifyAuthStateChanged()
{
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
{
var payload = jwt.Split('.')[1];
var padded = payload.Length % 4 switch
{
2 => payload + "==",
3 => payload + "=",
_ => payload
};
var bytes = Convert.FromBase64String(padded);
var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(bytes);
return json?.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()))
?? Enumerable.Empty<Claim>();
}
}
Après une connexion réussie, vous stockez le jeton et notifiez l’état d’authentification :
public class AuthService
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;
private readonly JwtAuthenticationStateProvider _authStateProvider;
public AuthService(
HttpClient httpClient,
ILocalStorageService localStorage,
AuthenticationStateProvider authStateProvider)
{
_httpClient = httpClient;
_localStorage = localStorage;
_authStateProvider = (JwtAuthenticationStateProvider)authStateProvider;
}
public async Task<bool> LoginAsync(string email, string password)
{
var response = await _httpClient.PostAsJsonAsync("/api/auth/login",
new { Email = email, Password = password });
if (!response.IsSuccessStatusCode)
return false;
var result = await response.Content
.ReadFromJsonAsync<LoginResponse>();
await _localStorage.SetItemAsync("authToken", result!.Token);
_authStateProvider.NotifyAuthStateChanged();
return true;
}
public async Task LogoutAsync()
{
await _localStorage.RemoveItemAsync("authToken");
_authStateProvider.NotifyAuthStateChanged();
}
}
Un mot d’avertissement : stocker les JWT dans localStorage les expose aux attaques XSS. Pour les applications plus sécurisées, envisagez de conserver les jetons en mémoire uniquement et d’utiliser des jetons d’actualisation, ou d’adopter le modèle Backend-for-Frontend (BFF) dans lequel le serveur gère les jetons et le client utilise des cookies HTTP uniquement.
Blazor Server vs WebAssembly : considérations de sécurité
Le modèle d’hébergement modifie fondamentalement votre posture de sécurité.
Blazor Server conserve toute la logique de vos composants sur le serveur. Le client ne voit que les différences HTML rendues sur SignalR. Cela signifie :
- La logique sensible ne quitte jamais le serveur
- Vous pouvez accéder aux bases de données et aux services internes directement depuis les composants
- L’état d’authentification provient du
HttpContextdu serveur lors de la connexion initiale - Le circuit peut survivre au cookie d’authentification : si le cookie d’un utilisateur expire, le circuit reste actif jusqu’à sa déconnexion.
- Vous devez gérer la déconnexion du circuit avec élégance et revalider l’état d’authentification lors de la reconnexion
Blazor WebAssembly s’exécute entièrement dans le navigateur. Cela signifie :
- Tout le code de votre composant est téléchargeable et inspectable
- Ne mettez jamais de secrets, de chaînes de connexion ou de logique métier sensible dans les composants WASM
- L’authentification n’est appliquée sur le client que pour l’UX ; la véritable application doit avoir lieu au niveau de votre couche API
- La gestion des jetons est de votre responsabilité
- Envisagez d’utiliser le modèle hébergé dans lequel un projet de serveur gère l’authentification et sert l’application WASM
Un modèle que je recommande pour les applications WebAssembly consiste à traiter chaque composant comme s’il s’agissait d’une « interface utilisateur non fiable » et chaque point de terminaison d’API comme s’il était appelé par un client inconnu. Validez tout côté serveur, indépendamment de ce que le client vérifie.
Création d’un AuthenticationStateProvider personnalisé
Parfois, les fournisseurs intégrés ne correspondent pas à votre architecture. Peut-être que vous intégrez un ancien système d’authentification ou que vous devez interroger les changements d’état d’authentification. Voici un fournisseur personnalisé plus complet pour Blazor Server :
public class CustomAuthStateProvider : AuthenticationStateProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IUserService _userService;
public CustomAuthStateProvider(
IHttpContextAccessor httpContextAccessor,
IUserService userService)
{
_httpContextAccessor = httpContextAccessor;
_userService = userService;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.User?.Identity?.IsAuthenticated != true)
return new AuthenticationState(
new ClaimsPrincipal(new ClaimsIdentity()));
var userId = httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId is null)
return new AuthenticationState(
new ClaimsPrincipal(new ClaimsIdentity()));
var user = await _userService.GetUserWithClaimsAsync(userId);
if (user is null)
return new AuthenticationState(
new ClaimsPrincipal(new ClaimsIdentity()));
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, user.Id),
new(ClaimTypes.Name, user.DisplayName),
new(ClaimTypes.Email, user.Email)
};
claims.AddRange(user.Roles.Select(r => new Claim(ClaimTypes.Role, r)));
claims.AddRange(user.Permissions.Select(p => new Claim("Permission", p)));
var identity = new ClaimsIdentity(claims, "Custom");
return new AuthenticationState(new ClaimsPrincipal(identity));
}
public void MarkUserAsAuthenticated(string userId)
{
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public void MarkUserAsLoggedOut()
{
var anonymous = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymous));
NotifyAuthenticationStateChanged(authState);
}
}
Enregistrez-le dans Program.cs :
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddScoped<CustomAuthStateProvider>();
L’information clé ici est NotifyAuthenticationStateChanged — son appel déclenche la mise à jour du paramètre en cascade, qui réévalue chaque AuthorizeView et AuthorizeRouteView dans votre arborescence de composants. C’est ainsi que vous faites réagir l’interface utilisateur aux événements de connexion/déconnexion sans actualisation complète de la page.
Pièges courants et solutions
Après avoir travaillé avec Blazor Auth sur de nombreux projets, voici les problèmes que je vois le plus souvent :
1. Utilisation de HttpContext dans les composants du serveur BlazorHttpContext est disponible lors de la requête HTTP initiale, mais il est null ou obsolète lors des interactions SignalR. N’injectez pas IHttpContextAccessor dans les composants qui s’exécutent après le rendu initial.
Solution : Capturez ce dont vous avez besoin à partir de HttpContext lors de l’initialisation et stockez-le dans un service étendu :
public class UserContext
{
public string? UserId { get; set; }
public string? AccessToken { get; set; }
}
// In a component that renders during the initial HTTP request:
@inject IHttpContextAccessor HttpContextAccessor
@inject UserContext UserContext
@code {
protected override void OnInitialized()
{
var context = HttpContextAccessor.HttpContext;
UserContext.UserId = context?.User.FindFirstValue(ClaimTypes.NameIdentifier);
UserContext.AccessToken = context?.Request.Headers.Authorization
.ToString().Replace("Bearer ", "");
}
}
2. L’état d’authentification ne se met pas à jour après la connexion
Vous appelez votre API de connexion, elle réussit, mais l’interface utilisateur affiche toujours « Connexion ».
Solution : Vous devez appeler NotifyAuthenticationStateChanged sur votre AuthenticationStateProvider après le changement de l’état d’authentification. Le framework ne détecte pas comme par magie qu’un jeton a été stocké ou qu’un cookie a été défini.
3. Autoriser l’attribut ne fonctionnant pas sur les composants
Vous ajoutez [Authorize] à un composant mais cela ne bloque pas les utilisateurs non authentifiés.
Solution : Assurez-vous d’utiliser AuthorizeRouteView au lieu de RouteView simple dans votre App.razor. La norme RouteView ignore entièrement les attributs d’autorisation.
4. Le prérendu interrompt l’état d’authentification
Lors du prérendu côté serveur dans Blazor WebAssembly, aucun jeton d’authentification n’est disponible. Les composants s’affichent comme non authentifiés, puis passent à l’état authentifié après le chargement de WASM.
Solution : Soit désactivez le prérendu pour les pages sensibles à l’authentification avec @rendermode InteractiveWebAssembly (sans prérendu), soit gérez l’état de chargement avec élégance :
<AuthorizeView>
<Authorized>
<p>Welcome back, @context.User.Identity?.Name</p>
</Authorized>
<Authorizing>
<p>Loading...</p>
</Authorizing>
<NotAuthorized>
<a href="/login">Sign in</a>
</NotAuthorized>
</AuthorizeView>
5. Expiration des jetons dans les circuits de longue durée
Les circuits Blazor Server peuvent rester en vie pendant des heures. Si votre jeton ou votre session expire, l’utilisateur reste « authentifié » dans l’interface utilisateur mais les appels d’API commencent à échouer.
Solution : implémentez une vérification périodique ou utilisez un RevalidatingServerAuthenticationStateProvider :
public class RevalidatingAuthStateProvider
: RevalidatingServerAuthenticationStateProvider
{
private readonly IServiceScopeFactory _scopeFactory;
public RevalidatingAuthStateProvider(
ILoggerFactory loggerFactory,
IServiceScopeFactory scopeFactory)
: base(loggerFactory)
{
_scopeFactory = scopeFactory;
}
protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30);
protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
await using var scope = _scopeFactory.CreateAsyncScope();
var userManager = scope.ServiceProvider
.GetRequiredService<UserManager<IdentityUser>>();
var user = await userManager.GetUserAsync(authenticationState.User);
return user is not null;
}
}
Cela confirme que l’utilisateur existe toujours (et que son cachet de sécurité n’a pas changé) toutes les 30 minutes.
Conclusion
L’authentification et l’autorisation dans Blazor nécessitent un changement de mentalité par rapport à l’ASP.NET traditionnel demande-réponse. L’abstraction AuthenticationStateProvider est la clé pour comprendre comment tout cela s’articule : une fois que vous avez intériorisé cela, le reste suit naturellement.
Pour la plupart des applications, commencez par ASP.NET Identity et les modèles intégrés. Ils gèrent le gros du travail de la gestion des utilisateurs, du hachage des mots de passe et de la génération de jetons. Ajoutez des politiques et des autorisations basées sur les revendications à mesure que vos besoins augmentent. Ajoutez des fournisseurs OAuth externes lorsque vos utilisateurs les attendent.
Le modèle d’hébergement est important : Blazor Server vous offre une posture de sécurité plus traditionnelle dans laquelle le code reste sur le serveur, tandis que WebAssembly vous pousse vers une réflexion axée sur l’API où le client n’est pas fiable de par sa conception. Ni l’un ni l’autre n’est intrinsèquement plus sûr : ils ont simplement des modèles de menace différents.
Quelle que soit l’approche que vous choisissez, n’oubliez pas la règle d’or : l’autorisation dans l’interface utilisateur est destinée à l’expérience utilisateur, l’autorisation sur le serveur est destinée à la sécurité. Appliquez toujours les deux.