Security
Security posture
Section titled “Security posture”Open Payments places three cryptographic checkpoints between a client and the resource server:
- HTTP message signatures on every request, so the ASE can authenticate the sender and detect tampering.
- Client keys registered to a wallet address (or provided directly via directed identity), so the ASE knows which key to verify against.
- Interaction hashes during interactive grants, so the client can verify that a redirect emanated from the ASE’s authorization server.
ASEs are responsible for the verification side of all three mechanisms. Failure to enforce any one of them weakens the others.
HTTP signature validation
Section titled “HTTP signature validation”Every request a client sends to an ASE’s authorization or resource server is signed using the HTTP message signatures profile defined by GNAP, with the Ed25519 variant of EdDSA.
For every signed incoming request, ASEs must:
- Read the
Signature-Inputheader to identify thekeyIdand the list of covered components. - Reconstruct the signature base from those components exactly as the client did.
- Resolve the public key (refer to Client key resolution below).
- Verify the
Signatureheader against the reconstructed signature base using Ed25519. - Reject the request if any of the following are true:
- Required components (method, target URI, content digest where applicable, authorization header where applicable) are missing.
- The signature does not validate.
- The signature is older than the maximum age the ASE permits.
- The
Content-Digestheader is present but does not match the body.
ASEs set their own maximum signature age. Open Payments does not bound it, but very long limits increase replay risk and very short limits can cause failures for clients on slow networks.
Bearer tokens are not supported. A token alone is never sufficient: every request must carry both an access token (where applicable) and a valid signature.
Client key resolution
Section titled “Client key resolution”The authorization server needs the client’s public key to verify signatures. Open Payments supports two modes:
Wallet address-identified clients
Section titled “Wallet address-identified clients”The client’s grant request body includes client.walletAddress. The authorization server:
- Extracts the client’s domain from the wallet address.
- Issues a
GETto{walletAddress}/jwks.jsonto retrieve the client’s key registry. - Locates the key whose
kidmatches thekeyIdin theSignature-Inputheader. - Confirms the key uses the expected algorithm (
EdDSA), key type (OKP), and curve (Ed25519). - Binds the resolved key to the grant. On subsequent requests against the same grant, the authorization server fetches the bound key from the grant’s stored domain rather than the request body.
Directed-identity clients
Section titled “Directed-identity clients”For non-interactive grants (incoming-payment, quote), the client may provide its key directly in the request body as a jwk. The authorization server uses that key for signature verification on the initial request and binds it to the resulting grant. No JWKS endpoint lookup is required, and the client’s wallet address is never exposed.
ASEs must reject directed identity on interactive grant requests.
Interaction hash generation
Section titled “Interaction hash generation”During interactive grants, the authorization server redirects the resource owner back to the client’s interact.finish URI with a hash parameter. The client uses this hash to confirm that the redirect actually emanated from the authorization server.
ASEs must generate the hash by concatenating four values in order, separated by single newlines (\n), with no padding, no surrounding whitespace, and no trailing newline:
- The
noncethe client sent in the initial grant request. - The
noncethe authorization server returned in its response to the initial grant request. - The
interact_refthe authorization server is about to return on this redirect. - The grant endpoint URI the client used for its initial request.
The resulting string is hashed with sha-256 (the only hashing algorithm Open Payments currently supports), and the digest is Base64-encoded with no padding. The result is sent as the hash parameter on the redirect.
VJLO6A4CATR0KROMBDOFXG4Y5CVJCX821LH4IFWWIKYB2PQ6U56NL1https://server.example.com/txx-gguKWTj8rQf7d7i3w3UhzvuJ5bpOlKyAlVpLxBffYASEs must use the exact concatenation order and separator above. Any deviation breaks the client’s verification.
Putting the three together
Section titled “Putting the three together”The three mechanisms reinforce each other:
- HTTP signatures authenticate every request and prevent tampering in flight.
- Client keys give the authorization server a stable identity to attach grants to.
- The interaction hash closes the loop on interactive grants by letting the client cryptographically confirm the round-trip through the IdP returned to the same authorization server.
If an ASE skips signature validation, it cannot trust the client identity. If it skips key resolution, it cannot detect a forged signature. If it skips hash generation, clients cannot trust the redirect. ASEs must implement all three.
For the client-side details (signing requests, generating keys, verifying the hash), refer to HTTP signatures, Client keys, and Hash verification.