feat(16.3-01): admin:token CLI, party.Activated, READMEs and docs

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-11 01:55:07 +02:00
parent ffdb5d00ca
commit e24e03c47d
22 changed files with 627 additions and 54 deletions

View File

@@ -62,6 +62,8 @@ Logging out is blacklisting the token's `jti`. Setting a user's `TokensValidAfte
A guard implements `bouncer.Guard`: it turns a request into a principal or an error. `bouncer.NewJWTGuard` is the frontend guard. It reads the bearer token, then any cookie names you give it, verifies the token, checks the blacklist and the user's cutoff, and loads the user through your `bouncer.UserProvider`. On failure it answers 401 with `{"error":true,"message":...}`. `bouncer.NewBackendJWTGuard` is the same for the admin audience.
The admin API's `backend` guard is composite: an `Authorization: Bearer` value that starts with `g15_` is looked up as a personal API token (see [API tokens](../backend/users-and-permissions.md#api-tokens)); every other request uses the unchanged JWT path (Bearer JWT, then the admin cookie). A `g15_` value is never read from a cookie or the query string.
Register guards in a `bouncer.Registry` under a name, and turn one into middleware with `bouncer.Registry.Middleware`. The middleware stores the principal on the context, where handlers read it with `bouncer.User`:
```go src=modules/bouncer/example_test.go#ExampleNewJWTGuard
@@ -103,6 +105,10 @@ To protect routes, return that middleware from `pact.HasMiddleware` under a name
A guard that resolves more than a user, such as an API token record, implements `bouncer.CredentialGuard`; the middleware stores that record too, and handlers read it with `bouncer.Credential`.
### Accepting admin tokens on plugin routes
Plugin routes that should accept an admin JWT or a personal API token call `cabana.ResolveBackend`. It reads only the Authorization Bearer header (never the admin cookie), applies the token's method filter, and for a scoped token requires a method-allowed scope that is full or matches every cover code you pass. On success it returns the request with `bouncer.WithUser` (and `bouncer.WithCredential` for a token). Failures are `cabana.ErrUnauthenticated` or `cabana.ErrTokenScope`. Routes registered before `cabana.Activate` must call it per request, because the resolver is published at activation.
## Passwords
`bouncer.HashPassword` hashes with bcrypt at the cost you give it, `bouncer.CheckPassword` compares in constant time, and `bouncer.NeedsRehash` reports a hash made below your configured cost. WinterCMS stores bcrypt hashes too, so existing passwords keep working: