SEP: 0034
Title: Wallet Request Attribution for Anchors
Author: Jake Urban, Leigh McCulloch
Track: Standard
Status: Final Comment Period (FCP)
Created: 2020-08-17
Discussion: https://github.com/stellar/stellar-protocol/issues/685
This protocol defines how anchors can cryptographically attribute HTTPS requests to wallet applications.
In general, web services may want to attribute resources created or actions taken on their service to client applications. Because requests headers like User-Agent
can be spoofed, these web services require a cryptographically secure method of verifying a client's identity.
Throughout this document, the resource mentioned is a deposit or withdrawal transaction, but the resource or action that requires identity verification could be related to different use cases between wallets and anchors.
This following protocol involves three parties:
- The Wallet: the application that makes HTTPS requests to the anchor server for a resource or action
- The Wallet Server: the web server that claims ownership of the wallet
- Note that frontend-only wallets must add a backend web server to comply with this protocol
- The Anchor Server: the web server that verifies the wallet's claims
At a high level, the Wallet Server generates a JSON Web Signature (JWS) that the Wallet receives and includes in future requests to the Anchor Server. The Anchor Server verifies that the JWS included in the request was signed by the Wallet Server and that the transaction (or resource) ID was included in the decoded JSON Web Token (JWT) payload. These steps prove that the Wallet Server takes ownership of the requests being made for a particular transaction.
How the Wallet communicates with its Wallet Server and receives the JWS is out of the scope of this document. However, there are basic requirements for any functioning Wallet Server. Do not accept requests for JWS tokens if:
- the Wallet cannot be authenticated. The authentication mechanism used is up to the Wallet Server
- the request omits any of the following parameters:
- the resource or action ID the Wallet received from the Anchor Server
- the user's Stellar account that funds will be deposited into/withdrawn from
- the fully qualified URL of the Anchor Server
If the request meets the above criteria, the Wallet Server should return a JWS token to the Wallet in the format described in the next section.
The JWS is generated using two JSON objects and a JSON Web Key (JWK).
The first JSON object is the JWS Protected Header:
{
"typ": "JWT",
"alg": "EdDSA",
"kid": "GCR5WQYXYT4ECBQ3SBALXHICPEVTWKY75XKKZ3ZMF63EXJ5RCWWDO726"
}
typ
is the type of payload encodedalg
specifies the algorithm used to secure the JWSEdDSA
stands for Edwards-curve Digital Signature Algorithm and is the same algorithm used for Stellar keypairs- Using the same algorithm allows signing servers to use the keypair specified by their stellar.toml's
SIGNING_KEY
to sign the JSON-encoded objects described here
kid
is the Key Id (Stellar public key) used to sign the JWS
The second JSON object is a JWS Payload. In this case, we'll use a JWT containing members similar to the claims described in SEP-10:
{
"iss": "https://walletsigningserver.com",
"sub": "GAC22YV3EG62HMQF5UQIO5HT6FCPLC2GEZ2FIAVGPEEIKWRQM5AN5TIS",
"jti": "aa77983a-e550-4d90-8cc2-d661d7f0b8f6",
"kid": "GCR5WQYXYT4ECBQ3SBALXHICPEVTWKY75XKKZ3ZMF63EXJ5RCWWDO726",
"aud": "https://anchorserver.com",
"exp": "1597789801",
"iat": "1597703375"
}
iss
— the fully qualified URL of the home domain for the Wallet Server issuing the tokensub
— the Stellar account address of the user requesting the transactionjti
— the identifier for the resource (transaction) being processed by the Wallet and defined by the Anchor Serverkid
— the Stellar public key address used to sign the JWSaud
— the fully qualified URL of the Anchor Server's home domainiat
— the timestamp for the time this token was issuedexp
— the timestamp for the time on or after which the JWT must not be accepted for processing
The two JSON objects should be Base64-encoded and concatenated with a .
character.
eyJhbGciOiJFZERTQSIsImtpZCI6IkdDUjVXUVlYWVQ0RUNCUTNTQkFMWEhJQ1BFVlRXS1k3NVhLS1ozWk1GNjNFWEo1UkNXV0RPNzI2IiwidHlwIjoiRWREU0EifQ
.
eyJhdWQiOiJodHRwczovL2FuY2hvcnNlcnZlci5jb20iLCJleHAiOiIxNTk3Nzg5ODAxIiwiaWF0IjoiMTU5NzcwMzM3NSIsImlzcyI6Imh0dHBzOi8vd2FsbGV0c2lnbmluZ3NlcnZlci5jb20iLCJqdGkiOiJhYTc3OTgzYS1lNTUwLTRkOTAtOGNjMi1kNjYxZDdmMGI4ZjYiLCJraWQiOiJHQ1I1V1FZWFlUNEVDQlEzU0JBTFhISUNQRVZUV0tZNzVYS0taM1pNRjYzRVhKNVJDV1dETzcyNiIsInN1YiI6IkdBQzIyWVYzRUc2MkhNUUY1VVFJTzVIVDZGQ1BMQzJHRVoyRklBVkdQRUVJS1dSUU01QU41VElTIn
The resulting string should then be signed by the Wallet Server's JSON Web Key (JWK). The JWK itself is generated using the Wallet Server's SIGNING_KEY
listed in the server's stellar.toml
and the associated secret key.
Stellar keypairs use their own encoding and representation, strkey. However, the raw bytes can be extracted and Base64-encoded to get the values necessary for the JWK.
For example, the following Stellar Keypair's raw key bytes can be extracted and represented in Base64:
Representation | Value |
---|---|
Stellar | GAC22YV3EG62HMQF5UQIO5HT6FCPLC2GEZ2FIAVGPEEIKWRQM5AN5TIS |
Base64 | Ba1iuyG9o7IF7SCHdPPxRPWLRiZ0VAKmeQiFWjBnQN4= |
Using the Base64 representation of the Stellar keypair and the Base64 encoded JSON objects described above as inputs to the JSON Web Algorithms library of your choice will produce the following JWS:
eyJhbGciOiJFZERTQSIsImtpZCI6IkdDUjVXUVlYWVQ0RUNCUTNTQkFMWEhJQ1BFVlRXS1k3NVhLS1ozWk1GNjNFWEo1UkNXV0RPNzI2IiwidHlwIjoiRWREU0EifQ
.
eyJhdWQiOiJodHRwczovL2FuY2hvcnNlcnZlci5jb20iLCJleHAiOiIxNTk3Nzg5ODAxIiwiaWF0IjoiMTU5NzcwMzM3NSIsImlzcyI6Imh0dHBzOi8vd2FsbGV0c2lnbmluZ3NlcnZlci5jb20iLCJqdGkiOiJhYTc3OTgzYS1lNTUwLTRkOTAtOGNjMi1kNjYxZDdmMGI4ZjYiLCJraWQiOiJHQ1I1V1FZWFlUNEVDQlEzU0JBTFhISUNQRVZUV0tZNzVYS0taM1pNRjYzRVhKNVJDV1dETzcyNiIsInN1YiI6IkdBQzIyWVYzRUc2MkhNUUY1VVFJTzVIVDZGQ1BMQzJHRVoyRklBVkdQRUVJS1dSUU01QU41VElTIn0
.
wIKlL0IF-FF8m_1kNiLdLtL65OyKUN0me5Tl8PyHfxZAtvlmu7lJ_33POkvuMw9Tl4Z-njPkW4ecAzojnb5RBw
The public key used to generate the JWS can also be used to verify the integrity of Protected Header and Payload. This is what the Anchor Server would do when a Wallet makes a request containing the JWS token.
Other proposals will specify how the JWS returned by the Wallet Server should be sent to the Anchor Server. The Anchor can keep the public keys of known Wallet Servers, but if the client is unknown, the Anchor should request the Wallet Server's stellar.toml and validate that the value found was used to encrypt the JWS.
Once verified, the Anchor Server can use the JWT attributes with confidence that the integrity of the data has been maintained and that the Wallet requesting the transaction and passing the JWS belongs to the Wallet's Server.
If Wallet Servers change or rotate their SIGNING_KEY
, Anchor Servers receiving JWS tokens from these Wallets may have to update the local copies of the Wallet Servers' public keys when initial verification fails. Anchors must ensure the updated SIGNING_KEY
is collected from the same service the previous SIGNING_KEY
was collected from. Therefore, Anchors should retain a mapping of known Wallet Server home domains and public signing keys.
Wallet Servers should select an expiration time for the JWT that is appropriate for the assumptions and risk of the interactions a user can perform with it. A Wallet application that owns the request for a deposit or withdrawal transaction at the time the JWT is issued may not own it in the future. While unlikely, a user could continue processing a transaction initiated previously with a different wallet application.
Expiration times that are too long increase the risk that control of the transaction request will change. Expiration times that are too short increase the number of reauthentication attempts required by the wallet, which creates a poor user experience.