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",
        "type": "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

If the type is sha3 then the token is presented as the Base64 encoded SHA3 hash of the token value.

GET / HTTP/1.1
Host: server.example.com
Authorization: Bearer WtHmGirjNWJUrWEv7K0I46_hwEdS7SqMcKZOmEsC8XE610t6o7DAv57Zm-3TL3Y9C7UG2AAynXQbwmROXaPMug

I'm not convinced that this method is worthwhile. Without some randomness mixed in to the request, the hash is static and therefore as good as the original token for replay and capture. Non-bearer access really needs binding to a key, as below.

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",
        "jwks": {
            "keys": [
                {
                    "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.

Refreshing tokens

If the access token is issued alongside a transaction handle:

{
    "access_token": {
        "value": "OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0",
        "type": "bearer"
    },
    "handle": {
        "value": "80UPRY5NM33OMUKMKSKU",
        "type": "bearer"
    }
}

Then the client can use that handle to request a new access token with the same characteristics.

{
    "handle": "80UPRY5NM33OMUKMKSKU"
}

If the transaction handle is still valid, the AS issues the new access token.

{
    "access_token": {
        "value": "FKPLDO5394XVIWHVNR7POUNC4OYJ2LWKYYZGKFS6",
        "type": "bearer"
    },
    "handle": {
        "value": "3TK228JBA3532AFKQUFN",
        "type": "bearer"
    }
}

Note that since this process uses a transaction handle, which is rotated upon each use, if the AS wants to let the client continue to get more access tokens it will return the new access token with a new transaction handle.

It is up to the AS whether it wants to throw out the original access token.

Revoking tokens

A client can request the revocation of a token by sending a DELETE request to the transaction endpoint with the access token in the payload and the client's bound keys that were used during the token's issuance in the request.

It would be nice to allow the token to be its own credential for its deletion, especially in cases where it's bound to, for instance, the client's certificate.

The semantics of a body with a DELETE request are iffy at best, but as an alternative, the AS could issue a token-management URL alongside the access token. This kind of URL could be used for introspection by the RS, if the RS had a way to get the URL along with the token itself.