Skip to content

BUD-11 Authorization#91

Open
hzrd149 wants to merge 2 commits intomasterfrom
update-auth-tokens
Open

BUD-11 Authorization#91
hzrd149 wants to merge 2 commits intomasterfrom
update-auth-tokens

Conversation

@hzrd149
Copy link
Owner

@hzrd149 hzrd149 commented Jan 14, 2026

The PR moves the optional authorization out into its own BUD and clarifies the role of the x and server tags

Readable Version

@hzrd149
Copy link
Owner Author

hzrd149 commented Jan 14, 2026

@pippellia-btc here is a quick first draft based on our discussions in #87 and nostr-protocol/nips#2187. I would appreciate feedback when you have the time

I haven't added any requirements or recommendations in to mitigate the issue with delete auth tokens. so this still needs to work at least recommend servers (or maybe clients) don't allow generic delete tokens

@pippellia-btc
Copy link

Hey man, here are the things I like:

  1. having one specific BUD for auth as it simplifies implementation
  2. the server tag being explicitly a domain name
  3. the expiration tag

Here are things that I think are confusing or need more polishing:

  1. Base64 encoding doesn't specify if it's with or without padding, URL safe or default. JWTs use Base64 URL-safe no padding, so for the sake of removing ambiguities and following the status quo I would make the same choice here.

  2. I miss having the not-before tag, as that can be useful for doing deferred delegation which I think will be useful in some cases. Given how hard it is to change auth schemes, I'll take the chance to add these goodies to be more future proof.

  3. I find the last section about the endpoints confusing because the role of the x tag seems to always be optional. One idea is to clarify its role is to make the use of the x tag equivalent to that of the server tag.

  1. If one or more x tags are present, the server MUST verify that the target blob hash matches at least one value specified in an x tag. If no x tags are present, the token MUST be considered valid for all blobs.

Now, even though this sounds good in theory, in practice this creates complex situations when the hash of the blob must be computed from the request body, which is the case in PUT /upload and PUT /media.
Verification should be cheap, and not require to buffer the request body (which can be massive), or pay the cost to store to disk before knowing if the request is verified.
Also, it can be expensive for clients to compute the hash of a file they want to upload.

Another idea would be to remove the x tag entirely, but that would also make DELETEs dangerous.
Fundamentally the x tag is only needed when the endpoint is /<sha256>.

Tags "Refactor"

I though about this way to "refactor" the auth tags.

  • The t tag is removed in favor of a tag that commits to the method (e.g. GET, PUT...)
  • The x tag is removed in favor of a tag that commits to the relative path of the request (e.g. /upload, /<sha256>)

old event

  "tags": [
    ["t", "upload"],
    ["x", "b16741...53"], // <--- have to buffer the body to validate this
    ["expiration", "1708858680"]
  ],

(possible) new event UPLOAD

  "tags": [
    ["path", "/upload"],
    ["method", "PUT"],
    ["expiration", "1708858680"]
  ],

(possible) new event DELETE

  "tags": [
    ["path", "/b16741...53"], // <--- commits to the blob being deleted
    ["method", "DELETE"],
    ["expiration", "1708858680"]
  ],

Obviously this is a breaking change, but IMO it's worth it to make things right. Nostr and Blossom are super small compared to what they might become, which means there will never be a better time to build solid foundations, yes, even at the cost of breaking old stuff.

@pippellia-btc
Copy link

As we agreed during our call, @hzrd149, my proposed solution would make upload tokens generic by removing the commitment to one or more blobs. This has the following effects:

  • Taking control away from the signer:
  • making leaks of these events more dangerous (an attacker can upload any blob using your public key).

One interesting idea to make verification cheaper and more self-contained is as follows:
Require clients to specify the Content-Digest header.
If x tags are present, the server must verify that the Content-Digest is present in at least one x tag. If no x tags are present, the token is valid for all blobs.

This doesn't change the nature of the commitment, but it allows for better separation of concerns and, in my opinion, simpler code. The reasons are:
For all endpoints, verifying the authorization event becomes cheap because it depends only on the request headers and the URL path. In other words, the authorization event commits only to the HTTP request metadata.
If the request metadata is incorrect (e.g., if the client is lying), we can only react afterwards and penalize the client.

In the framework I am building, this change would simplify the code by allowing for a more straightforward separation of concerns.

  1. First, I parse the request metadata.
  2. I validate the authorization event against the metadata.
  3. I apply the rejection functions.
  4. I apply the saving function.
  5. I react to lies.

Currently, steps 2. and 5. are intertwined, which, in my opinion, makes things harder to understand.

@flox1an
Copy link

flox1an commented Jan 29, 2026

I like the flexibility of making the x and server optional. Removing the x from the auth event would allow delegating the auth event to a DVM and to allow uploading in the name of the user. For transcoding usecases the hash is not known beforehand.

@hzrd149
Copy link
Owner Author

hzrd149 commented Jan 29, 2026

I like the flexibility of making the x and server optional. Removing the x from the auth event would allow delegating the auth event to a DVM and to allow uploading in the name of the user. For transcoding usecases the hash is not known beforehand.

This could be useful. bun I dont like the idea of users (or clients asking users) to give away free upload tokens. its asking to be exploited even if they set the expiration to something really aggressive

At least for now I think it makes sense to keep the requirement for the /upload to require at least one x tag on the token

@pippellia-btc
Copy link

I agree with @flox1an. The x tag should be optional just like the server tag. First, it's symmetrical, which makes sense to me because both seems to be equally important (x = what, server = where).

@hzrd149 if we take seriously the idea that users are authorizing something (and not authenticating with someone), then the authorization should be expressive, allowing maximum freedom, even freedom to shoot yourself in the foot.

Otherwise clients / servers will eventually just diverge from the spec if they need this freedom for their usecases

@hzrd149
Copy link
Owner Author

hzrd149 commented Jan 31, 2026

Removing the requirement for the x tag in uploads and mirrors would make the authorization logic symmetrical, and there may be merit in apps delegating upload permissions. however I'm reluctant to remove the requirement because of the second order effects it could have.

Once we allow uploads without requiring the x tag, that becomes the easiest implementation path for apps uploading to servers. Developers and especially AI agents tend to choose the simplest solution to get a feature working.
This would create a slippery slope:

  1. Developers will default to omitting the hash - It's simpler to skip calculating the hash client-side and just trust the server to handle it.
  2. Hash verification will be skipped - If clients aren't calculating the hash before upload, they won't verify it after upload either. They'll just trust the server.
  3. As more apps adopt this pattern, it becomes increasingly uncommon for apps to require servers not to transform or modify blobs, since they're no longer checking the hash anyway.
  4. We're back to trusted servers - Eventually, clients are simply uploading to a single server because they trust that server, without any verification of the upload.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants