DpopSigner

Signs DPoP proof JWTs using EC P-256 (ES256) per RFC 9449.

Each DpopSigner instance is bound to a single EC keypair for the lifetime of an OAuth session. The private key signs proofs; the public key is embedded in every proof's JWT header as a JWK so the server can verify possession.

AT Protocol DPoP proof JWT structure:

Header:

{ "typ": "dpop+jwt", "alg": "ES256", "jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." } }

Payload:

{ "jti": "<unique>", "htm": "POST", "htu": "https://pds/oauth/par", "iat": 1234567890, "nonce": "..." }

For PDS resource requests (not PAR/token), the payload also includes:

{ "ath": "<base64url(sha256(access_token))>" }

Per the AT Protocol spec, the iss claim is deliberately omitted from all DPoP proofs.

Types

Link copied to clipboard
object Companion
Link copied to clipboard
data class ExportedKeyPair(val privateKeyEncoded: ByteArray, val publicKeyEncoded: ByteArray)

Properties

Link copied to clipboard

Clock offset in seconds between the device and the server. If the server's Date header says 12:00:05 and the device says 12:00:00, offset is +5. Applied to iat claims so DPoP proofs are accepted even when the device clock is off.

Link copied to clipboard

Functions

Link copied to clipboard

Calibrates clockOffsetSeconds from a server response's Date header. Call this after the first response from any AT Protocol server.

Link copied to clipboard

Exports the keypair for session persistence. The caller is responsible for storing these securely (e.g. EncryptedSharedPreferences).

Link copied to clipboard
fun sign(method: String, url: String, accessTokenHash: String? = null, nonce: String? = null): String

Signs a DPoP proof JWT for the given HTTP method + URL.