-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Authentication, role and scope authorization, server-side sessions and CSRF protection for Rex.
go get github.com/kryovyx/rextension-securityimport (
"github.com/kryovyx/rex"
security "github.com/kryovyx/rextension-security"
)
app := rex.New(security.WithSecurity(security.NewConfig(
security.WithScheme(security.NewBearerScheme("jwt",
security.TokenValidatorFunc(validateJWT))),
security.WithCSRFPolicy(origins),
)))Routes declare what must authenticate them:
type AdminRoute struct{ rxroute.Route }
func (r *AdminRoute) RequiredSchemes() []string { return []string{"jwt"} }
func (r *AdminRoute) RequiredRoles() map[string][]string {
return map[string][]string{"jwt": {"admin"}}
}A route that declares nothing is public.
| Schemes | Bearer, Basic, API key and session-cookie authentication, plus your own |
| Securing Routes | declaring requirements on a route, and reading the principal in a handler |
| Roles and Scopes | per-scheme role and OAuth2 scope enforcement |
| Sessions | server-side sessions with idle and absolute timeouts, rotation on login |
| CSRF | on by default for cookie-authenticated routes; origin check plus double-submit token |
| Startup Validation | a misconfiguration fails the boot, not the request |
Three decisions follow from it, and each one fixed a real fail-open:
A requirement that cannot be enforced is a startup failure. A route
declaring RequiredRoles: {"jwt": {"admin"}} against a scheme whose validator
knows nothing about roles used to be served to every authenticated caller,
with no diagnostic anywhere — an endpoint marked "admin only" that was not. Now
the application refuses to start. See Startup Validation.
CSRF protection is on by default. An opt-in security control is off in every application whose author did not think about it, and those are exactly the applications that need it. It applies only where it can matter — an unsafe method on a route authenticated by a cookie-based scheme. A Bearer-only API is unaffected. See CSRF.
A scheme with no validator panics at construction. It cannot authenticate anything, so every request to a route requiring it fails — at request time, with a nil dereference or a permanent 401. Panicking at the line that caused it turns that into a startup failure where the mistake is.
Cookies are Secure unless you say otherwise. CookieOptions has an
AllowInsecureTransport field rather than a Secure one, so the unsafe choice
has to be named. It replaced a Secure bool that defaulted to false — making
the unsafe option the one you got by not thinking about it.
A 401 or 403 is a problem document with a generic detail:
{"type":"urn:rex:problem:unauthorized","title":"Unauthorized","status":401,
"detail":"credentials were not accepted"}Never the reason. "token expired at 14:02 for subject u_1934" tells an
attacker which half of a guess was right, and a validator's error text routinely
names another account, an internal host, or the shape of the authorization
model. The specifics go to the logger configured in MiddlewareConfig.Logger.
The extension registers a SchemeRegistry in the DI container. The OpenAPI
generator resolves it through the rextension.SchemeRegistry interface and
documents the schemes; Swagger renders an authorization panel from the same
data. Neither module imports this one.
That registry used to be a package-level global that replaced rather than appended and had no unregister — so two Rex instances in one process clobbered each other's schemes, and state leaked between tests in the same binary.
rextension-security — authentication, authorization and CSRF for Rex · MIT · © 2026 Kryovyx