OAuth2 DPoP per Delphi

· Funzionalità

I token di accesso OAuth 2.0 sono le chiavi del regno delle tue API: se qualcuno ne ruba uno, può usarlo da qualsiasi posto. DPoP (Demonstrating Proof of Possession), definito da RFC 9449, risolve il problema vincolando crittograficamente i token al client che li ha richiesti. Anche se un token viene intercettato, diventa inutile senza la chiave privata del client.

A partire da sgcWebSockets 2026.4.0, i componenti client e server OAuth2 includono il supporto completo a DPoP. Questo articolo spiega cos'è DPoP, come funziona e come configurarlo nella tua applicazione Delphi.

Cos'è DPoP?

In OAuth 2.0 standard, un Bearer token funziona come contanti: chi lo possiede può spenderlo. Se un attaccante intercetta il token tramite una rete compromessa, log trapelati o un proxy malevolo, ha pieno accesso alle tue risorse protette.

DPoP cambia tutto facendo funzionare i token come una carta di credito con PIN. Il token stesso è vincolato a una chiave pubblica e ogni richiesta deve includere un proof JWT firmato dalla corrispondente chiave privata. Il server verifica che il proof corrisponda alla chiave vincolata prima di accettare la richiesta.

Bearer Token (tradizionale)
Chiunque possieda il token può usarlo. I token rubati sono pienamente sfruttabili. Non è richiesta prova d'identità.
DPoP Token (sender-constrained)
Il token è vincolato a una coppia di chiavi. Ogni richiesta richiede un nuovo proof firmato. I token rubati sono inutili senza la chiave privata.

Come funziona DPoP

Il flusso DPoP aggiunge un leggero passo crittografico al processo standard OAuth2:

1. Generazione delle chiavi — Il client genera una coppia di chiavi asimmetriche (ES256 o RS256). La chiave privata resta sul client; quella pubblica viene inclusa come JWK nei proof DPoP.

2. Richiesta del token — Quando richiede un access token, il client include un header HTTP DPoP contenente un proof JWT firmato. Il proof include il metodo HTTP, l'URL, il timestamp e un identificatore univoco.

3. Binding del token — L'authorization server verifica la firma del proof, estrae il thumbprint JWK (hash SHA-256 della chiave pubblica) e lo vincola al token emesso. La risposta contiene token_type: DPoP invece di Bearer.

4. Accesso alle risorse — Per ogni chiamata API, il client invia sia il token sia un nuovo proof DPoP. Il proof include un claim ath (hash SHA-256 dell'access token), vincolando il proof a quello specifico token.

Inside a DPoP Proof

A DPoP proof is a compact JWT with three parts: header, payload, and signature.

// 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..."
}

Configuring DPoP in sgcWebSockets

Step 1: Generate an ES256 Key Pair

Use OpenSSL to generate an elliptic curve key pair:

# 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

Converti le coordinate X e Y della chiave pubblica in Base64URL per costruire il JWK:

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

Passo 2: configurare il client 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;

Tutto qui. Il componente automaticamente:

Step 3: Use DPoP Proofs for API Calls

After obtaining a DPoP-bound access token, generate a proof for each API request:

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;

Supported Providers

DPoP is already supported by major OAuth2 providers:

Auth0 Abilita DPoP nelle impostazioni dell'applicazione. Richiede il supporto al nonce (gestito automaticamente).
Okta Configura DPoP nelle policy di accesso dell'authorization server. GA dal 2024.
Microsoft Entra ID Supporta DPoP per i client confidenziali.
Ping Identity Supporto completo a DPoP in PingOne e PingFederate.

Server-Side DPoP Validation

The sgcWebSockets OAuth2 server component also supports DPoP validation. When OAuth2Options.DPoP is enabled, the server automatically:

// 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;

Riferimento API DPoP

Proprietà / metodo Descrizione
DPoPOptions.Enabled Abilita DPoP per tutte le richieste di token.
DPoPOptions.Algorithm dpopES256 (consigliato) o dpopRS256.
DPoPOptions.PrivateKey Chiave privata in PEM per firmare i proof DPoP.
DPoPOptions.PublicKeyJWK Rappresentazione JSON Web Key della chiave pubblica.
GetDPoPProof() Genera un proof DPoP JWT per un metodo HTTP, URL e access token specifici.
GetDPoPJWKThumbprint() Restituisce il thumbprint SHA-256 (RFC 7638) della chiave pubblica.
DPoPNonce Il valore corrente del DPoP-Nonce dal server (sola lettura).

Upgrade Your Token Security

DPoP is the most significant improvement to OAuth 2.0 token security since PKCE. It eliminates the entire class of token theft attacks with minimal code changes — just set DPoPOptions.Enabled := True, provide your keys, and the sgcWebSockets component handles the rest.

Il supporto a DPoP è disponibile in sgcWebSockets 2026.4.0 sia per Delphi (da DXE6 a D13) sia per .NET (da .NET Framework 2.0 a .NET 9). Scarica l'ultima versione su esegece.com.