Skip to content

fix(mint/auth): make a fresh 0.21.0 auth mint work end-to-end#1004

Open
robwoodgate wants to merge 31 commits into
cashubtc:mainfrom
robwoodgate:fix/auth-keyset-migrations
Open

fix(mint/auth): make a fresh 0.21.0 auth mint work end-to-end#1004
robwoodgate wants to merge 31 commits into
cashubtc:mainfrom
robwoodgate:fix/auth-keyset-migrations

Conversation

@robwoodgate
Copy link
Copy Markdown
Contributor

@robwoodgate robwoodgate commented May 13, 2026

Stacked on #999

This PR is a fix for BLS implementation PR #999.

Summary

Five coupled fixes that surface together when a fresh Nutshell 0.21.0 auth mint runs against any OIDC provider (e.g. Keycloak 25+) and a v3 (BLS) keyset is generated on first start. Verified end-to-end against a local Keycloak + this branch + cashu-ts.

Each fix is small; bundled because they share one architectural root — AuthLedger inherits the mint CRUD (LedgerCrudSqlite) and the global mint_input_fee_ppk setting, while the auth migrations chain / response models / user-id contract / auth-side CRUD never kept up.

The five fixes

  1. Force input_fee_ppk=0 on auth keyset generation. Auth proofs are NUT-22 amount-1 bearer tokens — never swapped or melted. AuthLedger.verify_blind_auth already explicitly skips fee calculation. But Ledger.activate_keyset reads settings.mint_input_fee_ppk unconditionally, so any mint with a non-zero global fee bakes that value into the auth keyset id — semantically wrong, and breaks wallet-side id re-derivation. Matches CDK's behaviour (crates/cdk/src/mint/builder.rs forces fee=0 for the Auth unit). Implementation: LedgerKeysets gets a per-instance keyset_input_fee_ppk: Optional[int] = None defaulting to settings.mint_input_fee_ppk; AuthLedger overrides to 0. No behaviour change for non-auth ledgers.

  2. m003: add final_expiry column to auth keysets table. LedgerCrudSqlite.store_keyset INSERTs final_expiry (added on the mint side in m031 for keysets v2). Auth migrations stopped at m002, so v3 keyset generation crashes with no column named final_expiry. Mirrors mint m031.

  3. m004: align auth promises table with the mint-side schema. The mint side evolved promises to add mint_quote / swap_id (m023) and melt_quote / signed_at + drop the c_ NOT NULL constraint (m032-ish). LedgerCrudSqlite.store_promise INSERTs the full column set, so auth-side blind minting (first exercised by v3 BAT issuance at 0.21+) trips first no column named mint_quote then NOT NULL constraint failed: promises.c_. SQLite path rebuilds the table; Postgres path uses the ALTER chain.

  4. Tolerate missing sub claim in clear-auth tokens. _get_user hard-coded decoded_token[\"sub\"], raising KeyError when the IdP omits sub from access tokens. Keycloak 25+ does this by default for public clients (the oidc-subject-mapper declared in the test cashu-realm.json gets silently dropped on import). CDK's verify_cat doesn't read sub at all and works against the same realm. Fall back to preferred_username then azp so single-user-per-realm rate-limit tracking still works without changing happy-path semantics for IdPs that ship sub. Cross-IdP, not Keycloak-specific.

  5. AuthLedgerCrudSqlite.get_keyset: use MintKeyset.from_row instead of MintKeyset(**row). The mint-side equivalent (cashu/mint/crud.py:962) uses from_row correctly; the auth-side spreads the row dict directly into the constructor, which passes amounts through as the raw stringified-JSON SQLite stores (e.g. \"[1]\"). Iteration over self.amounts would then walk characters instead of list elements, producing junk key material. Dead code under the current wiring — cashu/mint/startup.py:94 constructs AuthLedger with crud=LedgerCrudSqlite() (whose get_keyset is the correct from_row version), and self.auth_crud = AuthLedgerCrudSqlite() is only used for user CRUD (get_user / create_user / update_user), never for get_keyset. Fixed anyway as a 1-line free fix that removes a landmine for the eventual switch-to-AuthLedgerCrudSqlite cleanup noted below.

Out of scope (worth a follow-up)

The underlying smell is AuthLedger using LedgerCrudSqlite instead of the (existing-but-unused) AuthLedgerCrudSqlite, whose leaner store_keyset / store_promise already match the auth m001 schema and would obviate (1)–(3). Switching CRUDs requires adding several missing methods to AuthLedgerCrud (store_blinded_message, update_keyset, bump_keyset_*, balance logs) — too wide for this PR.

Test plan

  • Fresh container, no prior auth DB: migrations chain through m001 → m004 cleanly, v3 auth keyset persists, /v1/auth/blind/keysets exposes the keyset.
  • Wallet OIDC password grant against Keycloak 25 succeeds (was previously rejected with ClearAuthFailedError due to missing sub).
  • Wallet mints 3 BATs, BLS pairing verifies them, consumes them across mint / swap / receive flows.
  • No effect on non-auth ledgers: keyset_input_fee_ppk defaults to None → falls through to settings.mint_input_fee_ppk.
  • CI on this PR.
  • Postgres-backed auth DB (m004 has separate ALTER path; not tested locally).

Refs #999.

a1denvalu3 and others added 30 commits May 5, 2026 11:12
… verification

- Fixed keyset v3 derivation logic and ID generation
- Replaced additive blinding logic with BLS multiplicative blinding
- Handled backwards compatibility for DLEQ skipping and verifying
- Implemented batch BLS pairing verification for unblinded signatures
- Removed redundant dummy DLEQ generation in BLS operations
- Fixed failing unit tests and resolved type-checking union issues
fix v3 Proof.Y hash-to-curve and BlindedSignature dleq=None
@github-project-automation github-project-automation Bot moved this to Backlog in nutshell May 13, 2026
@robwoodgate robwoodgate force-pushed the fix/auth-keyset-migrations branch from c6a730a to 442caa4 Compare May 13, 2026 21:10
@robwoodgate robwoodgate changed the title fix(mint/auth): add final_expiry migration so v3 BLS keysets persist fix(mint/auth): make a fresh 0.21.0 auth mint work end-to-end May 13, 2026
@robwoodgate robwoodgate force-pushed the fix/auth-keyset-migrations branch 2 times, most recently from 9c8d3cb to b388db9 Compare May 13, 2026 21:21
@robwoodgate robwoodgate force-pushed the fix/auth-keyset-migrations branch from b388db9 to 1b2f28a Compare May 13, 2026 21:26
@a1denvalu3 a1denvalu3 changed the base branch from feature/bls12-381-v3-keyset to main May 14, 2026 12:21
robwoodgate added a commit to cashubtc/cashu-ts that referenced this pull request May 21, 2026
Mirrors the existing CDK auth_mint demo against a locally-built
Nutshell 0.21.0+ image (the first version that emits v3 BLS keysets
by default for the auth ledger). Exercises the BLS BAT verification
path wired into AuthManager.

- docker-compose-nutshell.yml — Keycloak + Nutshell auth mint built
  from ${NUT_BLS_PATH:-../../../nutshell}. Same authnet /
  keycloak.localtest.me alias setup as the CDK compose; mirrors
  NUT_ENVS from the repo-root Makefile; mint command blocks on a
  working OIDC discovery URL before exec'ing `poetry run mint`
  (depends_on only waits for container start, not for Keycloak to
  finish serving the realm).
- examples/auth_mint/Makefile: nutshell-up / nutshell-down /
  nutshell-demo / nutshell-demo-device / nutshell-demo-pkce /
  nutshell-demo-ci / nutshell-logs / nutshell-clean. Existing CDK
  targets untouched.
- cashu-realm.json: "sslRequired": "none". Keycloak 25's realm
  default is "external", which rejects plain HTTP from non-loopback
  source IPs; the wallet running on the host loops in via docker
  port mapping and gets rejected. Dev-only setting; also fixes the
  CDK demo on Keycloak 25 if anyone has noticed it broken there.

Demo end-to-end requires the Nutshell-side fixes in
cashubtc/nutshell#1004; once that lands on feature/bls12-381-v3-keyset
the local build picks them up automatically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants