Skip to content
GitHub

Client keys

All client requests in Open Payments are signed using a unique key that identifies the client to authorization and resource servers. All requests, except for new grant requests, will carry an access token that’s bound to the key.

A key registry is a list of keys that are generated and stored by the client for when the client requires access to protected Open Payment resources. Grant requests are completed over multiple signed HTTP requests. As such, it’s important that the client provides a way to consistently identify itself to an authorization server. The key registry allows the authorization server to verify that the client is who it says it is.

Each client is represented by a wallet address. A client’s key registry is publicly accessible through its wallet address via a jwks.json endpoint. An authorization server can retrieve the client’s key registry by accessing WALLET_ADDRESS/jwks.json.

Example
https://wallet.example.com/alice/jwks.json

The key registry must expose public keys in the form of JSON Web Key Sets (JWKS). The keys must be generated using the Ed25519 algorithm and the resulting JWKS document must contain the following fields and values.

{
alg: 'EdDSA',
kty: 'OKP',
crv: 'Ed25519'
}

Additionally, the document must contain the x and kid (key ID) fields for the specific client to identify itself in a signature.

Example: https://wallet.example.com/alice/jwks.json
{
"keys": [
{
"kid": "3724c845-829d-425a-9a0d-194d6f12c336",
"x": "_Eg6UcC8G-O4TY2cxGnZyG_lMn0aWF1rVV-Bqn9NmhE",
"alg": "EdDSA",
"kty": "OKP",
"crv": "Ed25519"
}
]
}

To initialize an authenticated Open Payments client SDK, you must have a public-private key pair and a key ID.

  1. The client SDK generates a key pair using the ED25519 algorithm . The client’s key registry exposes the public key in the form of a JWKS.
  2. The public key is provided to the client’s account servicing entity (ASE). The ASE is responsible for providing a way for clients to upload keys. The ASE adds the key to its resource server.
  3. A keyId is created to identify the key pair. The ASE is usually responsible for keyId creation.
  4. The client stores the private key for future request signing. The key signs the payload described in the key proofing method section below.

Since client requests are completed over multiple signed HTTP requests, it’s important for a client to provide a way to consistently identify itself across these requests. As such, clients must include the following when making requests:

  • Headers
    • A Signature-Input header that includes the keyId associated with the client’s key pair. This header is a comma-separated list of headers that map to values in the data that was signed.
    • A signature header generated based on the Signature-Input, using the EdDSA signing algorithm
  • Body
    • A client property containing the client’s wallet address

Securing client requests follows a profile of what’s defined in the GNAP specification .

Upon receiving a signed grant request, the authorization server obtains the client’s domain from the client property. The authorization server binds the domain to the grant so it can use the domain to acquire the key set for subsequent grant requests.

The authorization server then makes a GET request to the client’s JWKS endpoint at WALLET_ADDRESS/jwks.json. In return, the server acquires the client’s key registry. The server locates the public key containing the keyId in the request’s Signature-Input header. Then, the server uses the key to decrypt and validate the request’s signature. This binds the client to the grant and allows the authorization server to continue with the grant request.

Open Payments uses the HTTP message signatures (httpsig) key proofing method.

Declare the httpsig proofing method as part of the key material when directly using a key to request a grant. The key material below is for illustrative purposes. In Open Payments, it’s expected that the wallet address be used in the grant request.

Example
"key": {
"proof": "httpsig",
"jwk": {
"kid": "3724c845-829d-425a-9a0d-194d6f12c336",
"x": "_Eg6UcC8G-O4TY2cxGnZyG_lMn0aWF1rVV-Bqn9NmhE",
"alg": "EdDSA",
"kty": "OKP",
"crv": "Ed25519"
}
}

When using httpsig, the signer (the client) creates an HTTP message signature. Open Payments clients typically secure their requests to servers by presenting an access token and proof of a key it possesses. The exception is for calls to an authorization server to initiate a grant. In this case, a key proof is used with no access token and is a non-authorized signed request.

See the HTTP message signatures page for more information specific to Open Payments. Additional information is in the specification for HTTP message signatures.

An interactive grant is necessary for creating an outgoing-payment resource. This diagram shows the sequence of calls needed between an Open Payments client SDK and the servers on the sender’s side to obtain the grant.

Before initializing the client SDK, you must have a public-private key pair and a key ID.

sequenceDiagram
    autonumber
    participant C as Open Payments Client SDK
    participant AS as Authorization server
    participant RS as Resource server
    C->>C: Initialize client SDK with the wallet
address URL, keyId, and private key C->>AS: POST grant request (interactive outgoing-payment),
signed with private key AS->>AS: Pulls keyId from grant request's signature-input
header, gets client's domain from request's body AS->>RS: GET {client_domain/jwks.json} public keys
from client's JWKS endpoint RS-->>AS: 200 JWKS document found,
returns public key AS->>AS: Validates the signature in the client's original request
using the public key, binds client's domain to the grant AS-->>C: 200 OK note over AS: Explicit consent is collected from the client's user, facilitated by the client, authorization server, and IdP (not shown) C->>AS: POST grant continuation request,
signed with private key AS->>AS: Pulls the keyId from the grant request's
signature-input header, gets client's domain
from the database entry for the grant AS->>RS: GET {client_domain/jwks.json} public key bound
to the domain from client's JWKS endpoint RS-->>AS: 200 JWKS document found,
returns public key AS->>AS: Validates signature with the
key found in the registry AS-->>C: 200 success, access token issued,
grant continuation request complete