feat(19.7-01): in-process dispatch marker and compiled handler lookup
- bouncer.Dispatch, WithDispatch, Dispatched: context-only pre-resolved
principal marker honoured by Registry.Middleware for listed guard names
- cabana.ResolveBackend honours a backend-kind marker; additive
ResolveBackendToken, ScopeLimitFor and BackendPrincipal resolvers
- surf.Router.Handler and compileHandlers: one compiled chain per route,
shared by BuildRouter validation and compile
- smoke tests TestDispatchSmoke, TestDispatchedSmoke, TestDispatchMarkerSmoke
- READMEs and docs/services/{authentication,routing}.md updated
This commit is contained in:
@@ -29,6 +29,7 @@ Use cases:
|
||||
- Named guard registry: `bouncer.Registry.Register` accepts any `bouncer.Guard` or `bouncer.CredentialGuard`; `bouncer.Registry.Middleware` derives middleware that stores the principal (and credential, if any) on the context. Guards that do not implement `bouncer.UnauthorizedWriter` let unauthenticated requests through so later middleware can decide.
|
||||
- Standalone bearer middleware: `bouncer.Middleware`.
|
||||
- Context helpers: `bouncer.WithUser` and `bouncer.User` for the principal, `bouncer.WithCredential` and `bouncer.Credential` for the credential behind it (for example an API token record).
|
||||
- In-process dispatch marker: `bouncer.Dispatch` carries a pre-resolved principal (and credential) for a request a framework dispatcher builds itself, such as the MCP module calling a plugin route. `bouncer.WithDispatch` stores it under an unexported context key; nothing reads a header, cookie or query string for it, so an HTTP request can never carry one. `bouncer.Registry.Middleware` honours a marker only for the exact guard names it lists (`bouncer.Dispatched`) and then skips the guard; every other request authenticates as before.
|
||||
- Scope limits for personal API tokens: `bouncer.ScopeLimit` on `bouncer.Principal.Scope` (nil means unrestricted, as for a JWT session). A token guard sets it after filtering the token's scopes by the request method; `Full` means the token acts as the owner on every method (`*`) or on GET/HEAD (`admin:read`).
|
||||
- Blacklist stores behind `bouncer.BlacklistStore`: `bouncer.MemoryBlacklist` for tests and `bouncer.PostgresBlacklist` for production, which works on a caller-supplied table with `jti`, `expires_at` and `valid_until` columns and rejects unsafe table names.
|
||||
- Cookie transport: `bouncer.LoadCookieConfig` reads the `auth.cookie.*` block into a `bouncer.CookieConfig` and refuses an invalid domain, `same_site: none` without Secure and, in the production environment, a Secure that resolves to false (set to `false`, or inferred from an http or unset `app.url`); `bouncer.CookieConfig.TokenCookie` builds the httpOnly token cookie whose Max-Age is the refresh window, and `bouncer.CookieConfig.ForgetTokenCookie` the matching cookie that deletes it. A hand-built config with `SameSite` unset writes `SameSite=Lax`, and the zero `bouncer.CookieConfig` writes `token` on `/` with `HttpOnly`, `SameSite=Lax` and `Secure`. `bouncer.CookieRefreshHandler` and `bouncer.CookieLogoutHandler` are ready-made handlers for a cookie-based refresh and logout; the application supplies the refresh or revoke call and a writer for its own response envelope.
|
||||
@@ -153,6 +154,9 @@ func RefreshRoute(cfg bouncer.CookieConfigSource, users bouncer.UserProvider, se
|
||||
| `bouncer.AudienceUser`, `bouncer.AudienceBackend` | The frontend and admin audience values. |
|
||||
| `bouncer.WithUser`, `bouncer.User` | Store and read the principal on a context. |
|
||||
| `bouncer.WithCredential`, `bouncer.Credential` | Store and read the resolved credential on a context. |
|
||||
| `bouncer.Dispatch` | The in-process dispatch marker: the guard names that honour it, the pre-resolved principal and an optional credential. Context-only; never read from a request. |
|
||||
| `bouncer.WithDispatch` | Stores a `bouncer.Dispatch` on a context. Only an in-process dispatcher inside the framework calls it. |
|
||||
| `bouncer.Dispatched` | Reads the marker when it carries a principal and lists the given guard name exactly; false otherwise. |
|
||||
| `bouncer.BlacklistStore` | Revoked-jti store with a grace window and sweeping. |
|
||||
| `bouncer.NewMemoryBlacklist` | In-process blacklist for tests. |
|
||||
| `bouncer.NewPostgresBlacklist` | Blacklist over a `*sql.DB` and a table name. |
|
||||
|
||||
Reference in New Issue
Block a user