As aplicações modernas estão cada vez mais a afastar-se das palavras-passe tradicionais e a adotar métodos de autenticação sem palavra-passe que são mais seguros e fáceis para os utilizadores. Neste post, mostrarei como autenticar utilizadores numa aplicação ASP.NET Core utilizando FoxIDs com OpenID Connect e ativar uma experiência de início de sessão totalmente sem palavra-passe utilizando códigos únicos entregues por e-mail.
Começaremos com um ambiente FoxIDs limpo, criaremos uma nova aplicação web ASP.NET Core, ligá-la-emos com OpenID Connect e, finalmente, activaremos o login passwordless com OTP baseado em e-mail.
Criação de um ambiente de desenvolvimento FoxIDs
Comecei por criar um novo ambiente de desenvolvimento vazio em FoxIDs. Um ambiente limpo simplifica a configuração da autenticação a partir do zero e garante que pode replicar estes passos no seu próprio tenant.
Se nunca utilizou FoxIDs antes, pode criar um tenant gratuito em:
Cada tenant vem com dois ambientes prontos a usar para que possa experimentar livremente.
Configurar autenticação
Comece por configurar como os utilizadores devem fazer login. Neste caso, queremos que os utilizadores iniciem sessão sem palavra-passe, utilizando apenas o endereço de e-mail e uma palavra-passe de utilização única (OTP) que lhes foi enviada.
- Selecione o separador Autenticações no seu ambiente FoxIDs.
- Clique em Padrão — UI de início de sessão do utilizador.
- Na secção Autenticação, desative Autenticação por palavra-passe e ative Sem palavra-passe com e-mail (palavra-passe de utilização única).
- Clique em Atualizar para guardar as alterações.
Isto permite aos utilizadores criar uma nova conta e proporcionar uma experiência sem palavra-passe, onde se autenticam simplesmente introduzindo o seu endereço de e-mail e o OTP que recebem.
Criação da aplicação ASP.NET Core
Em seguida, precisamos de uma aplicação para autenticação. Pode utilizar o Visual Studio ou o VS Code – qualquer coisa que suporte o desenvolvimento ASP.NET Core padrão.
Leia o post “Autenticação sem palavra-passe numa aplicação ASP.NET Core gerada por IA com FoxIDs e OTP baseada em e-mail” se pretender gerar a aplicação ASP.NET Core com um aviso de IA.
Para este passo a passo, estou a criar uma aplicação ASP.NET Core Razor Pages simples direcionada para .NET 10 no Visual Studio, mas a mesma abordagem funciona para MVC, uma Blazor Web App ou qualquer outro projeto ASP.NET Core.
Execute a aplicação; a aplicação de exemplo é executada localmente em https://localhost:7283/
Pode encontrar o código de exemplo em: https://github.com/ITfoxtec/dotnet.samples/tree/main/WebAppPasswordLessEmail
Configure a aplicação web em FoxIDs
Agora que já sabemos o endereço local da aplicação web, podemos registá-la em FoxIDs para que possa autenticar os utilizadores através de OpenID Connect.
- Selecione o separador Aplicações no seu ambiente FoxIDs.
- Escolha Nova aplicação e selecione Aplicação Web — OpenID Connect.
- Em Nome, introduza um nome, por exemplo: ASP.NET aplicação web.
- No Redirect URI, adicione o endereço base da aplicação: https://localhost:7283/ Por defeito, o FoxIDs permite o redireccionamento para qualquer página subsequente neste URL base. Se preferir uma validação mais rigorosa, pode exigir URIs de redireccionamento absolutos.
- Clique em Criar.
FoxIDs apresenta agora os detalhes da ligação para a sua nova aplicação, incluindo:
- Authority
- ID do cliente
- Segredo do cliente
Copie Authority e Client ID para a secção IdentitySettings do projeto. Armazene o Segredo do cliente em segredos do utilizador .NET localmente ou numa variável de ambiente segura ou armazenamento de segredos na produção.
Configurar a autenticação na aplicação ASP.NET
Com a aplicação FoxIDs criada, o passo seguinte é configurar a autenticação no projeto ASP.NET Core. Utilizaremos autenticação de cookies para a sessão local e OpenID Connect para comunicação com FoxIDs.
Abra o Program.cs e adicione a configuração de autenticação com (builder.Services.AddAuthentication... e 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();
Adicione os detalhes da ligação não secreta FoxIDs a appsettings.json. Mantenha o segredo do cliente fora do controlo de origem e carregue-o a partir dos segredos do utilizador .NET durante o desenvolvimento local ou de uma variável de ambiente segura ou de armazenamento secreto na produção.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"IdentitySettings": {
"Authority": "https://foxids.com/{tenant}/{environment}/{application}/",
"ClientId": "your-client-id"
}
}
Armazene o segredo do cliente localmente sem o gravar em appsettings.json:
dotnet user-secrets init
dotnet user-secrets set "IdentitySettings:ClientSecret" "your-client-secret"
*Substitua os valores de exemplo por Authority, ID do cliente e segredo do cliente do seu próprio ambiente FoxIDs. Nunca cometa o verdadeiro segredo. *
Mostrar claims na página principal
Para efeitos deste exemplo, mostre os claims na página principal. Abra o Index.cshtml e altere o conteúdo:
@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>
}
Adicione um menu de início de sessão à aplicação
Para permitir que os utilizadores iniciem sessão, adicionámos uma visualização parcial dos controlos de início de sessão e de logout.
Crie um novo ficheiro:Pages/Shared/_LoginPartial.cshtml com o conteúdo:
@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>
}
Em seguida, inclua o login parcial no menu de navegação. Em Pages/Shared/_Layout.cshtml adicione o elemento de login parcial no final da barra de navegação:
<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>
Em seguida, adicione uma página inicial para os pedidos de início de sessão e de logout. Crie uma pasta Controladores e crie um novo ficheiro controlador na pasta: Controllers/AuthController.cs com o conteúdo:
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);
}
}
}
Execute e recarregue a aplicação e verá a opção Login no menu superior.
Conecte-se
Clique em Login e será redirecionado para o ecrã de login do FoxIDs.
Selecione Criar utilizador.
Preencha o formulário Criar utilizador com o seu e-mail (ou um e-mail de teste ao qual tenha acesso), nome e apelido e clique em Criar.
Introduza a palavra-passe de utilização única (OTP) que recebeu por e-mail e clique em Efetuar login.
Está ligado e os claims do seu utilizador de teste são exibidas.
Em seguida, tente fazer logoff e login novamente para experimentar o fluxo de login sem palavra-passe utilizando o e-mail OTP.