OAuth2 dPoP dla Delphi

· Funkcje

Tokeny dostępu OAuth 2.0 to klucze do twojego królestwa API — a jeśli ktoś je ukradnie, może ich używać z dowolnego miejsca. DPoP (Demonstrating Proof of Possession), zdefiniowany w RFC 9449, rozwiązuje ten problem przez kryptograficzne powiązanie tokenów z klientem, który je zażądał. Nawet jeśli token zostanie przechwycony, staje się bezużyteczny bez klucza prywatnego klienta.

Począwszy od sgcWebSockets 2026.4.0, komponenty klienta i serwera OAuth2 zawierają pełne wsparcie dla DPoP. Ten artykuł wyjaśnia, czym jest DPoP, jak działa i jak go skonfigurować w aplikacji Delphi.

Czym jest DPoP?

W standardowym OAuth 2.0 token Bearer działa jak gotówka — kto go posiada, może go używać. Jeśli atakujący przechwyci token przez skompromitowaną sieć, wyciek logów lub złośliwy serwer proxy, uzyskuje pełny dostęp do chronionych zasobów.

DPoP zmienia to, sprawiając że tokeny działają jak karta kredytowa z PIN-em. Token jest powiązany z kluczem publicznym, a każde żądanie musi zawierać proof JWT podpisany odpowiadającym kluczem prywatnym. Serwer weryfikuje, czy proof pasuje do powiązanego klucza, zanim zaakceptuje żądanie.

Token Bearer (tradycyjny)
Każdy, kto posiada token, może go użyć. Skradzione tokeny są w pełni podatne na nadużycia. Nie jest wymagany żaden dowód tożsamości.
Token DPoP (ograniczony do nadawcy)
Token jest powiązany z parą kluczy. Każde żądanie wymaga świeżego podpisanego proofа. Skradzione tokeny są bezwartościowe bez klucza prywatnego.

Jak działa DPoP

Przepływ DPoP dodaje lekki krok kryptograficzny do standardowego procesu OAuth2:

1. Generowanie klucza — Klient generuje asymetryczną parę kluczy (ES256 lub RS256). Klucz prywatny pozostaje na kliencie; klucz publiczny jest dołączany jako JWK w proofach DPoP.

2. Żądanie tokenu — Przy żądaniu tokenu dostępu klient dołącza nagłówek HTTP DPoP zawierający podpisany proof JWT. Proof zawiera metodę HTTP, URL, znacznik czasu i unikalny identyfikator.

3. Powiązanie tokenu — Serwer autoryzacji weryfikuje sygnaturę proofu, wyodrębnia odcisk JWK (skrót SHA-256 klucza publicznego) i wiąże go z wydanym tokenem. Odpowiedź zawiera token_type: DPoP zamiast Bearer.

4. Dostęp do zasobów — Przy każdym wywołaniu API klient wysyła zarówno token, jak i świeży proof DPoP. Proof zawiera roszczenie ath (skrót SHA-256 tokenu dostępu), wiążące proof z tym konkretnym tokenem.

Budowa proofu DPoP

Proof DPoP to kompaktowy JWT z trzema częściami: nagłówkiem, ładunkiem i sygnaturą.

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

Konfiguracja DPoP w sgcWebSockets

Krok 1: Generowanie pary kluczy ES256

Użyj OpenSSL, aby wygenerować parę kluczy krzywej eliptycznej:

# 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

Przekonwertuj współrzędne X i Y klucza publicznego do Base64URL, aby zbudować JWK:

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

Krok 2: Konfiguracja klienta 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;

To wszystko. Komponent automatycznie:

Krok 3: Używanie proofów DPoP do wywołań API

Po uzyskaniu tokenu dostępu powiązanego z DPoP, generuj proof dla każdego żądania 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;

Obsługiwani dostawcy

DPoP jest już obsługiwany przez głównych dostawców OAuth2:

Auth0 Włącz DPoP w ustawieniach aplikacji. Wymaga obsługi nonce (obsługiwanej automatycznie).
Okta Skonfiguruj DPoP w politykach dostępu serwera autoryzacji. Dostępne ogólnie od 2024 r.
Microsoft Entra ID Obsługuje DPoP dla klientów poufnych.
Ping Identity Pełna obsługa DPoP w PingOne i PingFederate.

Walidacja DPoP po stronie serwera

Komponent serwera OAuth2 dla sgcWebSockets obsługuje również walidację DPoP. Gdy OAuth2Options.DPoP jest włączone, serwer automatycznie:

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

Dokumentacja API DPoP

Właściwość / metoda Opis
DPoPOptions.Enabled Włącza DPoP dla wszystkich żądań tokenów.
DPoPOptions.Algorithm dpopES256 (zalecany) lub dpopRS256.
DPoPOptions.PrivateKey Klucz prywatny w formacie PEM do podpisywania proofów DPoP.
DPoPOptions.PublicKeyJWK Reprezentacja klucza publicznego jako JSON Web Key.
GetDPoPProof() Generuje proof JWT DPoP dla konkretnej metody HTTP, URL i tokenu dostępu.
GetDPoPJWKThumbprint() Zwraca odcisk SHA-256 klucza publicznego zgodnie z RFC 7638.
DPoPNonce Aktualna wartość DPoP-Nonce z serwera (tylko do odczytu).

Zwiększ bezpieczeństwo tokenów

DPoP to najważniejsze usprawnienie bezpieczeństwa tokenów OAuth 2.0 od czasu PKCE. Eliminuje całą klasę ataków kradzieży tokenów przy minimalnych zmianach kodu — wystarczy ustawić DPoPOptions.Enabled := True, podać klucze, a komponent sgcWebSockets zajmie się resztą.

Obsługa DPoP jest dostępna w sgcWebSockets 2026.4.0 zarówno dla Delphi (DXE6 do D13), jak i .NET (.NET Framework 2.0 do .NET 9). Pobierz najnowszą wersję na esegece.com.