Nowoczesne aplikacje w coraz większym stopniu odchodzą od tradycyjnych haseł na rzecz metod uwierzytelniania bez hasła, które są zarówno bezpieczniejsze, jak i łatwiejsze dla użytkowników. W tym poście pokażę, jak uwierzytelniać użytkowników w aplikacji ASP.NET Core przy użyciu FoxIDs z OpenID Connect i umożliwić logowanie się całkowicie bez hasła przy użyciu jednorazowych kodów dostarczanych pocztą elektroniczną.
Zaczniemy od czystego środowiska FoxIDs, stworzymy nową aplikację internetową ASP.NET Core, połączymy ją z OpenID Connect, a na koniec umożliwimy logowanie passwordless za pomocą OTP opartego na e-mailu.
Tworzenie środowiska deweloperskiego FoxIDs
Zacząłem od stworzenia nowego, pustego środowiska programistycznego w FoxIDs. Czyste środowisko ułatwia skonfigurowanie uwierzytelniania od podstaw i zapewnia możliwość powtórzenia tych kroków we własnym tenancie.
Jeśli nie korzystałeś wcześniej z FoxIDs, możesz stworzyć darmowy tenant pod adresem:
Każdy tenant ma dwa gotowe do użycia środowiska, dzięki czemu możesz swobodnie eksperymentować.
Skonfiguruj uwierzytelnianie
Zacznij od skonfigurowania w jaki sposób użytkownicy powinni się logować. Zależy nam na tym, aby użytkownicy logowali się bez hasła, korzystając wyłącznie ze swojego adresu e-mail i przesłanego im hasła jednorazowego (OTP).
- Wybierz zakładkę Uwierzytelnianie w swoim środowisku FoxIDs.
- Kliknij Domyślny — interfejs logowania użytkownika.
- W sekcji Uwierzytelnianie wyłącz Uwierzytelnianie hasłem i włącz Bez hasła za pomocą poczty elektronicznej (hasło jednorazowe).
- Kliknij Aktualizuj, aby zapisać zmiany.
Umożliwia to użytkownikom utworzenie nowego konta i zapewnia obsługę bez hasła, podczas której uwierzytelniają się, po prostu wprowadzając swój adres e-mail i otrzymany OTP.
Tworzenie aplikacji ASP.NET Core
Następnie potrzebujemy aplikacji do uwierzytelnienia. Możesz użyć Visual Studio lub VS Code — wszystkiego, co obsługuje standardowe programowanie ASP.NET Core.
Przeczytaj post „Uwierzytelnianie bez hasła w aplikacji ASP.NET Core generowanej przez sztuczną inteligencję za pomocą FoxIDs i OTP”, jeśli chcesz wygenerować aplikację ASP.NET Core z monitem AI.
Na potrzeby tego przewodnika tworzę prostą aplikację ASP.NET Core Razor Pages dla .NET 10 w Visual Studio, ale to samo podejście działa w przypadku MVC, aplikacji Blazor Web App lub dowolnego innego projektu ASP.NET Core.
Uruchom aplikację; przykładowa aplikacja działa lokalnie na https://localhost:7283/
Przykładowy kod można znaleźć w: https://github.com/ITfoxtec/dotnet.samples/tree/main/WebAppPasswordLessEmail
Skonfiguruj aplikację internetową w FoxIDs
Teraz, gdy znamy lokalny adres aplikacji internetowej, możemy ją zarejestrować w FoxIDs, aby mogła uwierzytelniać użytkowników poprzez OpenID Connect.
- Wybierz zakładkę Aplikacje w swoim środowisku FoxIDs.
- Wybierz Nowa aplikacja, a następnie wybierz Aplikacja internetowa — OpenID Connect.
- W polu Nazwa wpisz nazwę, np.: Aplikacja internetowa ASP.NET.
- W Redirect URI dodaj adres bazowy aplikacji: https://localhost:7283/ Domyślnie FoxIDs umożliwia przekierowanie do dowolnej kolejnej strony pod tym bazowym adresem URL. Jeśli wolisz bardziej rygorystyczną weryfikację, możesz wymagać bezwzględnych identyfikatorów URI przekierowań.
- Kliknij Utwórz.
FoxIDs wyświetla teraz szczegóły połączenia dla Twojej nowej aplikacji, w tym:
- Authority
- Identyfikator klienta
- Tajemnica klienta
Skopiuj Authority i Identyfikator klienta do sekcji IdentitySettings projektu. Przechowuj Tajemnicę klienta w kluczach użytkownika .NET lokalnie lub w bezpiecznej zmiennej środowiskowej lub magazynie sekretów w środowisku produkcyjnym.
Skonfiguruj uwierzytelnianie w aplikacji ASP.NET
Po utworzeniu aplikacji FoxIDs kolejnym krokiem jest skonfigurowanie uwierzytelnienia w projekcie ASP.NET Core. Będziemy używać uwierzytelniania plików cookie dla sesji lokalnej i OpenID Connect do komunikacji z FoxIDs.
Otwórz Program.cs i dodaj konfigurację uwierzytelniania za pomocą (builder.Services.AddAuthentication... i app.UseAuthentication()😉:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Logging;
var builder = WebApplication.CreateBuilder(args);
// Detailed authentication errors can help locally, but must not be enabled in production.
if (builder.Environment.IsDevelopment())
{
IdentityModelEventSource.ShowPII = true;
}
// Add authentication with cookie and OpenID Connect
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = builder.Configuration["IdentitySettings:Authority"];
options.ClientId = builder.Configuration["IdentitySettings:ClientId"];
options.ClientSecret = builder.Configuration["IdentitySettings:ClientSecret"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.SaveTokens = true;
options.Scope.Add("email");
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.MapInboundClaims = false;
options.TokenValidationParameters.NameClaimType = "sub";
options.TokenValidationParameters.RoleClaimType = "role";
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async context =>
{
// Custom claims transformation or other logic can be added here.
await Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
if (!builder.Environment.IsDevelopment())
{
return Task.CompletedTask;
}
context.HandleResponse();
context.Response.StatusCode = 500;
context.Response.ContentType = "text/plain";
return context.Response.WriteAsync(context.Exception.ToString());
}
};
});
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapStaticAssets();
app.MapControllers();
app.MapRazorPages()
.WithStaticAssets();
app.Run();
Dodaj niejawne szczegóły połączenia FoxIDs do appsettings.json. Trzymaj klucz tajny klienta poza kontrolą źródła i załaduj go z kluczy tajnych użytkownika .NET podczas lokalnego programowania lub z bezpiecznej zmiennej środowiskowej lub tajnego magazynu w środowisku produkcyjnym.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"IdentitySettings": {
"Authority": "https://foxids.com/{tenant}/{environment}/{application}/",
"ClientId": "your-client-id"
}
}
Przechowuj klucz tajny klienta lokalnie, bez zapisywania go w appsettings.json:
dotnet user-secrets init
dotnet user-secrets set "IdentitySettings:ClientSecret" "your-client-secret"
Zastąp przykładowe wartości Authority, identyfikatorem klienta i kluczem klienta z własnego środowiska FoxIDs. Nigdy nie zdradzaj prawdziwego sekretu.
Pokaż claims na stronie głównej
Na potrzeby tego przykładu wyświetl claims na stronie głównej. Otwórz Index.cshtml i zmień zawartość:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
@if (User.Identity?.IsAuthenticated == true)
{
<h2 class="mt-4">Claims</h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Type</th>
<th>Value</th>
</tr>
</thead>
<tbody>
@foreach (var claim in User.Claims)
{
<tr>
<td>@claim.Type</td>
<td>@claim.Value</td>
</tr>
}
</tbody>
</table>
}
else
{
<p class="mt-4 text-muted">Not signed in.</p>
}
Dodaj menu logowania do aplikacji
Aby umożliwić użytkownikom logowanie się, dodaliśmy częściowy widok kontroli logowania i wylogowywania.
Utwórz nowy plik:Pages/Shared/_LoginPartial.cshtml o zawartości:
@if (User.Identity?.IsAuthenticated == true)
{
<ul class="navbar-nav">
<li class="nav-item">
<form class="form-inline" asp-controller="Auth" asp-action="Logout" method="post">
@Html.AntiForgeryToken()
<button type="submit" class="nav-link btn btn-link text-dark">Log off</button>
</form>
</li>
</ul>
}
else
{
<ul class="navbar-nav navbar-right">
<li class="nav-item">
<a class="nav-link text-dark" asp-controller="Auth" asp-action="Login">Log in</a>
</li>
</ul>
}
Następnie dołącz część logowania do menu nawigacyjnego. W Pages/Shared/_Layout.cshtml dodaj częściowy element logowania na końcu paska nawigacyjnego:
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
</li>
</ul>
<partial name="_LoginPartial" />
</div>
Następnie dodaj stronę docelową dla żądań logowania i wylogowania. Utwórz folder Controllers i utwórz nowy plik kontrolera w folderze: Controllers/AuthController.cs o zawartości:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace WebAppPasswordLessEmail.Controllers
{
[AllowAnonymous]
[Route("[controller]/[action]")]
public class AuthController : Controller
{
[HttpGet]
public IActionResult Login()
{
var redirectUri = Url.Content("~/");
if (User.Identity?.IsAuthenticated == true)
{
return LocalRedirect(redirectUri);
}
return Challenge(new AuthenticationProperties { RedirectUri = redirectUri },
OpenIdConnectDefaults.AuthenticationScheme);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Logout()
{
var redirectUri = Url.Content("~/");
if (User.Identity?.IsAuthenticated != true)
{
return LocalRedirect(redirectUri);
}
return SignOut(new AuthenticationProperties { RedirectUri = redirectUri },
CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme);
}
}
}
Uruchom i załaduj ponownie aplikację, a w górnym menu zobaczysz opcję Zaloguj się.
Zaloguj się
Kliknij Zaloguj się, a nastąpi przekierowanie do ekranu logowania FoxIDs.
Wybierz Utwórz użytkownika.
Wypełnij formularz Utwórz użytkownika, podając swój adres e-mail (lub e-mail testowy, do którego masz dostęp), imię i nazwisko, a następnie kliknij Utwórz.
Wpisz hasło jednorazowe (OTP) otrzymane e-mailem, a następnie kliknij Zaloguj się.
Jesteś zalogowany i wyświetlane są claims Twojego użytkownika testowego.
Następnie spróbuj się wylogować i zalogować ponownie, aby doświadczyć procesu logowania bez hasła przy użyciu adresu e-mail OTP.