spxp / spxp-specs

Protocol Specifications
https://spxp.org
Apache License 2.0
7 stars 1 forks source link

First run through management extension #2

Open mwiesen opened 4 years ago

mwiesen commented 4 years ago

Here is what I did. @skopf

1 Create profile profile key pair

Create a new key pair and store it in mwiesen_keypair.json:

$ ./SpxpCryptoTool genprofilekeypair > mwiesen_keypair.json
$ cat mwiesen_keypair.json

Output:

{
    "kty": "OKP",
    "d": "...",
    "crv": "Ed25519",
    "kid": "nsgPGGSTKrnjoUWZ",
    "x": "NptsYvJ9GByZEt7E5CqD2qS5W7c7hlOLEVN8dLfDUUU"
}

Make a copy without the private key:

$ ./SpxpCryptoTool extractprofilepublic mwiesen_keypair.json > mwiesen_publickey.json
cat mwiesen_publickey.json

Output:

{
    "kty": "OKP",
    "crv": "Ed25519",
    "kid": "nsgPGGSTKrnjoUWZ",
    "x": "NptsYvJ9GByZEt7E5CqD2qS5W7c7hlOLEVN8dLfDUUU"
}

2 Register new profile

Use the public key as the value for the publickey key in the following JSON template:

{
    "slug" : "mwiesen",
    "publicKey" :
}

Post the resulting JSON object to the profile registration endpoint, profiles/v01/register in our case:

$ curl -i -H 'Content-Type: application/json' \
-d '{
    "slug" : "mwiesen",
    "publicKey" : {
        "kid": "nsgPGGSTKrnjoUWZ",
        "kty": "OKP",
        "crv": "Ed25519",
        "x": "NptsYvJ9GByZEt7E5CqD2qS5W7c7hlOLEVN8dLfDUUU"
    }
}' \
http://profiles.xaldon.com/profiles/v01/register

Response:

{"message":"Profile 'mwiesen' created","status":"200"}%   

NOTES:

3 Register device

Generate a random device_id. In our case, we're going to use my-fancy-phone. Take the following template, insert the profile_uri, device_id and timestamp and store it in a file, e.g. device_registration_request.json.

$ echo '{
    "profile_uri" : "http://profiles.xaldon.com/mwiesen",
    "device_id" : "my-fancy-phone",
    "timestamp" : "2020-09-20T09:36:54.514+00:00",
}' \
> device_registration_request.json

NOTES:

Sign the device registration JSON object using the key pair:

$ ./SpxpCryptoTool sign device_registration_request.json mwiesen_keypair.json

Output:

{
    "device_id": "my-fancy-phone",
    "signature": {
        "sig": "v_yV2PsIFacArurlR-DuM1xq4gEYSoNlQAyvUmjO7UGmRQvRw5l3jcvFy-_Rtx6tIXpbUioQa7j3xVdOV3VjDw",
        "key": "nsgPGGSTKrnjoUWZ"
    },
    "profile_uri": "http://profiles.xaldon.com/mwiesen",
    "timestamp": "2020-09-20T09:36:54.514+00:00"
}

POST this JSON object to the manage/v01/auth/device endpoint:

$ curl -i -H 'Content-Type: application/json' \
-d '{
    "device_id": "my-fancy-phone",
    "signature": {
        "sig": "v_yV2PsIFacArurlR-DuM1xq4gEYSoNlQAyvUmjO7UGmRQvRw5l3jcvFy-_Rtx6tIXpbUioQa7j3xVdOV3VjDw",
        "key": "nsgPGGSTKrnjoUWZ"
    },
    "profile_uri": "http://profiles.xaldon.com/mwiesen",
    "timestamp": "2020-09-20T09:36:54.514+00:00"
}' \
http://profiles.xaldon.com/manage/v01/auth/device

Output:

{"timestamp":"2020-09-20T09:43:28.936+00:00","status":500,"error":"Internal Server Error","message":"Incorrect result size: expected 1, actual 0","path":"/manage/v01/auth/device"}%

Automation

$ echo '{
    "device_id": "my-fancy-phone",
    "profile_uri": "http://profiles.xaldon.com/mwiesen",
    "timestamp": "'$(date -u +%Y-%m-%dT%T.000)'"
}' \
| ./SpxpCryptoTool sign /dev/stdin ./mwiesen_keypair.json \
| curl -i -H 'Content-Type: application/json' -d @- \
http://profiles.xaldon.com/manage/v01/auth/device

NOTES:

mwiesen commented 4 years ago

Ok, after a bug fix, the output to the above curl is:

{"token_type":"refresh_token","device_id":"my-fancy-phone","device_token":"ATnhQZQrrQIFz44k8fn6ZRZvbPh9v3KO"}
mwiesen commented 4 years ago

Get an access token

Take the following template, insert the device_token JSON object and the timestamp and store it in a file, e.g. access_token_request.json.

$ echo '{
    "device_token" : "ATnhQZQrrQIFz44k8fn6ZRZvbPh9v3KO",
    "timestamp" : "2020-09-20T11:24:54.514+00:00"
}' \
> access_token_request.json

Sign the access token request JSON object using the key pair:

$ ./SpxpCryptoTool sign access_token_request.json mwiesen_keypair.json
{
    "signature": {
        "sig": "o3a4NX4NLGYte74PPehlanX6vT1qCr6mU6AW-FFhF8RY1Ab0NNySVxC1xZlRxhlUcUIPsrL6fE9Fr_Vi9YUNCA",
        "key": "nsgPGGSTKrnjoUWZ"
    },
    "device_token": "ATnhQZQrrQIFz44k8fn6ZRZvbPh9v3KO",
    "timestamp": "2020-09-20T11:24:54.514+00:00"
}

POST this JSON object to the /auth/access_token endpoint:

$ curl -i -H 'Content-Type: application/json' \
-d '{
    "signature": {
        "sig": "o3a4NX4NLGYte74PPehlanX6vT1qCr6mU6AW-FFhF8RY1Ab0NNySVxC1xZlRxhlUcUIPsrL6fE9Fr_Vi9YUNCA",
        "key": "nsgPGGSTKrnjoUWZ"
    },
    "device_token": "ATnhQZQrrQIFz44k8fn6ZRZvbPh9v3KO",
    "timestamp": "2020-09-20T11:24:54.514+00:00"
}' \
http://profiles.xaldon.com/manage/v01/auth/access_token

Output:

{"token_type":"access_token","access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJtd2llc2VuIiwiZXhwIjoxNjAwNjA1NDMxfQ.9i5E0uFausrOuEmLtBk1jfTN5yKwl1K2b7bIbsgzI1Symsqc0hsCHl4r-fWwMf0DMdzrxrQrUYQOV-k6FalcOw","expires_in":3600}

Automation

Since access tokens expire rather quickly, it's likely that we need to use the device token to request a new access token rather frequently. So it makes sense to squash the above steps into one command that takes care of JSON object signing, the actual curl as well as access token extraction. We're going to use an environment variable for that:

export MWIESEN_DEVICE_TOKEN="ATnhQZQrrQIFz44k8fn6ZRZvbPh9v3KO"

Then we can get a new access token and store it in a file, e.g. mwiesen_access_token.json with the following shell command:

$ echo '{
    "device_token" : "'$MWIESEN_DEVICE_TOKEN'",
    "timestamp" : "'$(date -u +%Y-%m-%dT%T.000)'"
}' \
| ./SpxpCryptoTool sign /dev/stdin ./mwiesen_keypair.json \
| curl -H 'Content-Type: application/json' -d @- http://profiles.xaldon.com/manage/v01/auth/access_token \
| jq -r .access_token > mwiesen_access_token.json

Content of mwiesen_access_token.json:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJtd2llc2VuIiwiZXhwIjoxNjAwODY5MTQ4fQ.hKE3q17SzOB4TqHaKKwSBgsahoZAQnU3dyOL3dk7fiOab08zqTYEDNabVT26bVzv8reelqVtMQ6c9ZCjt4piCw
mwiesen commented 4 years ago

Get service info

This is a simple GET request against the service/info endpoint. It's authenticated by providing the access token in an HTTP header using the "Bearer" authentication scheme:

curl -X GET -i -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJtd2llc2VuIiwiZXhwIjoxNjAwNjA1NDMxfQ.9i5E0uFausrOuEmLtBk1jfTN5yKwl1K2b7bIbsgzI1Symsqc0hsCHl4r-fWwMf0DMdzrxrQrUYQOV-k6FalcOw' \
http://profiles.xaldon.com/manage/v01/service/info

We can also provide the access token via the mwiesen_access_token.json file to avoid copying and pasting:

curl -X GET -i -H "Authorization: Bearer $(cat mwiesen_access_token.json)" \
http://profiles.xaldon.com/manage/v01/service/info

Output:

{"server":{"vendor":"xaldon Technologies GmbH","product":"SPXP Server","version":"0.1","helpUri":"http://xaldon.com"},"endpoints":{"friendsEndpoint":"/mwiesen/friends","postsEndpoint":"/mwiesen/posts","keysEndpoint":"/mwiesen/keys","publishEndpoint":"/mwiesen/publish","connectEndpoint":"/mwiesen/connect","connectResponseEndpoint":"/mwiesen/packageExchange"},"limits":{"maxMediaSize":10485760}}
mwiesen commented 4 years ago

Retrieve service messages

Let's check if there are already service messages for our profile on the server. Another GET request against the /service/messages endpoint with a maximum messages parameter and before and after timestamps:

curl -X GET -i -H "Authorization: Bearer $(cat mwiesen_access_token.json)" \
http://profiles.xaldon.com/manage/v01/service/messages?max=100&before=2020-09-26T14:00:00.000+00:00&after=2020-09-16T11:24:54.514+00:00

Output:

{"data":[],"more":false}

No messages yet ;-)

mwiesen commented 4 years ago

Publishing profile objects

In the spec it says we need to PUT a new JSON object to the following endpoint /profile/root. So here is what we're trying to put. It adds a shortInfo and about key.

Hint: Don't forget to include your public key object and endpoints like the posts and friends endpoints. Generally it's advised to either first get the full profile from the server (e.g. by curling) or to maintain an up-to-date version of the profile locally, that then can be transmitted.

Hint: The JSON object is case sensitive, so e.g. a shortinfo key won't work as it needs to be shortInfo.

{
  "ver": "0.3",
  "name": "mwiesen",
  "shortInfo": "mwiesen's profile",
  "about": "mwiesen is a social media connoisseur and transparency evangelist par excellence.",
  "friendsEndpoint": "/mwiesen/friends",
  "postsEndpoint": "/mwiesen/posts",
  "keysEndpoint": "/mwiesen/keys",
  "publishEndpoint": "/mwiesen/publish",
  "connectEndpoint": "/mwiesen/connect",
  "connectResponseEndpoint": "/mwiesen/packageExchange",
  "publicKey": {
    "kty": "OKP",
    "crv": "Ed25519",
    "kid": "nsgPGGSTKrnjoUWZ",
    "x": "NptsYvJ9GByZEt7E5CqD2qS5W7c7hlOLEVN8dLfDUUU"
  }
}

The resulting shell command is:

$ echo '{
  "ver": "0.3",
  "name": "mwiesen",
  "shortInfo": "mwiesen'\''s profile",
  "about": "mwiesen is a social media connoisseur and transparency evangelist par excellence.",
  "friendsEndpoint": "/mwiesen/friends",
  "postsEndpoint": "/mwiesen/posts",
  "keysEndpoint": "/mwiesen/keys",
  "publishEndpoint": "/mwiesen/publish",
  "connectEndpoint": "/mwiesen/connect",
  "connectResponseEndpoint": "/mwiesen/packageExchange",
  "publicKey": {
    "kty": "OKP",
    "crv": "Ed25519",
    "kid": "nsgPGGSTKrnjoUWZ",
    "x": "NptsYvJ9GByZEt7E5CqD2qS5W7c7hlOLEVN8dLfDUUU"
  }
}' \
| ./SpxpCryptoTool sign /dev/stdin ./mwiesen_keypair.json \
| curl -X PUT -i -H "Authorization: Bearer $(cat mwiesen_access_token.json)" -H 'Content-Type: application/json' -d @- \
http://profiles.xaldon.com/manage/v01/profile/root

Response: HTTP/1.1 201

mwiesen commented 4 years ago

Create a new post

Here, we need to POST a post JSON object as defined in the SPXP spec to the /posts endpoint. Here is an example:

{
    "seqts" : "'$(date -u +%Y-%m-%dT%T.000)'",
    "type" : "text",
    "message" : "Hello, world!"
}

The resulting shell command is:

$ echo '{
    "createts" : "'$(date -u +%Y-%m-%dT%T.000)'",
    "type" : "text",
    "message" : "Hello, world!"
}' \
| ./SpxpCryptoTool sign /dev/stdin ./mwiesen_keypair.json \
| curl -i -H "Authorization: Bearer $(cat mwiesen_access_token.json)" -H 'Content-Type: application/json' -d @- \
http://profiles.xaldon.com/manage/v01/posts

Response: HTTP/1.1 201

List posts

This is actually not a part of the Management Extension but of the basic SPXP profile specification. But it's fun to see the new post listed. It's as simple as requesting:

curl http://profiles.xaldon.com/mwiesen/posts?max=2

Result:

{"data":[{"signature":{"sig":"xMsOtKNvWR83IuZI3n04YCb93_3R3AIJoA0a0PvGo5EfXX-ANaOA4VXu5WODazsKwe5TjNaniG1PdRqr2sGhBg","key":"nsgPGGSTKrnjoUWZ"},"createts":"2020-09-23T19:26:38.000","seqts":"2020-09-23T19:26:38.401","type":"text","message":"This post can soon be deleted again!"},{"signature":{"sig":"YqDqOqAp2d_G85UYRJIq6rrMVn6e4cIncxNx16PJNAJmQ5eKWawJj26tVTDAZUAc8-L-jv-hsKf39PHcfyOeAg","key":"nsgPGGSTKrnjoUWZ"},"createts":"2020-09-23T19:10:25.000","seqts":"2020-09-23T19:10:25.615","type":"text","message":"Blaukraut bleibt Blaukraut und Brautkleid bleibt Brautkleid!"}],"more":true}
mwiesen commented 4 years ago

Delete post

To delete a post, we need to send an authenticated DELETE request to the /posts endpoint. It needs to provide the unique seqts of the post object. Here's how it looks like:

$ curl -X DELETE -i -H "Authorization: Bearer $(cat mwiesen_access_token.json)" \
http://profiles.xaldon.com/manage/v01/posts/2020-09-23T19:26:38.401

Result: HTTP/1.1 204

A new request for listing the posts retrieves:

{"data":[{"signature":{"sig":"YqDqOqAp2d_G85UYRJIq6rrMVn6e4cIncxNx16PJNAJmQ5eKWawJj26tVTDAZUAc8-L-jv-hsKf39PHcfyOeAg","key":"nsgPGGSTKrnjoUWZ"},"createts":"2020-09-23T19:10:25.000","seqts":"2020-09-23T19:10:25.615","type":"text","message":"Blaukraut bleibt Blaukraut und Brautkleid bleibt Brautkleid!"},{"signature":{"sig":"oB_faGLpdvPKYwN4cEpecSyow8bzjdRrPXsUvX7UteQdgDIUBq9UDXm9n9BZV-XaF6FamlOsGx84G78Vn_2zDw","key":"nsgPGGSTKrnjoUWZ"},"createts":"2020-09-23T19:09:38.000","seqts":"2020-09-23T19:09:38.780","type":"text","message":"Hello, world!"}],"more":false}