Duende IdentityServer v7 - Novedades y migración desde v6
IdentityServer sigue evolucionando: v7 trae mejoras importantes
¿Qué hay de nuevo?
Duende IdentityServer v7 (lanzado 2024) trae:
- Soporte .NET 8
- Dynamic Client Registration (DCR)
- Mejoras en Pushed Authorization Requests (PAR)
- Server-side sessions mejoradas
- Breaking changes menores
Requisitos
- .NET 8.0+
- Duende.IdentityServer 7.x
- License key válida (si applies)
Migración desde v6
1. Actualizar packages
<!-- Antes -->
<PackageReference Include="Duende.IdentityServer" Version="6.3.0" />
<!-- Después -->
<PackageReference Include="Duende.IdentityServer" Version="7.0.0" />
2. Actualizar target framework
<TargetFramework>net8.0</TargetFramework>
3. Breaking changes
Namespace changes
// Algunos namespaces movidos
// Revisar imports y actualizar según errores de compilación
Configuration changes
// v6
services.AddIdentityServer(options =>
{
options.Discovery.ShowApiScopes = true;
});
// v7 - algunas opciones movidas/renombradas
services.AddIdentityServer(options =>
{
options.Discovery.ShowIdentityScopes = true;
options.Discovery.ShowApiScopes = true;
});
Dynamic Client Registration (DCR)
Nueva feature: registrar clientes dinámicamente via API.
Habilitar DCR
services.AddIdentityServer()
.AddDynamicClientRegistration();
Endpoint
POST /connect/register
Request de registro
curl -X POST https://auth.ejemplo.com/connect/register \
-H "Content-Type: application/json" \
-H "Authorization: Bearer initial-access-token" \
-d '{
"redirect_uris": ["https://app.ejemplo.com/callback"],
"client_name": "My Dynamic App",
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic"
}'
Response
{
"client_id": "generated-client-id",
"client_secret": "generated-secret",
"client_id_issued_at": 1709251200,
"client_secret_expires_at": 0,
"redirect_uris": ["https://app.ejemplo.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"]
}
Customizar DCR
public class CustomDynamicClientRegistrationValidator : IDynamicClientRegistrationValidator
{
public async Task<DynamicClientRegistrationValidationResult> ValidateAsync(
DynamicClientRegistrationContext context)
{
// Validar que el cliente puede registrarse
var client = context.Client;
// Ejemplo: solo permitir ciertos redirect_uris
foreach (var uri in client.RedirectUris)
{
if (!uri.StartsWith("https://"))
{
return new DynamicClientRegistrationValidationResult(
"redirect_uri must use HTTPS");
}
}
// Forzar configuración
client.RequirePkce = true;
client.AllowOfflineAccess = false;
return new DynamicClientRegistrationValidationResult(client);
}
}
// Registrar
services.AddTransient<IDynamicClientRegistrationValidator,
CustomDynamicClientRegistrationValidator>();
Pushed Authorization Requests (PAR) mejorado
PAR más robusto en v7:
services.AddIdentityServer(options =>
{
options.PushedAuthorization.Required = true; // Forzar PAR
options.PushedAuthorization.Lifetime = TimeSpan.FromMinutes(5);
options.PushedAuthorization.AllowUnregisteredPushedRedirectUris = false;
});
Flujo PAR
// 1. Cliente hace POST a /connect/par
POST /connect/par
Content-Type: application/x-www-form-urlencoded
client_id=my-client&
redirect_uri=https://app.ejemplo.com/callback&
response_type=code&
scope=openid profile&
code_challenge=xxx&
code_challenge_method=S256
// Response
{
"request_uri": "urn:ietf:params:oauth:request_uri:abc123",
"expires_in": 300
}
// 2. Cliente redirige a authorize con request_uri
GET /connect/authorize?
client_id=my-client&
request_uri=urn:ietf:params:oauth:request_uri:abc123
Beneficios:
- Parámetros sensibles no en URL
- Mejor logging/auditoría
- Requests firmados posibles
Server-Side Sessions mejoradas
Configuración
services.AddIdentityServer(options =>
{
options.ServerSideSessions.UserDisplayNameClaimType = "name";
options.ServerSideSessions.RemoveExpiredSessionsFrequency = TimeSpan.FromMinutes(10);
options.ServerSideSessions.RemoveExpiredSessionsBatchSize = 100;
options.ServerSideSessions.ExpiredSessionsTriggerBackchannelLogout = true;
});
Session management UI
// Obtener sesiones de un usuario
var sessions = await _sessionStore.GetSessionsAsync(new SessionFilter
{
SubjectId = userId
});
// Revocar sesión específica
await _sessionStore.DeleteSessionAsync(sessionId);
// Revocar todas las sesiones de un usuario
await _sessionStore.DeleteSessionsAsync(new SessionFilter
{
SubjectId = userId
});
Backchannel logout automático
Cuando una sesión expira o se revoca:
options.ServerSideSessions.ExpiredSessionsTriggerBackchannelLogout = true;
IdentityServer envía logout request a todos los clientes que participaron en esa sesión.
Mejoras de seguridad
JWT Secured Authorization Requests (JAR)
new Client
{
ClientId = "secure-client",
RequireRequestObject = true, // Solo aceptar requests firmados
AllowedSigningAlgorithms = { "RS256", "PS256" }
}
DPoP (Demonstrating Proof of Possession)
new Client
{
ClientId = "dpop-client",
RequireDPoP = true
}
DPoP previene token theft: tokens están vinculados a una clave del cliente.
Entity Framework migrations
# Crear migration para nuevas tablas
dotnet ef migrations add Duendev7Update -c PersistedGrantDbContext
# Aplicar
dotnet ef database update -c PersistedGrantDbContext
Cambios de schema:
- Nuevos campos en ServerSideSessions
- Tabla para DCR clients (si se usa)
Performance improvements
v7 incluye optimizaciones:
// Caching mejorado
services.AddIdentityServerCaching();
// Nuevo: caching distribuido para server-side sessions
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost";
});
Monitoreo y telemetría
// OpenTelemetry support mejorado
services.AddOpenTelemetry()
.WithTracing(builder =>
{
builder.AddSource("Duende.IdentityServer");
});
Métricas expuestas:
- Token requests
- Authorization requests
- Session creation/expiration
- DCR registrations
Migración paso a paso
Checklist
- Backup de base de datos
- Actualizar a .NET 8
- Actualizar packages Duende
- Revisar breaking changes
- Correr migrations de EF
- Actualizar configuración
- Testing exhaustivo
- Deploy a staging
- Monitoreo intensivo
- Deploy a producción
Rollback plan
# Si algo falla, tener preparado:
# 1. Tag de git con versión anterior
git checkout v6-production
# 2. Backup de DB antes de migrations
pg_restore -d identityserver backup_pre_v7.dump
# 3. Redeploy version anterior
Experiencia de migración
Nuestra migración:
- Tiempo: 4 horas (incluye testing)
- Breaking changes encontrados: 3 menores
- Downtime: 0 (blue-green deployment)
- Issues post-migración: 0
v7 es una actualización relativamente suave si vienes de v6.
¿Has migrado a v7? ¿Qué features nuevas te interesan más?
Por Vicente José Moreno Escobar el
18 de
septiembre
de
2024
Puedes disfrutar de otros artículos como éste en el archivo del sitio.