Authenticatie in ASP .NET Core 3.1

Authenticatie en autorisatie worden vaak in één adem genoemd en zijn in zekere zin ook onlosmakelijk aan elkaar verbonden. Toch hebben ze ieder hun specifieke verantwoordelijkheid. Met behulp van authenticatie kunnen gebruikers van een webapplicatie zich identificeren om toegang te krijgen tot de applicatie. Met autorisatie kunnen ze toegang krijgen tot specifieke functies en functionaliteit van een webapplicatie.

ASP.NET Core Identity is een zogenaamd membership system dat gebruikt wordt bij het bouwen van ASP.NET Core-webapplicaties, inclusief lidmaatschap, inloggen en gebruikersgegevens. ASP.NET Core Identity maakt het mogelijk om inlogfuncties toe te voegen aan een webapplicatie en maakt het eenvoudig om gegevens over de ingelogde gebruiker toe te passen.

ASP.NET Core Identity maakt authenticatie voor één enkele applicatie mogelijk. Het heeft zoals aangegeven redelijk wat features “out-of-the-box”. Wanneer je authenticatie op een meer gecentraliseerde en geïsoleerde manier wilt gebruiken voor meerdere client applicaties is het beter te kijken naar een token service met OAuth 2.0 and OpenID Connect implementatie zoals IdentityServer.

In dit artikel wordt één van de manieren beschreven waarop binnen ASP.NET Core 3.1 authenticatie kan worden geregeld met behulp van Identity Core. Het uitgebreidere IdentityServer behoort niet tot de scope van dit artikel.

De volgende items zullen aan de orde komen:

  • Opzetten ASP.NET Core Identity framework (EF Core 3.1 & migrations)
  • Registratie van nieuwe gebruikers
  • In-/uitloggen van gebruikers
  • Account Lockout mechanisme
  • Custom validatie naast de al in Identity aanwezige default validatie

Opzetten ASP.NET Core Identity framework

Er bestaan in Visual Studio diverse templates die kunnen worden gebruikt bij het opzetten van een Identity Core framework. In alle gevallen is de basis een context die is afgeleid van IdentityDbContext. Dit is de base class voor de EF Core database context die gebruikt wordt voor Identity en waaraan het User objecttype wordt meegegeven:

In de OnModelCreating methode kan een verdere configuratie van de objecten plaatsvinden zoals het verplicht maken van velden of het initiëren van de tabellen met bepaalde default data.

Daarnaast is in bovenstaande code snippet een DbSet aangemaakt die gebruikt gaat worden als IdentityUser object (en die is afgeleid van de IdentityUser base class).

Het registreren van de Identity DB context service en de setup voor het gebruik van (in dit geval) SQL Server gebeurt in de ConfigureServices methode van startup.cs:

Na het uitvoeren van de diverse EF Core migration commando’s is er in SQL Server een Identity database aangemaakt met daarin een aantal standaard Identity tabellen (welke allemaal de prefix ‘AspNet’ hebben):

In de volgende paragrafen zullen de betekenis en functie van diverse velden uit m.n. de AspNetUsers tabel verder aan de orde komen. Voor dit artikel zijn de volgende tabellen en hun onderlinge relatie van belang:

Om de Identity Core functionaliteit beschikbaar te maken in de applicatie zijn tot slot twee zaken nodig in de startup.cs:

  1. Het registreren van de services voor het ASP.NET Core Identity framework (in de ConfigureServices methode):
  1. Het toevoegen van de authenticatie middleware (in de Configure methode):

Registratie van nieuwe gebruikers

Om nieuwe Identity Users te registreren wordt gebruik gemaakt van de UserManager class. Deze class bevindt zich in de namespace Microsoft.AspNetCore.Identity en wordt d.m.v. dependecy injection in een controller geïnjecteerd:

Om een nieuwe gebruiker te registreren is het noodzakelijk naast een gebruikersnaam en/of emailadres ook een wachtwoord op te geven. Met behulp van deze gegevens kan een gebruiker door de applicatie geauthentiseerd worden.

Er kan m.b.t. authenticatie gekozen worden voor de combinatie gebruikersnaam/wachtwoord of emailadres/wachtwoord. Wanneer voor de laatste optie wordt gekozen is het aan te raden tijdens het registreren van de services voor het ASP.NET Core Identity framework de optie mee te geven dat gevalideerd moet worden dat het emailadres van de nieuw toe te voegen Identity User uniek is (User.RequireUniqueEmail = true).

Voor de eerste optie geldt by design dat het Identity framework valideert dat de gebruikersnaam van de nieuw toe te voegen Identity User uniek is.

Registratie gebeurt vervolgens in een POST action in de controller:

De return value van deze (async) functie is van het type IdentityResult en bevat een boolean flag Succeeded die aangeeft of de actie geslaagd is of niet. Indien de actie niet succesvol was bevat de property Errors de fout(en) die is/zijn opgetreden bij de CreateAsync actie.

Het wachtwoord wordt (indien de operatie succesvol was) volgens een hashing algoritme encrypted opgeslagen in de database. Daarnaast worden enkele velden automatisch gevuld zoals het NormalizedUserName en het NormalizedEmail veld.

In-/uitloggen van gebruikers

Zoals aangegeven worden gebruikers geauthentiseerd op basis van een gebruikersnaam/wachtwoord of emailadres/wachtwoord combinatie. Het ASP.NET Core Identity framework biedt hiervoor twee mogelijkheden die ieder een verschillend doel dienen:

  1. CheckPasswordAsync

Deze methode hasht het opgegeven wachtwoord en vergelijkt het met de bestaande wachtwoordhash (zoals dat bijvoorbeeld is opgeslagen in de database)

  1. PasswordSignInAsync

Deze methode doet naast het controleren van het wachtwoord veel meer:

  • Controleert of inloggen is toegestaan. Als de gebruiker bijvoorbeeld een bevestigde e-mail moet hebben voordat hij zich mag aanmelden, retourneert de methode Failed
  • Roept UserManager.CheckPasswordAsync op om te controleren of het wachtwoord correct is. Wanneer een mislukte inlogpoging (wachtwoord is niet correct en de Lockout optie is enabled) het geconfigureerde maximum aantal mislukte aanmeldingspogingen overschrijdt wordt het account van de gebruiker geblokkeerd
  • Als de optie two-factor authentication is ingeschakeld voor de gebruiker, stelt deze methode de cookie in en retourneert TwoFactorRequired
  • Maakt een ClaimsPrincipal aan en persisteert dit via een cookie

 

Wanneer bevestigde e-mails en lockout geen vereiste zijn dan volstaat het om de CheckPasswordAsync methode uit de UserManager class te gebruiken. De SignInManager class is gekoppeld aan de cookie-authenticatie.

De volgende code snippet toont een manier om door middel van emailadres/wachtwoord combinatie authenticatie van een gebruiker te doen:

Analoog aan bovenstaande code snippet kan een gebruiker worden geauthentiseerd via zijn/haar gebruikersnaam.

In de volgende paragraaf wordt uitgebreider ingegaan op het Lockout mechanisme waarmee restricties kunnen worden gesteld aan het aantal pogingen dat een gebruiker mag doen om zichzelf bij een applicatie te authentiseren.

Account Lockout mechanisme

Het ASP.NET Core Identity framework biedt standaard een aantal belangrijke beveiligingsfuncties die het authenticatieproces van extra checks te voorzien. Denk hierbij aan:

  • Two-Factor Authentication (met behulp van SMS of email)
  • Account Lockout
  • Account Confirmation

Account Lockout is een belangrijke functie van het ASP.NET Core Identity framework. Het blokkeert het account van de gebruiker als deze een bepaald aantal keren een verkeerd wachtwoord invoert. Dit kan worden gespecificeerd door het maximale aantal mislukte pogingen (default 5x) en de lockout-tijd (default 5 minuten) te configureren in startup.cs:

De volgende code snippet toont het gebruik van het Lockout mechanisme: een mogelijkheid om het aantal inlogpogingen te beperken tot een opgegeven maximum. Zoals in de vorige paragraaf aangegeven is het hiervoor noodzakelijk gebruik te maken van de SignInManager.PasswordSignInAsync methode. Door de parameter lockoutOnFailure te activeren (true), wordt de lockout-functionaliteit ingeschakeld.

Custom validatie

Naast de standaard al aanwezige validatie in het ASP.NET Core Identity framework is het mogelijk eigen validatie toe te voegen zoals bijvoorbeeld het controleren op emaildomein of het uitsluiten van de mogelijkheid dat gebruikersnaam en wachtwoord identiek zijn. De volgende code snippets tonen enkele mogelijke custom validatie’s.

Het opnemen van de (extra) custom validatie’s gebeurt in startup.cs:

Auteur: Pieter Baart © 2020 Bergler Competence Center