Write an ASPE server
What does an ASPE server do?
An Ariadne Signature Profile (ASP) is a type of identity profile that services like Keyoxide can verify but which rely on basic cryptographic standards like EdDSA and ES256 instead of cryptographic tools like OpenPGP.
The ASP Exchange (ASPE) protocol refers to the set of HTTP requests by which people can upload and download ASPs to and from dedicated servers.
A public ASPE server will:
- receive and verify ASP profiles that people upload,
- store ASP profiles,
- send the ASP profiles upon requests.
ASP profiles are encoded, stored and distributed as so-called profile JWSs, which in turn are sent to the ASPE server as part of so-called request JWSs.
Of course, an ASPE server could also be private, meaning people will not be able to upload their profiles to it — or only a list of pre-authorized people/profiles may do so — but the profiles that the server does store should still be publicly accessible.
Implementing an ASPE server
Now, let's try and implement an ASPE server! This will be a simple overview of the different required steps. For more implementation details, refer to the ASP specification.
I have already written a minimal ASPE server written in Rust, creatively named aspe-server-rs. I will be basing this guide on that repository, so feel free to inspect the code for more context.
Do you have a static web page? Follow the Implement ASPE on a static site guide instead.
Receiving ASPs
Let's begin the guide by looking at how ASPE servers receive ASPs.
An ASPE server has an endpoint dedicated to receiving ASPs:
POST ENDPOINT = '/.well-known/aspe/post'
This endpoint must use HTTPS and accept POST requests. POST requests must have a so-called request JWS as body (see the Request JWS section).
Upon receiving a POST request, the server must validate the request JWS by checking the different values it contains as well as validating the signature. These checks are all described in the Request JWS section.
The server must also check the validity of the profile JWS contained within the request JWS.
Luckily, there are many libraries and tools to help with this validation process, including jose (JS) and josekit (Rust).
If the request JWS is valid, the profile JWS it contains must be stored in a way the server implementation sees fit. See the Storing ASPs section. The request JWS must be discarded.
Request JWS
A request JWS is a JSON Web Signature (RFC7515) that is sent over HTTPS to transmit a so-called profile JWS (see the Profile JWS section).
To validate a request JWS, make sure that:
- the
typ
header is set toJWT
, - the
alg
header is set to eitherEdDSA
orES256
, - the
jwk
header is set to a valid JWK key, - the
kid
header is set to the JWK Key's fingerprint as obtained in Computing the fingerprint, - the
http://ariadne.id/type
property is set torequest
, - the
http://ariadne.id/action
property is set tocreate
, - the
http://ariadne.id/profile_jws
property is set to a valid Profile JWS, - the
iat
property is within a reasonable amount of time of the time of processing.
A reasonable amount of time is subjective and requires deliberate considation from the developer. A shorter amount of time helps protect profiles against replay attacks but punishes people living in regions with poor connectivity and flaky internet. The spec recommends accepting iat
values within an hour of the moment that the server receives the request, both before and after.
Profile JWS
A profile JWS is a JSON Web Signature that contains all the information of an ASP profile.
To validate a profile JWS, make sure that:
- the
typ
header is set toJWT
, - the
alg
,jwk
andkid
headers are set to the same values as in the request JWS, - the
http://ariadne.id/version
property is set to0
(as of this writing), - the
http://ariadne.id/type
property is set toprofile
, - the
http://ariadne.id/name
property is set, - the
http://ariadne.id/claims
property is set, - if the
exp
property is set, its value is not in the past.
Computing the fingerprint
To obtain the fingerprint associated with the ASP profile, one must create a specific JSON object based on the JWK-encoded public key, serialize it, hash it, truncate it and encode it. All the details for this process can be found in section 2.2 of the spec.
Storing ASPs
The server may store ASPs in any way it sees fit, as long as it does so in a queryable manner. After all, the sole purpose of storing ASPs is to subsequently retrieve and distribute them in response to a request.
ASPs can only be identified and requested by their URI:
URI = 'aspe:' domain ':' fingerprint
The domain is the one used to access the ASPE server.
The key's fingerprint is obtained as described in Computing the fingerprint. The fingerprint needs to be taken into consideration when implementing a storage layer for the ASPE server.
A simple Key-Value Database could suffice, where the fingerprint is the key and the profile JWS the value.
Distributing ASPs
The endpoint used to request ASPs from the server looks as follows:
GET ENDPOINT = '/.well-known/aspe/id/' fingerprint
Requests made to this endpoint must be GET requests.
If an ASP profile with the requested fingerprint was indeed stored by the ASP server and the profile is not expired (see Profile JWS), it should be sent as the response to the request; if not, the server should return a 404 NOT FOUND.
Allowing servers to update and delete ASPs
Updating and deleting ASPs are basically variations on the process of uploading ASPs!
When the ASPE server receives a request JWS with the http://ariadne.id/action
property set to update
, the server should check whether it indeed already has stored an ASP with the fingerprint of the incoming ASP and whether the keys perfectly match. If all checks are positive, the server can overwrite the existing ASP with the incoming ASP.
Likewise, if the request JWS has the http://ariadne.id/action
property set to delete
and the server has found an existing ASP with a matching fingerprint, it should remove said ASP from storage.
More information
All the information provided above is further elaborated on in the ASP specification.
Feel free to reach out to the community for questions and discussions.