{"id":"78c25aade9999d39d2afb7912f88ee1b5671b52ee4a0d5dae0a3e0dae05bcdab","pubkey":"d1678e7ef965374bbea308a1215609a78376dc158277a7d657680f9d5efd5c38","created_at":1774080108,"kind":30817,"tags":[["d","nut-22"],["title","NUT-22: Blind Authentication"],["summary","Blind authentication tokens, which let a mint restrict access to registered users without learning which one is calling."],["s","optional"],["t","cashu"],["t","ecash"],["t","nut"],["alt","A specification: NUT-22: Blind Authentication"],["client","openspecs-import"],["published_at","1738212654"],["proxy","https://github.com/cashubtc/nuts/blob/a845dfc998abae501fc3419592d53dc995d34b12/22.md","web"],["x","e1ce5d15b7de9459be7e728b61dbe8bc150423d66dcec5e2e9d6c848239a5327"]],"content":"# NUT-22: Blind Authentication\n\n`optional`\n\n`depends on: NUT-21, NUT-12`\n\n---\n\nThis NUT defines a blind authentication scheme that allows mint operators to limit the use of their mint to a set of authorized users while still providing privacy within that anonymity set.\n\nWe use two authentication schemes in conjunction: _clear authentication_ using an external OpenID Connect / OAuth 2.0 service (described in [NUT-21][21]), and _blind authentication_ with the mint to access its resources. A user's wallet first needs to obtain a clear authentication token (CAT) from an OpenID Connect authority that the mint selected, which is not the subject of this specification. Once the user has obtained the CAT from the OpenID Connect service, they can use it to obtain multiple blind authentication tokens (BAT) from the mint. We describe this process in this document.\n\nBlind authentication tokens (BATs) are used to access the protected endpoints of the mint and make sure that only users that previously presented a valid CAT can access the mint's features such as minting, melting, or swapping ecash. Wallets provide a BAT in the request header when making a request to one of the mint's protected endpoints. The mint parses the header for a BAT, verifies the signature (like with normal ecash as described in [NUT-00][00]), checks if the token has previously been spent, and if not, adds it to its spent BAT token database.\n\n## Blind authentication tokens are ecash\n\nBlind authentication tokens (BATs) are essentially the same as normal ecash tokens and are minted in the same way. They are signed with a special keyset of the mint that has the unit `auth` and a single amount `1`.\n\nBATs can only be used a single time for each request that the wallet makes to the mint's protected endpoints. For each successful request, the BAT is added to the mint's spent token list after which they are regarded as spent. The BAT is not marked as spent if the request results in an error.\n\nTo summarize:\n\n- Wallet connects to mint and user is prompted to register or log in with an OAuth 2.0 service\n- Upon login, wallet receives a clear authentication (CAT) token that identifies the user\n- CAT is used to obtain blind authentication tokens (BAT) from the mint\n- BATs are used to access the mint\n\nThe diagram below illustrates the protocol flow.\n\n```\n\n┌──────────────────────────────────────────────────────────────────────────┐\n│ ┌────────┐                     ┌────────┐                  ┌───────────┐ │\n│ │  User  │                     │  Mint  │                  │   OpenID  │ │\n├─┴────────┘─────────────────────└────────┴──────────────────└───────────┴─┤\n│                                                                          │\n│                           1. Clear authentication                        │\n│                           =======================                        │\n│                                                                          │\n│                                       Mint registers OpenID service      │\n│                                     ◄───────────────────────────────     │\n│                                                                          │\n│           Wallet GET /v1/info                                            │\n│    ─────────────────────────────────►                                    │\n│                                                                          │\n│                                OpenID login                              │\n│    ────────────────────────────────────────────────────────────────►     │\n│                                                                          │\n│                              Respond with CAT                            │\n│    ◄────────────────────────────────────────────────────────────────     │\n│                                                                          │\n│                           2. Blind authentication                        │\n│                           =======================                        │\n│                                                                          │\n│           Request BAT using CAT                                          │\n│    ─────────────────────────────────►                                    │\n│                                                                          │\n│                Return BAT                                                │\n│    ◄─────────────────────────────────                                    │\n│                                                                          │\n│           Use BAT to access mint                                         │\n│    ─────────────────────────────────►                                    │\n│                                                                          │\n└──────────────────────────────────────────────────────────────────────────┘\n\n```\n\nThe steps for `1. Clear authentication` are described in [NUT-21][21], whereas the steps in `2. Blind authentication` are subject of this document.\n\n## Endpoints\n\nThe mint offers new endpoints that behave similarly to the endpoints for getting the keys, keysets, and minting tokens with normal ecash ([NUT-01][01], [NUT-02][02], [NUT-04][04]). These endpoints start with a prefix `/v1/auth/` to differentiate them from the normal endpoints of the mint. Using these endpoints, wallets can mint blind authentication tokens (BATs) and use them later when accessing the protected endpoints of the mint. Note that BATs cannot be swapped against other BATs.\n\n### Keys\n\nLike in [NUT-01][01] and [NUT-02][02], the mint responds with its BAT keyset for the following request:\n\n```\nGET /v1/auth/blind/keys\n```\n\nor\n\n```\nGET /v1/auth/blind/keys/{keyset_id}\n```\n\nwhere the mint returns a `GetKeysResponse`:\n\n```json\n{\n  \"keysets\": [\n    {\n      \"id\": \"000e479673849bf6\",\n      \"unit\": \"auth\",\n      \"keys\": {\n        \"1\": \"024ec000e31e230e4c59760def29601557c0b1650617dc8f38d3b2cfd21ad0351b\"\n      }\n    }\n  ]\n}\n```\n\nNotice that the unit is `auth` and only a single amount of `1` is supported.\n\n### Keysets\n\nLike in [NUT-02][02] the mint also offers the endpoints returning the keysets:\n\n```\nGET v1/auth/blind/keysets\n```\n\nThe mint returns the same `GetKeysetsResponse` response types as described in [NUT-02][02].\n\n## Minting blind authentication tokens\n\nTo mint blind authentication tokens (BATs), the wallet makes a request to the following endpoint:\n\n```http\nPOST /v1/auth/blind/mint\n```\n\nTo access this endpoint the wallet MUST provide a valid CAT (obtained via [NUT-21][21]) in its request header, IF this endpoint is marked as protected in the info response of the mint as per [NUT-21][21].\n\n```\nClear-auth: <CAT>\n```\n\nLike in [NUT-04][04], the wallet includes a `PostAuthBlindMintRequest` in the request body:\n\n```json\n{\n  \"outputs\": <Array[BlindedMessage]>\n}\n```\n\nwhere `outputs` are `BlindedMessages` (see [NUT-00][00]) from the blind auth keyset of the mint with a unit `amount`. The sum of all amounts of the outputs cannot exceed the maximum allowed amount of BATs as specified in `bat_max_mint` in the mint's `MintBlindAuthSetting`.\n\nNotice that in contrast to [NUT-04][04], we did not create a quote and did not include it in this request. Instead, we directly minted the maximum allowed amount of BATs.\n\nThe mint responds with a `PostAuthBlindMintResponse`:\n\n```json\n{\n  \"signatures\": <Array[BlindSignature]>\n}\n```\n\nThe wallet un-blinds the response to obtain the signatures `C` as described in [NUT-00][00]. It then stores the resulting `AuthProofs` in its database:\n\n```json\n{\n  \"id\": <hex_str>,\n  \"secret\": <str>,\n  \"C\": <hex_str>,\n  \"dleq\": {\n    \"e\": <str>,\n    \"s\": <str>,\n    \"r\": <str>\n  }\n}\n```\n\nTo prevent pinning, wallets MUST validate the DLEQ proofs `dleq` as defined in [NUT-12](nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnyf7fzky). Should `AuthProofs` be sent to another user of the mint, it MUST include the `dleq` proof so that the receiving user can validate it.\n\n## Using blind authentication tokens\n\nThe wallet checks the `MintBlindAuthSetting` of the mint to determine which endpoints require blind authentication. Similar to `NUT-21`, the wallet performs a match on the `protected_endpoints` in the `MintBlindAuthSetting` before attempting a request to one of the mint's endpoints. If the match is positive, the wallet needs to add a blind authentication token (BAT) to the request header.\n\n### Serialization\n\nTo add a blind authentication token (BAT) to the request header, we need to serialize a single `AuthProof` JSON as base64url with the prefix `authA`:\n\n```sh\nauthA[base64url_authproof_json]\n```\n\nThis string is a BAT.\n\nNote that `base64_url` strings may have padding characters (usually `=`) at the end, which can be omitted. Mints **MUST** accept and decode both padded and unpadded forms.\n\n> [!CAUTION]\n>\n> To protect the privacy of the wallet, the BAT MUST NOT contain the `dleq` proof when it is sent to the mint in the request header.\n\n### Request header\n\nWe add this serialized BAT to the request header:\n\n```\nBlind-auth: <BAT>\n```\n\nand make the request as we usually would.\n\n`AuthProofs` are single-use. The wallet MUST delete the `AuthProof` after a successful request, and SHOULD delete it even if request results in an error. If the wallet runs out of `AuthProofs`, it can [mint new ones](#minting-blind-authentication-tokens) using its clear authentication token (CAT).\n\n## Mint\n\n### DLEQs\n\nThe mint MUST return DLEQ proofs for every blind signature in `PostAuthBlindMintResponse` as defined in [NUT-12](nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnyf7fzky)\n\n### Signaling protected endpoints and settings\n\nThe mint lists each protected endpoint that requires a blind authentication token (BAT) in the `MintBlindAuthSetting` in its [NUT-06][06] info response:\n\n```json\n\"22\" : {\n  \"bat_max_mint\": 50,\n  \"protected_endpoints\": [\n    {\n      \"method\": \"GET\",\n      \"path\": \"/v1/mint/*\"\n    },\n    {\n      \"method\": \"POST\",\n      \"path\": \"/v1/mint/*\"\n    }\n  ]\n}\n```\n\n`bat_max_mint` is the number of blind authentication tokens (BATs) that can be minted in a single request using the `POST /v1/auth/blind/mint` endpoint.\n\n`protected_endpoints` contains the endpoints that are protected by blind authentication. `method` denotes the HTTP method of the endpoint, and `path` is either:\n\n1. **Exact match**: no trailing `*` → request path MUST equal `path`\n2. **Prefix match**: ends with `*` → request path MUST start with the prefix (`*` removed)\n\nThe `*` wildcard, if present, MUST be the final character only.\n\nFor example:\n\n- `/v1/*` matches any path starting `/v1/` (all endpoints)\n- `/v1/mint/*` matches any path that starts with `/v1/mint/` (all minting endpoints)\n- `/v1/mint/bolt*` matches any path starting `/v1/mint/bolt` (bolt11/12 minting endpoints)\n\n> [!CAUTION]\n> Wallets **MUST** treat mint provided `path` values as untrusted input and use exact or prefix matching only. Never use regex matching on untrusted input.\n\n## Error codes\n\nSee [Error Codes][errors]:\n\n- `31001`: Endpoint requires blind auth\n- `31002`: Blind authentication failed\n- `31003`: Maximum BAT mint amount exceeded\n- `31004`: BAT mint rate limit exceeded\n\n[00]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crqqpd6d9\n[01]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crznyve7g\n[02]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cry0t0uzl\n[03]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crxuwwl3j\n[04]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crg74fknc\n[05]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cr2dsg4q4\n[06]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crv3ltsuz\n[21]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595erz3x9w8s\n[errors]: https://github.com/cashubtc/nuts/blob/a845dfc998abae501fc3419592d53dc995d34b12/error_codes.md\n","sig":"fc876eacdd67c800fc32fe240e8c192e5364b56271cd6361550e06e3bea8d19639a1ad519d5eba47f4d58f1bbd05ce9ff360647c9e26ca210b5e8010c3bf7e7c"}