Auth Overview
Built-in auth middleware provides bridge patterns for WordPress REST routes.
Available middleware
BetterRoute\Middleware\Jwt\JwtAuthMiddlewareBetterRoute\Middleware\Jwt\Rs256JwksJwtVerifier(v0.6.0) — RS256/ES256 verifier backed by JWKS; see JWKS (RS256 / ES256)BetterRoute\Middleware\Jwt\HttpJwksProvider/StaticJwksProvider(v0.6.0)BetterRoute\Middleware\Auth\BearerTokenAuthMiddlewareBetterRoute\Middleware\Auth\HmacSignatureMiddleware(v0.6.0) — HMAC request signatures with replay-window enforcement; see HMAC Request SignaturesBetterRoute\Middleware\Auth\CookieNonceAuthMiddlewareBetterRoute\Middleware\Auth\ApplicationPasswordAuthMiddlewareBetterRoute\Middleware\Auth\WpClaimsUserMapperBetterRoute\Middleware\Auth\OwnershipGuardMiddleware(v0.5.0) — see Ownership guards
Context propagation
On success, middleware writes auth context attributes:
auth(provider, userId, subject, scopes)- derived
claims,userId,user,scopes; since 1.1.1 these are replaced even when null/empty, so stale outer identity fields do not survive hmac(v0.6.0) —keyIdandalgorithmafter successful HMAC signature verification. (v1.1.0) HMAC verification now also writes the sharedauthattribute (provider: 'hmac',subject: <keyId>), so identity-scoped middleware sees HMAC callers like any other authenticated identity.singleUseToken(v0.6.0) — issuer-supplied context after a single-use token is consumed
(v1.1.0) Identity-scoped middleware (caching, idempotency, rate limiting) resolves the caller through BetterRoute\Support\RequestIdentity: the auth attribute first, then a userId attribute, then the native WordPress user (get_current_user_id()), then the HMAC key id. A logged-in WP user is therefore scoped correctly even when no auth middleware is attached to the route.
Important policy note
Even with auth middleware, route registration still requires explicit permission_callback (by design in dispatcher integration). For middleware-authenticated routes, declare the intent with ->protectedByMiddleware(...) (since 0.4.0). For routes guarded by HMAC or single-use tokens (no WordPress user behind them), use ->publicRoute() and let the middleware be the gate.
(v1.1.0) This now applies to every HTTP method: raw Router routes — GET and OPTIONS included — deny by default until you declare intent with ->permission(), ->protectedByMiddleware(), or ->publicRoute(). See Router.
v1.1.0 hardening
- All raw routes deny by default.
GET/OPTIONSare no longer public without explicit intent (see the policy note above). - JWT lifetime cap requires
iat+exp. WithmaxLifetimeSecondsconfigured, tokens missing either claim are rejected instead of skipping the check. See JWT and Bearer. - JWKS refresh throttling. Unknown-
kidrefreshes respect per-verifier and persisted provider cooldowns, run under a MySQL advisory lock, and keep the last known-good keys when a fetch fails. See JWKS (RS256 / ES256). - Shared identity resolution.
RequestIdentityscopes cache/idempotency/rate-limit keys by auth identity, native WP user, or HMAC key id (see Context propagation above).
v1.0.0 hardening
WpClaimsUserMapperemail/login mapping is off by default. Only id claims and an explicit custom resolver map a WP user out of the box; email mapping is opt-in and requires a truthyemail_verifiedclaim. Prevents account takeover from unverified third-party-issuer email claims. See JWT and Bearer.- Granted-scope wildcards are opt-in. A trailing
*on a token-supplied scope is treated as a literal unlessallowGrantedScopeWildcards: true; server-defined required-scope wildcards are unchanged. - Optional HMAC query-string signing via
signQueryString(off by default). See HMAC Request Signatures. HttpJwksProvideruseswp_safe_remote_getwith bounded redirects and response size.
v0.6.0 additions
- Asymmetric JWT.
Rs256JwksJwtVerifierplusHttpJwksProviderandStaticJwksProviderfor OIDC-style providers. Strictkidmatching, algorithm pinning, private-field stripping. See JWKS (RS256 / ES256). - HMAC request signatures.
HmacSignatureMiddlewareplusHmacSecretProviderInterfaceandArrayHmacSecretProvider. Canonical input, replay window, multi-key rotation. See HMAC Request Signatures. - Single-use tokens.
SingleUseTokenMiddlewareplus three stores. See Single-Use Tokens.
v0.5.0 additions
OwnershipGuardMiddlewarefor routes where the authenticated user may only access their own object. DefaultdeniedStatusis404to avoid leaking existence.Resource\OwnedResourcePolicy::currentUserOwns()— Resource DSL preset that wires the URLid, the auth identity, and an optionalbypassCapabilitytogether. See Ownership guards.
v0.3.0 hardening
Hs256JwtVerifierrequiresexpby default and supportsexpectedIssuer,expectedAudience,maxLifetimeSeconds, andmaxTokenLength. See JWT and Bearer.WpClaimsUserMapperremoved'sub'from defaultidClaims— re-add it explicitly when needed.BearerTokenAuthMiddlewareno longer leaks the verifier exception message; failed tokens uniformly return401 invalid_tokenwith nodetails.reason.
Common mistakes
- Relying on middleware alone without route permissions
- Missing
Authorizationheader normalization - Not mapping JWT/Bearer claims to WP user where needed
Validation checklist
401on missing/invalid credential403on missing required scopes- expected auth attributes exist in
RequestContext
Native user scope (1.1.1)
JWT, Bearer and Application Password middleware bind the mapped WordPress user only while the downstream pipeline executes, then restore the previous user in finally. Nested calls and exceptions unwind correctly. A verified JWT/Bearer identity without a positive WP mapping runs downstream as native user 0.
If a custom setCurrentUser callback targets another identity store, supply the appended optional getCurrentUser callback for that same store. By default the getter uses get_current_user_id() (or 0 outside WordPress); earlier positional constructor arguments are unchanged.
WordPress permission callbacks run before middleware; protectedByMiddleware() does not itself authenticate. Later rest_request_after_callbacks, rest_post_dispatch and _embed processing see the restored caller. Use native WordPress request authentication when those phases need an authenticated WP user, or perform protected work inside the pipeline. See migration.