Tokens

Tokens in XYZ are issued in response to a request to the transaction endpoint. Unlike OAuth 2, tokens are never issued in the front channel via the browser regardless of the mode or type of client.

{
    "access_token": {
        "value": "OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0",
        "proof": "bearer"
    }
}

Access token formats and values are opaque to the client, but must be known to both the AS (so that it can issue them), and sometimes to the RS (so that it can interpret them, unless it uses an externalized service for doing so).

Token-key binding

A token issued without an associated key is a bearer token, and any party with access to the token can present it to the RS. This token can be presented as-is if the type field is bearer, and the client sends it using the Authorization header method defined in RFC6750.

GET / HTTP/1.1
Host: server.example.com
Authorization: Bearer OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0

A token issued with an associated key is bound to that key, and proof of possession of that key must be presented to the RS alongside the token.

{
    "access_token": {
        "value": "OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0",
        "proof": "httpsig",
        "key": {
            "jwk": {
                "kty": "RSA",
                "e": "AQAB",
                "kid": "xyz-1",
                "alg": "RS256",
                "n": "kOB5rR4Jv0GMeLaY6_It_r3ORwdf8ci_JtffXyaSx8xYJCCNaOKNJn_Oz0YhdHbXTeWO5AoyspDWJbN5w_7bdWDxgpD-y6jnD1u9YhBOCWObNPFvpkTM8LC7SdXGRKx2k8Me2r_GssYlyRpqvpBlY5-ejCywKRBfctRcnhTTGNztbbDBUyDSWmFMVCHe5mXT4cL0BwrZC6S-uu-LAx06aKwQOPwYOGOslK8WPm1yGdkaA1uF_FpS6LS63WYPHi_Ap2B7_8Wbw4ttzbMS_doJvuDagW8A1Ip3fXFAHtRAcKw7rdI4_Xln66hJxFekpdfWdiPQddQ6Y1cK2U3obvUg7w"
            }
        }
    }
}

The associated key may be one of the public keys the client has proved ownership of when talking to the AS during this transaction, or it may be a key generated by the AS and handed to the client.

Methods for key binding are the same as used by the client to talk to the transaction endpoint, but applied to the resource server.

Since we don't have full control over the resource server's API, the methods available to the client aren't as well defined ahead of time. For example, using detached JWS, we rely on signing the body, but there may not be a request body at the RS. This makes more sense with MTLS.

Managing access tokens

If the access token is issued alongside a token management URI:

{
    "access_token": {
        "value": "OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0",
        "proof": "bearer",
        "manage": "https://server.example.com/token/PRY5NM33OM4TB8N6BW7OZB8CDFONP219RP1L"
    }
}

Then the client can send a POST request to the token management URL to get a new access token.

The token itself is used to access the API at the management URL. If the token is bound to a specific key, the client has to present that token If the token is a bearer token, the client has to prove possession of the same key that was used to issue the token.

POST /token/PRY5NM33OM4TB8N6BW7OZB8CDFONP219RP1L HTTP/1.1
Host: server.example.com
Authorization: Bearer OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0
Detached-JWS: ejy... 

If the management URI is still valid, the AS revokes the old access token (if possible) and issues the new access token.

{
    "access_token": {
        "value": "FKPLDO5394XVIWHVNR7POUNC4OYJ2LWKYYZGKFS6",
        "proof": "bearer",
        "manage": "https://server.example.com/token/PRY5NM33OM4TB8N6BW7OZB8CDFONP219RP1L"
    }
}

Note that since this process could result in the management URL rotating upon each use. If no management URL is returned, the client can no longer refresh the token.

A client can request the revocation of a token by sending a DELETE request to the token management URL.

DELETE /token/PRY5NM33OM4TB8N6BW7OZB8CDFONP219RP1L HTTP/1.1
Host: server.example.com
Authorization: Bearer OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0
Detached-JWS: ejy... 

Both the AS and client throw out the access token after completion.

Multiple access tokens

A client can make a request with a multi-part resources section, which is formatted as a JSON object with the field names chosen by the client to differentiate the access being requested. A request would look like this.

{
    "token1": [
        {
            "type": "example.com/resource-set",
            "actions": [
                "read",
                "write",
                "dolphin"
            ],
            "locations": [
                "https://server.example.net/",
                "https://resource.local/other"
            ],
            "datatypes": [
                "metadata",
                "images"
            ]
        }
    ],
    "token2": [
        {
            "type": "example.com/resource-set",
            "actions": [
                "foo",
                "bar",
                "dolphin"
            ],
            "locations": [
                "https://resource.other/"
            ],
            "datatypes": [
                "data",
                "pictures"
            ]
        }
    ]
}

For this request, the server can return multiple access tokens like this.

{
    "multiple_access_tokens": {
        "token1": {
            "value": "OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0",
            "proof": "bearer"
        },
        "token2": {
            "value": "UFGLO2FDAFG7VGZZPJ3IZEMN21EVU71FHCARP4J1",
            "proof": "bearer"
        }
    }
}

The token1 access token in the response corresponds directly to the token1 portion of the resources request, and the same applies to token2. The AS can't add an additional access token that the client did not specifically request, but it can omit a requested access token in case it was not issued. This could be because the user denied the authorization request or some other policy decision demanded it.