Vicen Moreno

Pro Googler

Follow me on GitHub

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

  1. Backup de base de datos
  2. Actualizar a .NET 8
  3. Actualizar packages Duende
  4. Revisar breaking changes
  5. Correr migrations de EF
  6. Actualizar configuración
  7. Testing exhaustivo
  8. Deploy a staging
  9. Monitoreo intensivo
  10. 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?


 Anterior      Posterior

Por Vicente José Moreno Escobar el 18 de septiembre de 2024
Archivado en: Duende   IdentityServer   OAuth2



Puedes disfrutar de otros artículos como éste en el archivo del sitio.