Comment utiliser Identity pour sécuriser un back-end d’API web pour les applications monopages
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.
ASP.NET Core Identity fournit des API qui gèrent l’authentification, l’autorisation et la gestion des identités. Les API permettent de sécuriser les points de terminaison d’un back-end d’API web avec une authentification basée sur les cookie. Il existe une option basée sur des jetons pour les clients qui ne peuvent pas utiliser les cookie.
Cet article explique comment utiliser Identity pour sécuriser un back-end d’API web pour les SPA telles que les applications Angular, React et Vue. Les mêmes API back-end peuvent être utilisées pour sécuriser les applications Blazor WebAssembly.
Prérequis
Les étapes décrites dans cet article ajoutent l’authentification et l’autorisation à une application API web ASP.NET Core qui :
- N’est pas déjà configurée pour l’authentification.
- Cible
net8.0
ou une version ultérieure. - Peut être une API minimale ou une API basée sur un contrôleur.
Certaines des instructions de test de cet article utilisent l’interface utilisateur Swagger incluse dans le modèle de projet. L’interface utilisateur Swagger n’est pas nécessaire pour utiliser Identity avec un back-end d’API web.
Installer les packages NuGet
Installez les packages NuGet suivants :
Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Permet à Identity d’utiliser Entity Framework Core (EF Core).- Elle permet à EF Core d’utiliser une base de données, telle que l’un des packages suivants :
Pour commencer rapidement, utilisez la base de données en mémoire.
Remplacez la base de données par SQLite ou SQL Server ultérieurement pour enregistrer les données utilisateur entre les sessions lors du test ou de l’utilisation en production. Cela introduit une certaine complexité par rapport à la base de données en mémoire, car la base de données doit être crée via des migrations, comme illustré dans le tutoriel de prise en mainEF Core.
Installez ces packages à l’aide du gestionnaire de package NuGet dans Visual Studio ou de la commande CLI dotnet add package.
Créer un IdentityDbContext
Ajoutez une classe nommée ApplicationDbContext
qui hérite de IdentityDbContext<TUser> :
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) :
base(options)
{ }
}
Le code présenté fournit un constructeur spécial qui permet de configurer la base de données pour différents environnements.
Ajoutez une ou plusieurs des directives using
suivantes si nécessaire lors de l’ajout du code indiqué dans ces étapes.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
Configurer le contexte EF Core
Comme indiqué précédemment, la manière la plus simple de commencer consiste à utiliser la base de données en mémoire. Avec la base de données en mémoire, chaque exécution commence par une nouvelle base de données et il n’est pas nécessaire d’utiliser des migrations. Après l’appel à WebApplication.CreateBuilder(args)
, ajoutez le code suivant pour configurer Identity pour utiliser une base de données en mémoire :
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseInMemoryDatabase("AppDb"));
Pour enregistrer les données utilisateur entre les sessions lors du test ou de l’utilisation en production, remplacez la base de données par SQLite ou SQL Server ultérieurement.
Ajouter des services Identity au conteneur
Après l’appel à WebApplication.CreateBuilder(args)
, appelez AddAuthorization pour ajouter des services au conteneur d’injection de dépendances (DI) :
builder.Services.AddAuthorization();
Activer les API Identity
Après l’appel à WebApplication.CreateBuilder(args)
, appelez AddIdentityApiEndpoints<TUser>(IServiceCollection) et AddEntityFrameworkStores<TContext>(IdentityBuilder).
builder.Services.AddIdentityApiEndpoints<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Par défaut, les jetons cookie et propriétaires sont activés. Les cookies et les jetons sont émis lors de la connexion si le paramètre de chaîne de requête useCookies
dans le point de terminaison de connexion est true
.
Mapper des itinéraires Identity
Après l’appel à builder.Build()
, appelez MapIdentityApi<TUser>(IEndpointRouteBuilder) pour mapper les points de terminaison Identity :
app.MapIdentityApi<IdentityUser>();
Sécuriser les points de terminaison sélectionnés
Pour sécuriser un point de terminaison, utilisez la méthode d’extension RequireAuthorization sur l’appel Map{Method}
qui définit l’itinéraire. Par exemple :
app.MapGet("/weatherforecast", (HttpContext httpContext) =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
})
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi()
.RequireAuthorization();
La méthode RequireAuthorization
peut également être utilisée pour :
Sécuriser les points de terminaison de l’interface utilisateur Swagger, comme illustré dans l’exemple suivant :
app.MapSwagger().RequireAuthorization();
Sécuriser avec une revendication ou une autorisation spécifique, comme indiqué dans l’exemple suivant :
.RequireAuthorization("Admin");
Dans un projet d'API Web basé sur un contrôleur, sécurisez les points de terminaison en appliquant l'attribut [Authorize
] à un contrôleur ou à une action.
Tester l’API
Un moyen rapide de tester l’authentification consiste à utiliser la base de données en mémoire et l’interface utilisateur Swagger incluse dans le modèle de projet. Les étapes suivantes montrent comment tester l’API avec l’interface utilisateur Swagger. Assurez-vous que les points de terminaison de l’interface utilisateur Swagger ne sont pas sécurisés.
Tenter d’accéder à un point de terminaison sécurisé
- Exécutez l’application et accédez à l’interface utilisateur Swagger.
- Développez un point de terminaison sécurisé, tel que
/weatherforecast
dans un projet créé par le modèle d’API web. - Sélectionnez Essayer.
- Sélectionnez Exécuter. La réponse est
401 - not authorized
.
Tester l’inscription
Développez
/register
et sélectionnez Essayer.Dans la sectionParamètres de l’interface utilisateur, un exemple de corps de la demande s’affiche :
{ "email": "string", "password": "string" }
Remplacez « string » par une adresse e-mail et un mot de passe valides, puis sélectionnez Exécuter.
Pour respecter les règles de validation de mot de passe par défaut, le mot de passe doit comporter au moins six caractères et contenir au moins l’un des caractères suivants :
- Lettre majuscule
- Lettre minuscule
- Un chiffre numérique
- Caractère non-alphanumérique
Si vous entrez une adresse e-mail invalide ou un mot de passe incorrect, le résultat inclut les erreurs de validation. Voici un exemple de corps de réponse avec des erreurs de validation :
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "PasswordTooShort": [ "Passwords must be at least 6 characters." ], "PasswordRequiresNonAlphanumeric": [ "Passwords must have at least one non alphanumeric character." ], "PasswordRequiresDigit": [ "Passwords must have at least one digit ('0'-'9')." ], "PasswordRequiresLower": [ "Passwords must have at least one lowercase ('a'-'z')." ] } }
Les erreurs sont retournées au format ProblemDetails afin que le client puisse les analyser et afficher les erreurs de validation en fonction des besoins.
Une inscription réussie entraîne une réponse
200 - OK
.
Tester la connexion
Développez
/login
et sélectionnez Essayer. L’exemple de corps de la demande montre deux paramètres supplémentaires :{ "email": "string", "password": "string", "twoFactorCode": "string", "twoFactorRecoveryCode": "string" }
Les propriétés JSON supplémentaires ne sont pas nécessaires pour cet exemple et peuvent être supprimées. Affectez la valeur
useCookies
àtrue
.Remplacez « string » par l’adresse e-mail et le mot de passe que vous avez utilisés pour vous inscrire, puis sélectionnez Exécuter.
Une connexion réussie entraîne une réponse
200 - OK
avec un cookie en en-tête de réponse.
Retester le point de terminaison sécurisé
Après une connexion réussie, réexécutez le point de terminaison sécurisé. L’authentification cookie est automatiquement envoyée avec la demande et le point de terminaison est autorisé. L’authentification basée sur les Cookie est intégrée en toute sécurité dans le navigateur et « fonctionne simplement ».
Test avec des clients non-navigateurs
Certains clients web peuvent ne pas inclure de cookie dans l’en-tête par défaut :
Si vous utilisez un outil pour tester les API, vous devrez peut-être activer les cookie paramètres.
L’API JavaScript
fetch
n’inclut pas les cookie par défaut. Activez-les en définissantcredentials
sur la valeurinclude
dans les options.Un
HttpClient
s’exécuant dans une application Blazor WebAssembly a besoin que leHttpRequestMessage
inclue des informations d’identification, comme suit :request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
Utiliser l’authentification basée sur un jeton
Pour les clients qui ne prennent pas en charge les cookie, l’API de connexion fournit un paramètre pour demander des jetons. Un jeton personnalisé (qui est propriétaire de la plateforme d’identité ASP.NET Core) est émis et peut être utilisé pour authentifier les requêtes suivantes. Le jeton est transmis dans l’en-tête Authorization
en tant que jeton du porteur. Un jeton d’actualisation est également fourni. Ce jeton permet à l’application de demander un nouveau jeton lorsque l’ancien expire sans forcer l’utilisateur à se reconnecter.
Les jetons ne sont pas des jetons JWT (JSON Web Tokens) standard. L’utilisation de jetons personnalisés est intentionnelle, car l’API intégrée Identity est destinée principalement aux scénarios simples. L’option de jeton n’est pas destinée à être un fournisseur de services d’identité ou un serveur de jetons complet, mais plutôt une alternative à l’option cookie pour les clients qui ne peuvent pas utiliser de cookie.
Pour utiliser l’authentification basée sur les jetons, affectez la valeur false
au paramètre de chaîne de requête useCookies
lors de l’appel au point de terminaison /login
. Les jetons utilisent le schéma d’authentification du porteur. À l’aide du jeton retourné par l’appel à /login
, les appels suivants aux points de terminaison protégés doivent ajouter l’en-tête Authorization: Bearer <token>
où <token>
est le jeton d’accès. Pour plus d’informations, consultez Utilisation du point de terminaison POST /login
plus loin dans cet article.
Se déconnecter
Pour permettre à l’utilisateur de se déconnecter, définissez un point de terminaison /logout
comme dans l’exemple suivant :
app.MapPost("/logout", async (SignInManager<IdentityUser> signInManager,
[FromBody] object empty) =>
{
if (empty != null)
{
await signInManager.SignOutAsync();
return Results.Ok();
}
return Results.Unauthorized();
})
.WithOpenApi()
.RequireAuthorization();
Fournissez un objet JSON ({}
) vide dans le corps de la requête lors de l’appel à ce point de terminaison. Le code suivant est un exemple d’appel au point de terminaison de déconnexion :
public signOut() {
return this.http.post('/logout', {}, {
withCredentials: true,
observe: 'response',
responseType: 'text'
Les points de terminaison MapIdentityApi<TUser>
L’appel à MapIdentityApi<TUser>
ajoute les points de terminaison suivants à l’application :
POST /register
POST /login
POST /refresh
GET /confirmEmail
POST /resendConfirmationEmail
POST /forgotPassword
POST /resetPassword
POST /manage/2fa
GET /manage/info
POST /manage/info
Utiliser le point de terminaison POST /register
Le corps de la requête doit avoir des propriétés Email et Password :
{
"email": "string",
"password": "string",
}
Pour plus d’informations, consultez l’article suivant :
- Tester l’inscription plus haut dans cet article.
- RegisterRequest.
Utiliser le point de terminaison POST /login
Dans le corps de la requête, Email et Password sont obligatoires. Si l’authentification à deux facteurs (2FA) est activée, TwoFactorCode ou TwoFactorRecoveryCode est requis. Si 2FA n’est pas activée, omettez twoFactorCode
et twoFactorRecoveryCode
. Pour plus d’informations, consultez Utiliser le point de terminaison POST /manage/2fa
plus loin dans cet article.
Voici un exemple de corps de requête avec 2FA non activée :
{
"email": "string",
"password": "string"
}
Voici des exemples de corps de requête avec 2FA activée :
-
{ "email": "string", "password": "string", "twoFactorCode": "string", }
-
{ "email": "string", "password": "string", "twoFactorRecoveryCode": "string" }
Le point de terminaison attend un paramètre de chaîne de requête :
useCookies
: définir surtrue
pour l’authentification basée sur les cookies. Définir surfalse
ou omettre pour l’authentification basée sur les jetons.
Pour plus d’informations sur l’authentification basée sur les cookies, consultez Tester la connexion plus haut dans cet article.
Authentification basée sur un jeton
Si useCookies
est false
ou omis, l’authentification basée sur les jetons est activée. Le corps de la réponse comprend les propriétés suivantes :
{
"tokenType": "string",
"accessToken": "string",
"expiresIn": 0,
"refreshToken": "string"
}
Pour plus d’informations sur ces propriétés, consultez AccessTokenResponse.
Placez le jeton d’accès dans un en-tête pour effectuer des requêtes authentifiées, comme illustré dans l’exemple suivant.
Authorization: Bearer {access token}
Lorsque le jeton d’accès est sur le point d’expirer, appelez le point de terminaison /refresh.
Utiliser le point de terminaison POST /refresh
Pour une utilisation uniquement avec l’authentification basée sur les jetons. Obtient un nouveau jeton d’accès sans forcer l’utilisateur à se reconnecter. Appelez ce point de terminaison lorsque le jeton d’accès est sur le point d’expirer.
Le corps de la requête contient uniquement le RefreshToken. Voici un exemple de corps de requête :
{
"refreshToken": "string"
}
Si l’appel réussit, le corps de la réponse est un nouveau AccessTokenResponse, comme illustré dans l’exemple suivant :
{
"tokenType": "string",
"accessToken": "string",
"expiresIn": 0,
"refreshToken": "string"
}
Utiliser le point de terminaison GET /confirmEmail
Si Identity est configuré pour la confirmation par e-mail, un appel réussi au point de terminaison /register
entraîne l’envoi d’un e-mail contenant un lien vers le point de terminaison /confirmEmail
. Le lien contient les paramètres de chaîne de requête suivants :
userId
code
changedEmail
: inclus uniquement si l’utilisateur a changé l’adresse e-mail lors de l’inscription.
Identity fournit le texte par défaut de l’e-mail de confirmation. Par défaut, l’objet de l’e-mail est « Confirmez votre e-mail » et le corps de l’e-mail ressemble à l’exemple suivant :
Please confirm your account by <a href='https://contoso.com/confirmEmail?userId={user ID}&code={generated code}&changedEmail={new email address}'>clicking here</a>.
Si la propriété RequireConfirmedEmail a la valeur true
, l’utilisateur ne peut pas se connecter tant qu’il n’a pas confirmé l’adresse e-mail en cliquant sur le lien dans l’e-mail. Le point de terminaison /confirmEmail
:
- Confirme l’adresse e-mail et permet à l’utilisateur de se connecter.
- Retourne le texte « Merci d’avoir confirmé votre e-mail » dans le corps de la réponse.
Pour configurer Identity pour la confirmation par e-mail, ajoutez du code dans Program.cs
afin d’affecter la valeur true
à RequireConfirmedEmail
et ajoutez une classe qui implémente IEmailSender dans le conteneur d’injection de dépendances. Par exemple :
builder.Services.Configure<IdentityOptions>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
});
builder.Services.AddTransient<IEmailSender, EmailSender>();
Pour plus d’informations, consultez Confirmation de compte et récupération de mot de passe dans ASP.NET Core.
Identity fournit le texte par défaut des autres e-mails qui doivent également être envoyés, comme pour l’authentification 2FA et la réinitialisation de mot de passe. Pour personnaliser ces e-mails, fournissez une implémentation personnalisée de l’interface IEmailSender
. Dans l’exemple précédent, EmailSender
est une classe qui implémente IEmailSender
. Pour plus d’informations, notamment un exemple de classe qui implémente IEmailSender
, consultez Confirmation de compte et récupération de mot de passe dans ASP.NET Core.
Utiliser le point de terminaison POST /resendConfirmationEmail
Envoie un e-mail uniquement si l’adresse est valide pour un utilisateur inscrit.
Le corps de la requête contient uniquement le Email. Voici un exemple de corps de requête :
{
"email": "string"
}
Pour plus d’informations, consultez Utilisation du point de terminaison GET /confirmEmail
plus haut dans cet article.
Utiliser le point de terminaison POST /forgotPassword
Génère un e-mail qui contient un code de réinitialisation de mot de passe. Envoyez ce code à /resetPassword
avec un nouveau mot de passe.
Le corps de la requête contient uniquement l’Email. Voici un exemple :
{
"email": "string"
}
Pour plus d’informations sur la façon de permettre à Identity d’envoyer des e-mails, consultez Utiliser le point de terminaison GET /confirmEmail
.
Utiliser le point de terminaison POST /resetPassword
Appelez ce point de terminaison après avoir obtenu un code de réinitialisation en appelant le point de terminaison /forgotPassword
.
Le corps de la requête nécessite Email, ResetCode et NewPassword. Voici un exemple :
{
"email": "string",
"resetCode": "string",
"newPassword": "string"
}
Utiliser le point de terminaison POST /manage/2fa
Configure l’authentification à deux facteurs (2FA) pour l’utilisateur. Lorsque 2FA est activée, l’établissement d’une connexion nécessite un code généré par une application d’authentification en plus de l’adresse e-mail et du mot de passe.
Activer la 2FA
Pour activer 2FA pour l’utilisateur actuellement authentifié :
Appelez le point de terminaison
/manage/2fa
, en envoyant un objet JSON vide ({}
) dans le corps de la requête.Le corps de la réponse fournit la SharedKey ainsi que d’autres propriétés qui ne sont pas nécessaires à ce stade. La clé partagée est utilisée pour configurer l’application d’authentification. Exemple de corps de réponse :
{ "sharedKey": "string", "recoveryCodesLeft": 0, "recoveryCodes": null, "isTwoFactorEnabled": false, "isMachineRemembered": false }
Utilisez la clé partagée pour obtenir un mots de passe à usage unique et durée définie (TOTP). Pour plus d’informations, consultez Activer la génération de code QR pour les applications d’authentification TOTP dans ASP.NET Core.
Appelez le point de terminaison
/manage/2fa
, en envoyant le TOTP et"enable": true
dans le corps de la requête. Par exemple :{ "enable": true, "twoFactorCode": "string" }
Le corps de la réponse confirme que IsTwoFactorEnabled est true et fournit les RecoveryCodes. Les codes de récupération sont utilisés pour se connecter lorsque l’application d’authentification n’est pas disponible. Exemple de corps de réponse après l’activation de 2FA :
{ "sharedKey": "string", "recoveryCodesLeft": 10, "recoveryCodes": [ "string", "string", "string", "string", "string", "string", "string", "string", "string", "string" ], "isTwoFactorEnabled": true, "isMachineRemembered": false }
Se connecter avec 2FA
Appelez le point de terminaison /login
, en envoyant l’adresse e-mail, le mot de passe et le TOTP dans le corps de la requête. Par exemple :
{
"email": "string",
"password": "string",
"twoFactorCode": "string"
}
Si l’utilisateur n’a pas accès à l’application d’authentification, connectez-vous en appelant le point de terminaison /login
avec l’un des codes de récupération fournis lorsque 2FA a été activée. Le corps de votre requête ressemble à l’exemple suivant :
{
"email": "string",
"password": "string",
"twoFactorRecoveryCode": "string"
}
Réinitialiser les codes de récupération
Pour obtenir une nouvelle collection de codes de récupération, appelez ce point de terminaison avec ResetRecoveryCodes défini sur true
. Voici un exemple de corps de requête :
{
"resetRecoveryCodes": true
}
Réinitialiser la clé partagée
Pour obtenir une nouvelle clé partagée aléatoire, appelez ce point de terminaison avec ResetSharedKey défini sur true
. Voici un exemple de corps de requête :
{
"resetSharedKey": true
}
La réinitialisation de la clé désactive automatiquement l’exigence de connexion à deux facteurs pour l’utilisateur authentifié jusqu’à ce qu’elle soit réactivée par une requête ultérieure.
Oublier la machine
Pour effacer le cookie « indicateur Mémoriser mes informations » s’il est présent, appelez ce point de terminaison avec ForgetMachine défini sur true. Voici un exemple de corps de requête :
{
"forgetMachine": true
}
Ce point de terminaison n’a aucun impact sur l’authentification basée sur les jetons.
Utiliser le point de terminaison GET /manage/info
Obtient l’adresse e-mail et l’état de la confirmation par e-mail de l’utilisateur connecté. Les revendications ont été omises de ce point de terminaison pour des raisons de sécurité. Si des revendications sont nécessaires, utilisez les API côté serveur afin de configurer un point de terminaison pour les revendications. Ou au lieu de partager toutes les revendications des utilisateurs, fournissez un point de terminaison de validation qui accepte une revendication et répond si l’utilisateur la détient.
La requête ne nécessite aucun paramètre. Le corps de la réponse inclut les propriétés Email et IsEmailConfirmed, comme dans l’exemple suivant :
{
"email": "string",
"isEmailConfirmed": true
}
Utiliser le point de terminaison POST /manage/info
Met à jour l’adresse e-mail et le mot de passe de l’utilisateur connecté. Envoyez NewEmail, NewPassword et OldPassword dans le corps de la requête, comme illustré dans l’exemple suivant :
{
"newEmail": "string",
"newPassword": "string",
"oldPassword": "string"
}
Voici un exemple de corps de réponse :
{
"email": "string",
"isEmailConfirmed": false
}
Voir aussi
Pour plus d’informations, consultez les ressources suivantes :
- Choisir une solution de gestion des identités
- Identity solutions de gestion pour des applications web .NET
- Autorisation simple dans ASP.NET Core
- Ajouter, télécharger et supprimer des données utilisateur dans Identity dans un projet ASP.NET Core
- Créer une application ASP.NET Core avec des données utilisateur protégées par une autorisation
- Account confirmation and password recovery in ASP.NET Core (Confirmation de compte et récupération de mot de passe dans ASP.NET Core)
- Activer la génération de code QR pour les applications d’authentification TOTP dans ASP.NET Core
- Exemple de back-end d’API web pour les SPA Le fichier .http affiche l’authentification basée sur les jetons. Par exemple :
- Ne définit pas
useCookies
- Utilise l’en-tête d’autorisation pour passer le jeton
- Affiche l’actualisation pour étendre la session sans forcer l’utilisateur à se reconnecter
- Ne définit pas
- Exemple d’application Angular qui utilise Identity pour sécuriser un back-end d’API web
Les modèles ASP.NET Core offrent l’authentification dans les applications monopage (SPA) à l’aide de la prise en charge de l’autorisation d’API. ASP.NET Core Identity pour l’authentification et le stockage des utilisateurs est combiné avec Duende Identity Server pour implémenter OpenID Connect.
Important
Duende Software peut vous demander de payer des frais de licence pour une utilisation en production de Duende Identity Server. Pour plus d’informations, consultez Migrer de ASP.NET Core 5.0 vers 6.0.
Un paramètre d’authentification a été ajouté aux modèles de projet Angular et React. Il est similaire au paramètre d’authentification dans les modèles de projet Application web (modèle-vue-contrôleur) (MVC) et Application web (RazorPages). Les valeurs de paramètre autorisées sont Aucune et Individuelle. Le modèle de projet React.js et Redux ne prend pas en charge le paramètre d’authentification pour le moment.
Créer une application avec prise en charge de l’autorisation d’API
L’authentification et l’autorisation de l’utilisateur peuvent être utilisées à la fois avec des SPA Angular et React. Ouvrez un interpréteur de commandes et exécutez la commande suivante :
Angular :
dotnet new angular -au Individual
React :
dotnet new react -au Individual
La commande précédente crée une application ASP.NET Core avec un répertoire ClientApp contenant l’application monopage.
Description générale des composants ASP.NET Core de l’application
Les sections suivantes décrivent les ajouts au projet lorsque la prise en charge de l’authentification est incluse :
Program.cs
Les exemples de code suivants s’appuient sur le package NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServeur. Les exemples configurent l’authentification et l’autorisation d’API à l’aide des méthodes d’extension AddApiAuthorization et AddIdentityServerJwt. Les projets utilisant les modèles de projet SPA React ou Angular avec authentification incluent une référence à ce package.
dotnet new angular -au Individual
génère le fichier Program.cs
suivant :
using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using output_directory_name.Data;
using output_directory_name.Models;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
Le code précédent configure :
Identity avec l’interface utilisateur par défaut :
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
Serveur Identity avec une méthode d’assistance supplémentaire
AddApiAuthorization
qui configure certaines conventions de ASP.NET Core par défaut sur le serveur Identity :builder.Services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
Authentification avec une méthode d’assistance supplémentaire
AddIdentityServerJwt
qui configure l’application pour valider les jetons JWT produits par le serveur Identity :builder.Services.AddAuthentication() .AddIdentityServerJwt();
L’intergiciel d’authentification qui est chargé de valider les informations d’identification de la demande et de définir l’utilisateur sur le contexte de la requête :
app.UseAuthentication();
L’intergiciel serveur Identity qui expose les points de terminaison OpenID Connect :
app.UseIdentityServer();
Azure App Service sur Linux
Pour les déploiements Azure App Service sur Linux, spécifiez explicitement l’émetteur :
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
Dans le code précédent, l’espace {AUTHORITY}
réservé est le Authority à utiliser lors des appels OpenID Connect.
Exemple :
options.Authority = "https://contoso-service.azurewebsites.net";
AddApiAuthorization
Cette méthode d’assistance configure le serveur Identity pour qu’il utilise notre configuration prise en charge. Le serveur Identity est une infrastructure puissante et extensible pour gérer les problèmes de sécurité des applications. En même temps, cela expose une complexité inutile pour les scénarios les plus courants. Par conséquent, nous vous fournissons un ensemble de conventions et d’options de configuration que nous considérons comme un bon point de départ. Une fois que vos besoins d’authentification changent, la puissance totale du serveur Identity est toujours disponible pour personnaliser l’authentification en fonction de vos besoins.
AddIdentityServerJwt
Cette méthode d’assistance configure un schéma de stratégie pour l’application en tant que gestionnaire d’authentification par défaut. La stratégie est configurée pour permettre à Identity de gérer toutes les requêtes routées vers n’importe quel sous-chemin dans l’espace URL Identity « /Identity ». JwtBearerHandler
gère toutes les autres requêtes. En outre, cette méthode inscrit une ressource d’API <<ApplicationName>>API
auprès du serveur Identity avec une étendue par défaut de <<ApplicationName>>API
et configure l’intergiciel de jeton du porteur JWT pour valider les jetons émis par le serveur Identity pour l’application.
WeatherForecastController
Dans le fichier, notez l’attribut [Authorize]
appliqué à la classe qui indique que l’utilisateur doit être autorisé en fonction de la stratégie par défaut pour accéder à la ressource. La stratégie d’autorisation par défaut est configurée pour utiliser le schéma d’authentification par défaut, qui est configuré par AddIdentityServerJwt
dans le schéma de stratégie mentionné ci-dessus, ce qui fait du JwtBearerHandler
configuré par cette méthode d’assistance le gestionnaire par défaut pour les demandes adressées à l’application.
ApplicationDbContext
Dans le fichier, notez que le même DbContext
est utilisé dans Identity, à l’exception du fait qu’il étend ApiAuthorizationDbContext
(une classe plus dérivée de IdentityDbContext
) pour inclure le schéma pour le serveur Identity.
Pour obtenir un contrôle total du schéma de base de données, héritez de l’une des classes IdentityDbContext
disponibles et configurez le contexte pour inclure le schéma Identity en appelant builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
sur la méthode OnModelCreating
.
OidcConfigurationController
Dans le fichier, notez le point de terminaison approvisionné pour traiter les paramètres OIDC que le client doit utiliser.
appsettings.json
Dans le fichier appsettings.json
de la racine du projet, une nouvelle IdentityServer
section décrit la liste des clients configurés. Dans l’exemple suivant, il existe un seul client. Le nom du client correspond au nom de l’application et est mappé par convention au paramètre ClientId
OAuth. Le profil indique le type d’application en cours de configuration. Il est utilisé en interne pour générer des conventions qui simplifient le processus de configuration du serveur. Plusieurs profils sont disponibles, comme expliqué dans la section Profils d’application.
"IdentityServer": {
"Clients": {
"angularindividualpreview3final": {
"Profile": "IdentityServerSPA"
}
}
}
appsettings.Development.json
Dans le fichier appsettings.Development.json
de la racine du projet, une section IdentityServer
décrit la clé utilisée pour signer les jetons. Lors du déploiement en production, une clé doit être approvisionnée et déployée en même temps que l’application, comme expliqué dans la section Déployer dans un environnement de production.
"IdentityServer": {
"Key": {
"Type": "Development"
}
}
Description générale de l’application Angular
La prise en charge de l’authentification et de l’autorisation de l’API dans le modèle Angular réside dans son propre module Angular dans le répertoire ClientApp/src/api-authorization. Le module est constitué des éléments suivants :
- 3 composants :
login.component.ts
: gère le flux de connexion de l’application.logout.component.ts
: gère le flux de déconnexion de l’application.login-menu.component.ts
: widget qui affiche l’un des ensembles de liens suivants :- Gestion des profils utilisateur et liens de déconnexion lorsque l’utilisateur est authentifié.
- Liens d’inscription et de connexion lorsque l’utilisateur n’est pas authentifié.
- Protection
AuthorizeGuard
qui peut être ajoutée aux routes et nécessite l’authentification d’un utilisateur avant d’accéder à la route. - Intercepteur HTTP
AuthorizeInterceptor
qui attache le jeton d’accès aux requêtes HTTP sortantes ciblant l’API lorsque l’utilisateur est authentifié. - Service
AuthorizeService
qui gère les détails de niveau inférieur du processus d’authentification et expose des informations sur l’utilisateur authentifié au reste de l’application pour consommation. - Un module Angular qui définit les routes associées aux parties d’authentification de l’application. Il expose le composant de menu de connexion, l’intercepteur, la protection et le service au reste de l’application pour consommation.
Description générale de l’application React
La prise en charge de l’authentification et de l’autorisation d’API dans le modèle React réside dans le répertoire ClientApp/src/components/api-authorization. Il est constitué des éléments suivants :
- 4 composants :
Login.js
: gère le flux de connexion de l’application.Logout.js
: gère le flux de déconnexion de l’application.LoginMenu.js
: widget qui affiche l’un des ensembles de liens suivants :- Gestion des profils utilisateur et liens de déconnexion lorsque l’utilisateur est authentifié.
- Liens d’inscription et de connexion lorsque l’utilisateur n’est pas authentifié.
AuthorizeRoute.js
: composant de routage qui nécessite l’authentification d’un utilisateur avant d’afficher le composant indiqué dans le paramètreComponent
.
authService
Instance exportée de classeAuthorizeService
qui gère les détails de niveau inférieur du processus d’authentification et expose des informations sur l’utilisateur authentifié au reste de l’application pour consommation.
Maintenant que vous avez vu les principaux composants de la solution, vous pouvez examiner plus en détail les différents scénarios de l’application.
Exiger une autorisation sur une nouvelle API
Par défaut, le système est configuré pour exiger facilement une autorisation pour les nouvelles API. Pour ce faire, créez un nouveau contrôleur et ajoutez l’attribut [Authorize]
à la classe de contrôleur ou à toute action au sein du contrôleur.
Personnaliser le gestionnaire d’authentification d’API
Pour personnaliser la configuration du gestionnaire JWT de l’API, configurez son instance JwtBearerOptions :
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
...
});
Le gestionnaire JWT de l’API déclenche des événements qui permettent de contrôler le processus d’authentification à l’aide de JwtBearerEvents
. Pour assurer la prise en charge de l’autorisation d’API, AddIdentityServerJwt
inscrit ses propres gestionnaires d’événements.
Pour personnaliser le traitement d’un événement, ajoutez si besoin une logique supplémentaire au wrapper du gestionnaire d’événements existant. Par exemple :
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
...
}
});
Dans le code précédent, le gestionnaire d’événements OnTokenValidated
est remplacé par une implémentation personnalisée. Cette implémentation :
- Appelle l’implémentation d’origine fournie par la prise en charge de l’autorisation de l’API.
- Exécute sa propre logique personnalisée.
Protège une route côté client (Angular)
La protection d’une route côté client s’effectue en ajoutant la protection d’autorisation à la liste des protections à exécuter lors de la configuration d’un itinéraire. Par exemple, vous pouvez voir comment la route fetch-data
est configurée dans le module principal d’application Angular :
RouterModule.forRoot([
// ...
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])
Il est important de mentionner que la protection d’une route ne protège pas le point de terminaison réel (qui nécessite toujours un attribut [Authorize]
), mais qu’elle empêche uniquement l’utilisateur de naviguer vers la route côté client lorsqu'il n'est pas authentifié.
Authentifier les requêtes API (Angular)
L’authentification des requêtes API hébergées en même temps que l’application s’effectue automatiquement via l’utilisation de l’intercepteur client HTTP défini par l’application.
Protéger une route côté client (React)
Protégez une route côté client en utilisant le composant AuthorizeRoute
plutôt que le composant brut Route
. Par exemple, notez comment la route fetch-data
est configurée dans le composant App
:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
Protéger une route :
- Ne protège pas le point de terminaison réel (qui nécessite toujours qu’un attribut
[Authorize]
lui soit appliqué). - Empêche l’utilisateur de naviguer vers la route côté client donnée uniquement lorsqu’il n’est pas authentifié.
Authentifier les requêtes API (React)
L’authentification des demandes avec React est effectuée en important d’abord l’instance authService
depuis AuthorizeService
. Le jeton d’accès est récupéré depuis authService
et est attaché à la requête, comme indiqué ci-dessous. Dans les composants React, ce travail est généralement effectué dans la méthode de cycle de vie componentDidMount
ou en tant que résultat d’une interaction utilisateur.
Importer authService
dans un composant
import authService from './api-authorization/AuthorizeService'
Récupérer et joindre le jeton d’accès à la réponse
async populateWeatherData() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/WeatherForecasts', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
Déployer en production
Pour déployer l’application en production, les ressources suivantes doivent être approvisionnées :
- Une base de données pour stocker les comptes d’utilisateur Identityet les autorisations du serveur Identity.
- Un certificat de production à utiliser pour la signature des jetons.
- Il n’existe aucune exigence spécifique pour ce certificat ; il peut s’agir d’un certificat auto-signé ou d’un certificat provisionné par le biais d’une autorité d’autorité de certification.
- Il peut être généré via des outils standard tels que PowerShell ou OpenSSL.
- Il peut être installé dans le magasin de certificats sur les ordinateurs cibles ou déployé sous la forme d’un fichier .pfx avec un mot de passe fort.
Exemple : déploiement sur un hébergeur non-Azure
Dans votre panneau d’hébergement web, créez ou chargez votre certificat. Ensuite, dans le fichier appsettings.json
de l’application, modifiez la section IdentityServer
pour inclure les détails de la clé. Par exemple :
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "WebHosting",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
Dans l’exemple précédent :
StoreName
représente le nom du magasin de certificats dans lequel le certificat est stocké. Dans ce cas, il pointe vers le magasin d’hébergement web.StoreLocation
représente l’emplacement à partir duquel charger le certificat (CurrentUser
dans ce cas).Name
correspond à l’objet unique du certificat.
Exemple : déploiement sur Azure App Service
Cette section décrit le déploiement de l’application sur Azure App Service à l’aide d’un certificat stocké dans le magasin de certificats. Pour modifier l’application afin de charger un certificat à partir du magasin de certificats, un plan de service de niveau Standard ou mieux est requis lorsque vous configurez l’application dans le Portail Azure à une étape ultérieure.
Dans le fichier appsettings.json
de l’application, modifiez la section IdentityServer
pour inclure les détails de la clé :
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
- Le nom du magasin représente le nom du magasin de certificats dans lequel le certificat est stocké. Dans ce cas, il pointe vers le magasin d’utilisateurs personnels.
- L’emplacement du magasin représente l’emplacement à partir duquel charger le certificat (
CurrentUser
ouLocalMachine
). - La propriété name sur le certificat correspond à l’objet unique du certificat.
Pour déployer sur Azure App Service, suivez les étapes décrites dans Déployer l’application sur Azure, qui explique comment créer les ressources Azure nécessaires et déployer l’application en production.
Après avoir suivi les instructions précédentes, l’application est déployée sur Azure, mais n’est pas encore fonctionnelle. Le certificat utilisé par l’application doit être configuré dans le Portail Azure. Recherchez l’empreinte du certificat et suivez les étapes décrites dans Charger vos certificats.
Bien que ces étapes mentionnent SSL, il existe une section Certificats privés dans le Portail Azure où vous pouvez charger le certificat provisionné à utiliser avec l’application.
Après avoir configuré l’application et les paramètres de l’application dans le Portail Azure, redémarrez l’application dans le portail.
Autres options de configuration
La prise en charge de l’autorisation d’API s’appuie sur le serveur Identityavec un ensemble de conventions, de valeurs par défaut et d’améliorations pour simplifier l’expérience pour les applications monopage. Inutile de dire que toute la puissance du serveur Identityest disponible en arrière-plan si les intégrations ASP.NET Core ne couvrent pas votre scénario. Le support d’ASP.NET Core est axé sur les applications « internes », où toutes les applications sont créées et déployées par notre organisation. Par conséquent, le support n’est pas offert pour des éléments tels que le consentement ou la fédération. Pour ces scénarios, utilisez le serveur Identity et suivez sa documentation.
Profils d’application
Les profils d’application sont des configurations prédéfinies pour les applications qui définissent davantage leurs paramètres. À ce stade, les profils suivants sont pris en charge :
IdentityServerSPA
: représente une SPA hébergée avec le serveur Identity en tant qu’unité unique.- La valeur
redirect_uri
par défaut est/authentication/login-callback
. - La valeur
post_logout_redirect_uri
par défaut est/authentication/logout-callback
. - L’ensemble d’étendues inclut
openid
,profile
, et chaque étendue définie pour les API dans l’application. - L’ensemble des types de réponse OIDC autorisés est
id_token token
ou chacun d’eux individuellement (id_token
,token
). - Le mode de réponse autorisé est
fragment
.
- La valeur
SPA
: représente une SPA qui n’est pas hébergée avec le serveur Identity.- L’ensemble d’étendues inclut
openid
,profile
, et chaque étendue définie pour les API dans l’application. - L’ensemble des types de réponse OIDC autorisés est
id_token token
ou chacun d’eux individuellement (id_token
,token
). - Le mode de réponse autorisé est
fragment
.
- L’ensemble d’étendues inclut
IdentityServerJwt
: représente une API hébergée avec le serveur Identity.- L’application est configurée pour avoir une étendue unique qui correspond par défaut au nom de l’application.
API
: représente une API qui n’est pas hébergée avec le serveur Identity.- L’application est configurée pour avoir une étendue unique qui correspond par défaut au nom de l’application.
Configuration via AppSettings
Configurez les applications via le système de configuration en les ajoutant à la liste de Clients
ou Resources
.
Configurez la propriété redirect_uri
et post_logout_redirect_uri
de chaque client, comme illustré dans l’exemple suivant :
"IdentityServer": {
"Clients": {
"MySPA": {
"Profile": "SPA",
"RedirectUri": "https://www.example.com/authentication/login-callback",
"LogoutUri": "https://www.example.com/authentication/logout-callback"
}
}
}
Lors de la configuration des ressources, vous pouvez configurer les étendues de la ressource comme indiqué ci-dessous :
"IdentityServer": {
"Resources": {
"MyExternalApi": {
"Profile": "API",
"Scopes": "a b c"
}
}
}
Configuration via du code
Vous pouvez également configurer les clients et les ressources via le code à l’aide d’une surcharge de AddApiAuthorization
qui prend une action pour configurer les options.
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddSPA(
"My SPA", spa =>
spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
.WithLogoutRedirectUri(
"http://www.example.com/authentication/logout-callback"));
options.ApiResources.AddApiResource("MyExternalApi", resource =>
resource.WithScopes("a", "b", "c"));
});
Ressources supplémentaires
Les modèles ASP.NET Core 3.1 et ultérieurs offrent l’authentification dans les applications monopage (SPA) à l’aide de la prise en charge de l’autorisation d’API. ASP.NET Core Identity pour l’authentification et le stockage des utilisateurs est combiné avec Identityserveur pour implémenter OpenID Connect.
Un paramètre d’authentification a été ajouté aux modèles de projet Angular et React. Il est similaire au paramètre d’authentification dans les modèles de projet Application web (modèle-vue-contrôleur) (MVC) et Application web (RazorPages). Les valeurs de paramètre autorisées sont Aucune et Individuelle. Le modèle de projet React.js et Redux ne prend pas en charge le paramètre d’authentification pour le moment.
Créer une application avec prise en charge de l’autorisation d’API
L’authentification et l’autorisation de l’utilisateur peuvent être utilisées à la fois avec des SPA Angular et React. Ouvrez un interpréteur de commandes et exécutez la commande suivante :
Angular :
dotnet new angular -o <output_directory_name>
React :
dotnet new react -o <output_directory_name> -au Individual
La commande précédente crée une application ASP.NET Core avec un répertoire ClientApp contenant l’application monopage.
Description générale des composants ASP.NET Core de l’application
Les sections suivantes décrivent les ajouts au projet lorsque la prise en charge de l’authentification est incluse :
Classe Startup
Les exemples de code suivants s’appuient sur le package NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServeur. Les exemples configurent l’authentification et l’autorisation d’API à l’aide des méthodes d’extension AddApiAuthorization et AddIdentityServerJwt. Les projets utilisant les modèles de projet SPA React ou Angular avec authentification incluent une référence à ce package.
La classe Startup
possède les ajouts suivants :
À l’intérieur de la méthode
Startup.ConfigureServices
:Identity avec l’interface utilisateur par défaut :
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity<ApplicationUser>() .AddEntityFrameworkStores<ApplicationDbContext>();
Serveur Identity avec une méthode d’assistance supplémentaire
AddApiAuthorization
qui configure certaines conventions de ASP.NET Core par défaut sur le serveur Identity :services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
Authentification avec une méthode d’assistance supplémentaire
AddIdentityServerJwt
qui configure l’application pour valider les jetons JWT produits par le serveur Identity :services.AddAuthentication() .AddIdentityServerJwt();
À l’intérieur de la méthode
Startup.Configure
:L’intergiciel d’authentification qui est chargé de valider les informations d’identification de la demande et de définir l’utilisateur sur le contexte de la requête :
app.UseAuthentication();
L’intergiciel serveur Identity qui expose les points de terminaison OpenID Connect :
app.UseIdentityServer();
Azure App Service sur Linux
Pour les déploiements Azure App Service sur Linux, spécifiez explicitement l’émetteur dans Startup.ConfigureServices
:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
Dans le code précédent, l’espace {AUTHORITY}
réservé est le Authority à utiliser lors des appels OpenID Connect.
Exemple :
options.Authority = "https://contoso-service.azurewebsites.net";
AddApiAuthorization
Cette méthode d’assistance configure le serveur Identity pour qu’il utilise notre configuration prise en charge. Le serveur Identity est une infrastructure puissante et extensible pour gérer les problèmes de sécurité des applications. En même temps, cela expose une complexité inutile pour les scénarios les plus courants. Par conséquent, nous vous fournissons un ensemble de conventions et d’options de configuration que nous considérons comme un bon point de départ. Une fois que vos besoins d’authentification changent, la puissance totale du serveur Identity est toujours disponible pour personnaliser l’authentification en fonction de vos besoins.
AddIdentityServerJwt
Cette méthode d’assistance configure un schéma de stratégie pour l’application en tant que gestionnaire d’authentification par défaut. La stratégie est configurée pour permettre à Identity de gérer toutes les requêtes routées vers n’importe quel sous-chemin dans l’espace URL Identity « /Identity ». JwtBearerHandler
gère toutes les autres requêtes. En outre, cette méthode inscrit une ressource d’API <<ApplicationName>>API
auprès du serveur Identity avec une étendue par défaut de <<ApplicationName>>API
et configure l’intergiciel de jeton du porteur JWT pour valider les jetons émis par le serveur Identity pour l’application.
WeatherForecastController
Dans le fichier, notez l’attribut [Authorize]
appliqué à la classe qui indique que l’utilisateur doit être autorisé en fonction de la stratégie par défaut pour accéder à la ressource. La stratégie d’autorisation par défaut est configurée pour utiliser le schéma d’authentification par défaut, qui est configuré par AddIdentityServerJwt
dans le schéma de stratégie mentionné ci-dessus, ce qui fait du JwtBearerHandler
configuré par cette méthode d’assistance le gestionnaire par défaut pour les demandes adressées à l’application.
ApplicationDbContext
Dans le fichier, notez que le même DbContext
est utilisé dans Identity, à l’exception du fait qu’il étend ApiAuthorizationDbContext
(une classe plus dérivée de IdentityDbContext
) pour inclure le schéma pour le serveur Identity.
Pour obtenir un contrôle total du schéma de base de données, héritez de l’une des classes IdentityDbContext
disponibles et configurez le contexte pour inclure le schéma Identity en appelant builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
sur la méthode OnModelCreating
.
OidcConfigurationController
Dans le fichier, notez le point de terminaison approvisionné pour traiter les paramètres OIDC que le client doit utiliser.
appsettings.json
Dans le fichier appsettings.json
de la racine du projet, une nouvelle IdentityServer
section décrit la liste des clients configurés. Dans l’exemple suivant, il existe un seul client. Le nom du client correspond au nom de l’application et est mappé par convention au paramètre ClientId
OAuth. Le profil indique le type d’application en cours de configuration. Il est utilisé en interne pour générer des conventions qui simplifient le processus de configuration du serveur. Plusieurs profils sont disponibles, comme expliqué dans la section Profils d’application.
"IdentityServer": {
"Clients": {
"angularindividualpreview3final": {
"Profile": "IdentityServerSPA"
}
}
}
appsettings.Development.json
Dans le fichier appsettings.Development.json
de la racine du projet, une section IdentityServer
décrit la clé utilisée pour signer les jetons. Lors du déploiement en production, une clé doit être approvisionnée et déployée en même temps que l’application, comme expliqué dans la section Déployer dans un environnement de production.
"IdentityServer": {
"Key": {
"Type": "Development"
}
}
Description générale de l’application Angular
La prise en charge de l’authentification et de l’autorisation de l’API dans le modèle Angular réside dans son propre module Angular dans le répertoire ClientApp/src/api-authorization. Le module est constitué des éléments suivants :
- 3 composants :
login.component.ts
: gère le flux de connexion de l’application.logout.component.ts
: gère le flux de déconnexion de l’application.login-menu.component.ts
: widget qui affiche l’un des ensembles de liens suivants :- Gestion des profils utilisateur et liens de déconnexion lorsque l’utilisateur est authentifié.
- Liens d’inscription et de connexion lorsque l’utilisateur n’est pas authentifié.
- Protection
AuthorizeGuard
qui peut être ajoutée aux routes et nécessite l’authentification d’un utilisateur avant d’accéder à la route. - Intercepteur HTTP
AuthorizeInterceptor
qui attache le jeton d’accès aux requêtes HTTP sortantes ciblant l’API lorsque l’utilisateur est authentifié. - Service
AuthorizeService
qui gère les détails de niveau inférieur du processus d’authentification et expose des informations sur l’utilisateur authentifié au reste de l’application pour consommation. - Un module Angular qui définit les routes associées aux parties d’authentification de l’application. Il expose le composant de menu de connexion, l’intercepteur, la protection et le service au reste de l’application pour consommation.
Description générale de l’application React
La prise en charge de l’authentification et de l’autorisation d’API dans le modèle React réside dans le répertoire ClientApp/src/components/api-authorization. Il est constitué des éléments suivants :
- 4 composants :
Login.js
: gère le flux de connexion de l’application.Logout.js
: gère le flux de déconnexion de l’application.LoginMenu.js
: widget qui affiche l’un des ensembles de liens suivants :- Gestion des profils utilisateur et liens de déconnexion lorsque l’utilisateur est authentifié.
- Liens d’inscription et de connexion lorsque l’utilisateur n’est pas authentifié.
AuthorizeRoute.js
: composant de routage qui nécessite l’authentification d’un utilisateur avant d’afficher le composant indiqué dans le paramètreComponent
.
authService
Instance exportée de classeAuthorizeService
qui gère les détails de niveau inférieur du processus d’authentification et expose des informations sur l’utilisateur authentifié au reste de l’application pour consommation.
Maintenant que vous avez vu les principaux composants de la solution, vous pouvez examiner plus en détail les différents scénarios de l’application.
Exiger une autorisation sur une nouvelle API
Par défaut, le système est configuré pour exiger facilement une autorisation pour les nouvelles API. Pour ce faire, créez un nouveau contrôleur et ajoutez l’attribut [Authorize]
à la classe de contrôleur ou à toute action au sein du contrôleur.
Personnaliser le gestionnaire d’authentification d’API
Pour personnaliser la configuration du gestionnaire JWT de l’API, configurez son instance JwtBearerOptions :
services.AddAuthentication()
.AddIdentityServerJwt();
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
...
});
Le gestionnaire JWT de l’API déclenche des événements qui permettent de contrôler le processus d’authentification à l’aide de JwtBearerEvents
. Pour assurer la prise en charge de l’autorisation d’API, AddIdentityServerJwt
inscrit ses propres gestionnaires d’événements.
Pour personnaliser le traitement d’un événement, ajoutez si besoin une logique supplémentaire au wrapper du gestionnaire d’événements existant. Par exemple :
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
...
}
});
Dans le code précédent, le gestionnaire d’événements OnTokenValidated
est remplacé par une implémentation personnalisée. Cette implémentation :
- Appelle l’implémentation d’origine fournie par la prise en charge de l’autorisation de l’API.
- Exécute sa propre logique personnalisée.
Protège une route côté client (Angular)
La protection d’une route côté client s’effectue en ajoutant la protection d’autorisation à la liste des protections à exécuter lors de la configuration d’un itinéraire. Par exemple, vous pouvez voir comment la route fetch-data
est configurée dans le module principal d’application Angular :
RouterModule.forRoot([
// ...
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])
Il est important de mentionner que la protection d’une route ne protège pas le point de terminaison réel (qui nécessite toujours un attribut [Authorize]
), mais qu’elle empêche uniquement l’utilisateur de naviguer vers la route côté client lorsqu'il n'est pas authentifié.
Authentifier les requêtes API (Angular)
L’authentification des requêtes API hébergées en même temps que l’application s’effectue automatiquement via l’utilisation de l’intercepteur client HTTP défini par l’application.
Protéger une route côté client (React)
Protégez une route côté client en utilisant le composant AuthorizeRoute
plutôt que le composant brut Route
. Par exemple, notez comment la route fetch-data
est configurée dans le composant App
:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
Protéger une route :
- Ne protège pas le point de terminaison réel (qui nécessite toujours qu’un attribut
[Authorize]
lui soit appliqué). - Empêche l’utilisateur de naviguer vers la route côté client donnée uniquement lorsqu’il n’est pas authentifié.
Authentifier les requêtes API (React)
L’authentification des demandes avec React est effectuée en important d’abord l’instance authService
depuis AuthorizeService
. Le jeton d’accès est récupéré depuis authService
et est attaché à la requête, comme indiqué ci-dessous. Dans les composants React, ce travail est généralement effectué dans la méthode de cycle de vie componentDidMount
ou en tant que résultat d’une interaction utilisateur.
Importer authService
dans un composant
import authService from './api-authorization/AuthorizeService'
Récupérer et joindre le jeton d’accès à la réponse
async populateWeatherData() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/WeatherForecasts', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
Déployer en production
Pour déployer l’application en production, les ressources suivantes doivent être approvisionnées :
- Une base de données pour stocker les comptes d’utilisateur Identityet les autorisations du serveur Identity.
- Un certificat de production à utiliser pour la signature des jetons.
- Il n’existe aucune exigence spécifique pour ce certificat ; il peut s’agir d’un certificat auto-signé ou d’un certificat provisionné par le biais d’une autorité d’autorité de certification.
- Il peut être généré via des outils standard tels que PowerShell ou OpenSSL.
- Il peut être installé dans le magasin de certificats sur les ordinateurs cibles ou déployé sous la forme d’un fichier .pfx avec un mot de passe fort.
Exemple : déploiement sur un hébergeur non-Azure
Dans votre panneau d’hébergement web, créez ou chargez votre certificat. Ensuite, dans le fichier appsettings.json
de l’application, modifiez la section IdentityServer
pour inclure les détails de la clé. Par exemple :
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "WebHosting",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
Dans l’exemple précédent :
StoreName
représente le nom du magasin de certificats dans lequel le certificat est stocké. Dans ce cas, il pointe vers le magasin d’hébergement web.StoreLocation
représente l’emplacement à partir duquel charger le certificat (CurrentUser
dans ce cas).Name
correspond à l’objet unique du certificat.
Exemple : déploiement sur Azure App Service
Cette section décrit le déploiement de l’application sur Azure App Service à l’aide d’un certificat stocké dans le magasin de certificats. Pour modifier l’application afin de charger un certificat à partir du magasin de certificats, un plan de service de niveau Standard ou mieux est requis lorsque vous configurez l’application dans le Portail Azure à une étape ultérieure.
Dans le fichier appsettings.json
de l’application, modifiez la section IdentityServer
pour inclure les détails de la clé :
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
- Le nom du magasin représente le nom du magasin de certificats dans lequel le certificat est stocké. Dans ce cas, il pointe vers le magasin d’utilisateurs personnels.
- L’emplacement du magasin représente l’emplacement à partir duquel charger le certificat (
CurrentUser
ouLocalMachine
). - La propriété name sur le certificat correspond à l’objet unique du certificat.
Pour déployer sur Azure App Service, suivez les étapes décrites dans Déployer l’application sur Azure, qui explique comment créer les ressources Azure nécessaires et déployer l’application en production.
Après avoir suivi les instructions précédentes, l’application est déployée sur Azure, mais n’est pas encore fonctionnelle. Le certificat utilisé par l’application doit être configuré dans le Portail Azure. Recherchez l’empreinte du certificat et suivez les étapes décrites dans Charger vos certificats.
Bien que ces étapes mentionnent SSL, il existe une section Certificats privés dans le Portail Azure où vous pouvez charger le certificat provisionné à utiliser avec l’application.
Après avoir configuré l’application et les paramètres de l’application dans le Portail Azure, redémarrez l’application dans le portail.
Autres options de configuration
La prise en charge de l’autorisation d’API s’appuie sur le serveur Identityavec un ensemble de conventions, de valeurs par défaut et d’améliorations pour simplifier l’expérience pour les applications monopage. Inutile de dire que toute la puissance du serveur Identityest disponible en arrière-plan si les intégrations ASP.NET Core ne couvrent pas votre scénario. Le support d’ASP.NET Core est axé sur les applications « internes », où toutes les applications sont créées et déployées par notre organisation. Par conséquent, le support n’est pas offert pour des éléments tels que le consentement ou la fédération. Pour ces scénarios, utilisez le serveur Identity et suivez sa documentation.
Profils d’application
Les profils d’application sont des configurations prédéfinies pour les applications qui définissent davantage leurs paramètres. À ce stade, les profils suivants sont pris en charge :
IdentityServerSPA
: représente une SPA hébergée avec le serveur Identity en tant qu’unité unique.- La valeur
redirect_uri
par défaut est/authentication/login-callback
. - La valeur
post_logout_redirect_uri
par défaut est/authentication/logout-callback
. - L’ensemble d’étendues inclut
openid
,profile
, et chaque étendue définie pour les API dans l’application. - L’ensemble des types de réponse OIDC autorisés est
id_token token
ou chacun d’eux individuellement (id_token
,token
). - Le mode de réponse autorisé est
fragment
.
- La valeur
SPA
: représente une SPA qui n’est pas hébergée avec le serveur Identity.- L’ensemble d’étendues inclut
openid
,profile
, et chaque étendue définie pour les API dans l’application. - L’ensemble des types de réponse OIDC autorisés est
id_token token
ou chacun d’eux individuellement (id_token
,token
). - Le mode de réponse autorisé est
fragment
.
- L’ensemble d’étendues inclut
IdentityServerJwt
: représente une API hébergée avec le serveur Identity.- L’application est configurée pour avoir une étendue unique qui correspond par défaut au nom de l’application.
API
: représente une API qui n’est pas hébergée avec le serveur Identity.- L’application est configurée pour avoir une étendue unique qui correspond par défaut au nom de l’application.
Configuration via AppSettings
Configurez les applications via le système de configuration en les ajoutant à la liste de Clients
ou Resources
.
Configurez la propriété redirect_uri
et post_logout_redirect_uri
de chaque client, comme illustré dans l’exemple suivant :
"IdentityServer": {
"Clients": {
"MySPA": {
"Profile": "SPA",
"RedirectUri": "https://www.example.com/authentication/login-callback",
"LogoutUri": "https://www.example.com/authentication/logout-callback"
}
}
}
Lors de la configuration des ressources, vous pouvez configurer les étendues de la ressource comme indiqué ci-dessous :
"IdentityServer": {
"Resources": {
"MyExternalApi": {
"Profile": "API",
"Scopes": "a b c"
}
}
}
Configuration via du code
Vous pouvez également configurer les clients et les ressources via le code à l’aide d’une surcharge de AddApiAuthorization
qui prend une action pour configurer les options.
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddSPA(
"My SPA", spa =>
spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
.WithLogoutRedirectUri(
"http://www.example.com/authentication/logout-callback"));
options.ApiResources.AddApiResource("MyExternalApi", resource =>
resource.WithScopes("a", "b", "c"));
});