{"id":"af76f8052202d19cbf9d8d1c9ce20fbe4f5fea0a6892f5b15c5c50fc8cfa9af8","pubkey":"1336a17e161d0e8af2b68ee95ad2a479fc38bef96a17d6127ea02a40d28dd97e","created_at":1775492308,"kind":30817,"tags":[["d","bud-11"],["title","BUD-11: Nostr Authorization"],["summary","Defines the kind 24242 event a user signs to prove to a server that they allowed an application to act on their behalf."],["s","draft"],["t","blossom"],["t","bud"],["k","24242","Blossom authorization"],["alt","A specification: BUD-11: Nostr Authorization"],["client","openspecs-import"],["published_at","1768362511"],["proxy","https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/11.md","web"],["x","d041bd4e1a6706726572079c9ec802c0591ca9a4dca1fc381311824462cd138f"]],"content":"# BUD-11\n\n## Nostr Authorization\n\n`draft` `optional`\n\nDefines the format of the authorization token used by Blossom servers to identify users and by users to authorize actions on those servers. Authorization tokens are optional; servers MAY require them for specific endpoints.\n\n## Authorization tokens\n\nAuthorization tokens are signed [nostr](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts95crz57jnvt) events of kind `24242` proving to a server that the user (`pubkey`) has permitted an application to take an action on their behalf.\n\nAll authorization tokens:\n\n- MUST have the `content` set to a human readable string explaining intended use to the user. For example `Upload Blob`, `Delete old blobs`, `List Images`, etc.\n\n- MUST have a [NIP-40](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956rqrlphyl) `expiration` tag set to a unix timestamp at which the token should be considered expired.\n\n- MUST have a `t` tag with a verb of `get`, `upload`, `list`, `delete`, or `media`. The value of the `t` tag MUST correspond to the action performed by the target endpoint, as defined in the section [Endpoint Authorization Requirements](#endpoint-authorization-requirements).\n\n## Tag scoping\n\nAuthorization tokens MAY include `server` and `x` tags to scope the token to specific servers or blob hashes.\n\n- **`server` tag**: Limits the token to specific servers by domain name. If no `server` tags are present, the token is valid for all servers. Multiple `server` tags may be present to allow the token to be used on multiple servers. The value MUST be a lowercase domain name only (e.g., `cdn.example.com`), not a full URL.\n\n- **`x` tag**: Scopes the token to specific lowercase hex-encoded blob hashes. Multiple `x` tags may be present for endpoints that require a sha256 hash. When `x` tags are present, the token is only valid for operations on the specified blob hashes.\n\nExample authorization token:\n\n```jsonc\n{\n  \"id\": \"7a1735c3852cf3f374edae4b2af2ee18e750e6dec583e19c4795d3b179af6d17\",\n  \"kind\": 24242,\n  \"pubkey\": \"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\n  \"created_at\": 1772019044,\n  \"tags\": [\n    [\"t\",\"upload\"],\n    [\"expiration\",\"1708858680\"],\n    // Authorization token MAY have multiple \"x\" tags\n    [\"x\",\"b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553\"],\n  ],\n  \"content\": \"\",\n  \"sig\": \"4b57c22b1797b109530ffe5d04cabac468b1a5942873a5141334ecbc77694fc968a1b941979ba13602fceb1dad8014ab6469c6ae9cef0b5668cc23ad1449e103\"\n}\n```\n\n## HTTP Authorization Header\n\nUsing the `Authorization` HTTP header, the authorization token MUST be encoded as Base64 URL-safe without padding (Base64url, as used by JWTs) and use the Authorization scheme Nostr\n\nExample HTTP Authorization Header:\n\n```\nAuthorization: Nostr ewogICJpZCI6ICI3YTE3MzVjMzg1MmNmM2YzNzRlZGFlNGIyYWYyZWUxOGU3NTBlNmRlYzU4M2UxOWM0Nzk1ZDNiMTc5YWY2ZDE3IiwKICAia2luZCI6IDI0MjQyLAogICJwdWJrZXkiOiAiNzliZTY2N2VmOWRjYmJhYzU1YTA2Mjk1Y2U4NzBiMDcwMjliZmNkYjJkY2UyOGQ5NTlmMjgxNWIxNmY4MTc5OCIsCiAgImNyZWF0ZWRfYXQiOiAxNzcyMDE5MDQ0LAogICJ0YWdzIjogWwogICAgWyJ0IiwidXBsb2FkIl0sCiAgICBbImV4cGlyYXRpb24iLCIxNzA4ODU4NjgwIl0sCiAgICAvLyBBdXRob3JpemF0aW9uIHRva2VuIE1BWSBoYXZlIG11bHRpcGxlICJ4IiB0YWdzCiAgICBbIngiLCJiMTY3NDE5MWE4OGVjNWNkZDczM2U0MjQwYTgxODAzMTA1ZGM0MTJkNmM2NzA4ZDUzYWI5NGZjMjQ4ZjRmNTUzIl0sCiAgXSwKICAiY29udGVudCI6ICIiLAogICJzaWciOiAiNGI1N2MyMmIxNzk3YjEwOTUzMGZmZTVkMDRjYWJhYzQ2OGIxYTU5NDI4NzNhNTE0MTMzNGVjYmM3NzY5NGZjOTY4YTFiOTQxOTc5YmExMzYwMmZjZWIxZGFkODAxNGFiNjQ2OWM2YWU5Y2VmMGI1NjY4Y2MyM2FkMTQ0OWUxMDMiCn0\n```\n\n## Validation\n\nTo validate an authorization token, a server MUST perform the following checks:\n\n1. The event `kind` MUST be `24242`.\n2. The `created_at` timestamp MUST be in the past.\n3. An `expiration` tag MUST be present and set to a Unix timestamp in the future.\n4. The `t` tag MUST contain a verb matching the intended action of the endpoint.\n5. If one or more `server` tags are present, the server MUST verify that its domain name appears in at least one `server` tag.\n6. If the endpoint requires `x` tags, the server MUST verify that at least one `x` tag matches the blob hash implied by the endpoint.\n\n## Endpoint Authorization Requirements\n\nThe table below defines, for each endpoint, the required `t` tag action, the implied blob hash (if any), and whether at least one matching `x` tag is required.\n\n| Endpoint             | Required `t` | Implied Blob Hash            | `x` Tag Requirement |\n| -------------------- | ------------ | ---------------------------- | ------------------- |\n| `GET /<sha256>`      | `get`        | `<sha256>` from the URL      | optional            |\n| `HEAD /<sha256>`     | `get`        | `<sha256>` from the URL      | optional            |\n| `PUT /upload`        | `upload`     | `X-SHA-256` request header   | required            |\n| `HEAD /upload`       | `upload`     | `X-SHA-256` request header   | required            |\n| `DELETE /<sha256>`   | `delete`     | `<sha256>` from the URL      | required            |\n| `GET /list/<pubkey>` | `list`       | —                            | not applicable      |\n| `PUT /mirror`        | `upload`     | SHA-256 of the mirrored blob | required            |\n| `PUT /media`         | `media`      | `X-SHA-256` request header   | required            |\n| `HEAD /media`        | `media`      | `X-SHA-256` request header   | required            |\n\n## Security Considerations\n\n### Unscoped Tokens\n\nA token that does not include a `server` tag is valid on any Blossom server that accepts it. If intercepted or leaked, such a token can be replayed against other servers for the duration of its validity (until its `expiration`).\n\nThis risk is particularly significant for `delete` tokens. An unscoped `delete` token intercepted from one server can be reused to delete the same blob from any other server where it exists.\n","sig":"486146321908f03c0e672659bf08992637d49c6e130dc17b1c92ef74790766ead1733a098105bbe13d727f26ef5646c98041aeac491a9b419ade39ab791a9499"}