OAuth2 dPoP en Delphi

· Características

Los access tokens de OAuth 2.0 son las llaves de tu reino de APIs — y si alguien roba uno, puede usarlo desde cualquier lugar. DPoP (Demonstrating Proof of Possession), definido en la RFC 9449, resuelve esto vinculando criptográficamente los tokens al cliente que los solicitó. Incluso si un token es interceptado, se vuelve inútil sin la clave privada del cliente.

A partir de sgcWebSockets 2026.4.0, los componentes cliente y servidor de OAuth2 incluyen soporte completo de DPoP. Este artículo explica qué es DPoP, cómo funciona y cómo configurarlo en tu aplicación Delphi.

¿Qué es DPoP?

En OAuth 2.0 estándar, un token Bearer funciona como dinero en efectivo: quien lo tenga puede gastarlo. Si un atacante intercepta el token mediante una red comprometida, logs filtrados o un proxy malicioso, tiene acceso completo a tus recursos protegidos.

DPoP cambia esto haciendo que los tokens funcionen como una tarjeta de crédito con un PIN. El token en sí está vinculado a una clave pública, y cada petición debe incluir un proof JWT firmado por la clave privada correspondiente. El servidor verifica que el proof coincide con la clave vinculada antes de aceptar la petición.

Token Bearer (tradicional)
Cualquiera con el token puede usarlo. Los tokens robados son totalmente explotables. No requiere prueba de identidad.
Token DPoP (sender-constrained)
El token está vinculado a un par de claves. Cada petición requiere un proof firmado fresco. Los tokens robados no valen nada sin la clave privada.

Cómo funciona DPoP

El flujo DPoP añade un paso criptográfico ligero al proceso OAuth2 estándar:

1. Generación de claves — El cliente genera un par de claves asimétricas (ES256 o RS256). La clave privada se queda en el cliente; la clave pública se incluye como JWK en los proofs DPoP.

2. Solicitud de token — Al solicitar un access token, el cliente incluye una cabecera HTTP DPoP que contiene un proof JWT firmado. El proof incluye el método HTTP, la URL, el timestamp y un identificador único.

3. Vinculación del token — El servidor de autorización verifica la firma del proof, extrae el thumbprint del JWK (hash SHA-256 de la clave pública) y lo vincula al token emitido. La respuesta contiene token_type: DPoP en lugar de Bearer.

4. Acceso al recurso — Para cada llamada a la API, el cliente envía tanto el token como un proof DPoP fresco. El proof incluye un claim ath (hash SHA-256 del access token), vinculando el proof a ese token específico.

Dentro de un proof DPoP

Un proof DPoP es un JWT compacto con tres partes: cabecera, payload y firma.

// Header - identifies the key and algorithm
{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "cWs37kZLJMej6fpd...",
    "y": "e2bkcQGaBERgSZUb..."
  }
}
// Payload - proves freshness and binds to the request
{
  "htm": "POST",
  "htu": "https://auth.example.com/oauth/token",
  "iat": 1774950263,
  "jti": "F1AFCD1F-95F7-401B-A2F5-195A31DB1802",
  "ath": "fUHyO2r2Z3DZ53EsNr..."
}

Configurar DPoP en sgcWebSockets

Paso 1: generar un par de claves ES256

Usa OpenSSL para generar un par de claves de curva elíptica:

# Generate EC private key
openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem
# Extract public key parameters
openssl ec -in dpop_private.pem -text -noout

Convierte las coordenadas X e Y de la clave pública a Base64URL para construir el JWK:

{"kty":"EC","crv":"P-256","x":"cWs37kZLJMej6fpdyKaI8Gz6CE...","y":"e2bkcQGaBERgSZUbAGR-iOOM..."}

Paso 2: configurar el cliente OAuth2

// Configure OAuth2 as usual
OAuth2.OAuth2Options.GrantType := auth2CodePKCE;
OAuth2.OAuth2Options.ClientId := 'your-client-id';
OAuth2.OAuth2Options.ClientSecret := 'your-client-secret';
OAuth2.AuthorizationServerOptions.AuthURL := 'https://auth.example.com/authorize';
OAuth2.AuthorizationServerOptions.TokenURL := 'https://auth.example.com/oauth/token';
// Enable DPoP
OAuth2.DPoPOptions.Enabled := True;
OAuth2.DPoPOptions.Algorithm := dpopES256;
OAuth2.DPoPOptions.PrivateKey.LoadFromFile('dpop_private.pem');
OAuth2.DPoPOptions.PublicKeyJWK := '{"kty":"EC","crv":"P-256","x":"...","y":"..."}';
// Start the OAuth2 flow - DPoP headers are added automatically
OAuth2.Start;

Eso es todo. El componente automáticamente:

Paso 3: usar proofs DPoP en las llamadas a la API

Tras obtener un access token vinculado por DPoP, genera un proof para cada petición a la API:

var
  vProof: String;
begin
  // Generate a DPoP proof for the API call
  vProof := OAuth2.GetDPoPProof(
    'GET',
    'https://api.example.com/userinfo',
    OAuth2.AccessToken
  );
  // Include both headers in your HTTP request:
  //   Authorization: DPoP <access_token>
  //   DPoP: <proof_jwt>
  HTTPClient.Request.CustomHeaders.AddValue('Authorization',
    'DPoP ' + OAuth2.AccessToken);
  HTTPClient.Request.CustomHeaders.AddValue('DPoP', vProof);
  HTTPClient.Get('https://api.example.com/userinfo');
end;

Proveedores compatibles

DPoP ya está soportado por los principales proveedores de OAuth2:

Auth0 Enable DPoP in application settings. Requires nonce support (handled automatically).
Okta Configure DPoP in authorization server access policies. GA since 2024.
Microsoft Entra ID Supports DPoP for confidential clients.
Ping Identity Full DPoP support in PingOne and PingFederate.

Validación DPoP en el lado servidor

El componente servidor OAuth2 de sgcWebSockets también admite la validación DPoP. Cuando OAuth2Options.DPoP está activado, el servidor automáticamente:

// Enable DPoP on the OAuth2 server
OAuth2Server.OAuth2Options.DPoP := True;
// Optional: custom validation via event
OAuth2Server.OnOAuth2ValidateDPoP := procedure(Sender: TObject;
  Connection: TsgcWSConnection;
  const DPoPProof, AccessToken: String;
  var IsValid: Boolean)
begin
  // Add custom checks here (e.g., verify against a key registry)
  IsValid := True;
end;

Referencia de API DPoP

Property / Method Description
DPoPOptions.Enabled Enables DPoP for all token requests.
DPoPOptions.Algorithm dpopES256 (recommended) or dpopRS256.
DPoPOptions.PrivateKey PEM-encoded private key for signing DPoP proofs.
DPoPOptions.PublicKeyJWK JSON Web Key representation of the public key.
GetDPoPProof() Generates a DPoP proof JWT for a specific HTTP method, URL, and access token.
GetDPoPJWKThumbprint() Returns the RFC 7638 SHA-256 thumbprint of the public key.
DPoPNonce The current DPoP-Nonce value from the server (read-only).

Mejora la seguridad de tus tokens

DPoP es la mejora más significativa a la seguridad de tokens de OAuth 2.0 desde PKCE. Elimina toda la clase de ataques de robo de tokens con cambios mínimos de código — basta con establecer DPoPOptions.Enabled := True, proporcionar tus claves y el componente sgcWebSockets se encarga del resto.

El soporte de DPoP está disponible en sgcWebSockets 2026.4.0 tanto para Delphi (DXE6 hasta D13) como para .NET (.NET Framework 2.0 hasta .NET 9). Descarga la última versión en esegece.com.