feat(middleware): add fail-closed AUTH_REQUIRED option - #127
Merged
Conversation
Adds an opt-in AUTH_REQUIRED switch (AuthClient.Required, read from the AUTH_REQUIRED env var at construction) that inverts the default fail-open posture. When set and auth is disabled or misconfigured (!Enabled || Address == ""), every protected route refuses to serve — HTTP 503 on the Fiber path and gRPC codes.Unavailable on the unary and stream interceptors — instead of silently passing the request through unauthenticated. A loud error is also logged at construction so the misconfiguration is visible. Default (false) preserves the prior pass-through behavior exactly, so this is backward-compatible. Enforcement lives at the handler/interceptor rather than as a constructor error, to avoid a breaking change to NewAuthClient's existing (non-erroring) signature. The tenant-claim propagation duplicated across the two gRPC interceptors is extracted into a shared tenantContext helper, keeping cognitive complexity under the linter threshold after the added fail-closed branch. Closes #107 Claude-Session: https://claude.ai/code/session_01RcQLK4qK1kbpvXWjuAR3Xh
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughChangesRequired authentication enforcement
Sequence Diagram(s)sequenceDiagram
participant Client
participant AuthMiddleware
participant AuthService
participant ProtectedHandler
Client->>AuthMiddleware: protected request or RPC
AuthMiddleware->>AuthService: authorize when available
alt required auth unavailable
AuthMiddleware-->>Client: HTTP 503 or gRPC Unavailable
else authorized
AuthMiddleware->>ProtectedHandler: forward request with auth context
ProtectedHandler-->>Client: response
end
Possibly related PRs
✨ Finishing Touches✨ Simplify code
Comment |
|
🎉 This PR is included in version 3.0.0-beta.6 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
2 tasks
lerian-studio-midaz-push-bot Bot
pushed a commit
that referenced
this pull request
Jul 22, 2026
## [3.0.0](v2.9.0...v3.0.0) (2026-07-22) ### ⚠ BREAKING CHANGES * **middleware:** module path is now github.com/LerianStudio/lib-auth/v3 and the Authorize middleware requires Fiber v3. X-Lerian-Ref: 0x1 ### Features * **middleware:** add authz cache, circuit breaker, and bounded retry ([c0469e6](c0469e6)), closes [#107](#107) [#127](#127) [#127](#127) [#108](#108) * **middleware:** add fail-closed AUTH_REQUIRED option ([f7fff6c](f7fff6c)), closes [#107](#107) * **middleware:** add M2M authentication gate ([2034198](2034198)) * **middleware:** add opt-in local JWT signature verification ([#128](#128)) ([48b81a3](48b81a3)), closes [#106](#106) * **middleware:** add optional issuer pinning to M2M gate ([5afd305](5afd305)) * **middleware:** expose M2M identity via request context ([c48393a](c48393a)), closes [#125](#125) * **middleware:** forward product on M2M auth (flag-gated) ([93c44db](93c44db)) * **middleware:** migrate to Fiber v3, cut /v3 major ([024a909](024a909)) * real M2M subject and token type whitelist ([e31ac53](e31ac53)) ### Bug Fixes * **middleware:** return zero M2MIdentity when reporting absent ([25fc4ec](25fc4ec)), closes [#126](#126)
|
🎉 This PR is included in version 3.0.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an opt-in fail-closed switch to the auth middleware so a
disabled/misconfigured client refuses to serve instead of silently passing every
request through unauthenticated.
AuthClient.Required boolfield, read once from theAUTH_REQUIREDenv varat construction (mirrors the existing
AUTH_M2M_PRODUCT_FORWARD_ENABLEDwiring).Required == trueand auth cannot authorize (!Enabled || Address == ""),every protected route refuses to serve:
Authorize→ HTTP 503 Service Unavailablecodes.UnavailableAUTH_REQUIREDis set but auth isdisabled/blank, so the misconfiguration is visible immediately rather than at
first request.
Required == falsepreserves the prior pass-through behavior exactly.Why
Per #107, the middleware fails open:
if !auth.Enabled || auth.Address == ""returns
c.Next()(and the gRPC equivalents pass through). One missing/typo'd envvar silently downgrades a protected service to fully open, with no error, no boot
failure. Downstream (midaz) compensates with N hand-rolled per-service boot gates
that drift independently; the fail-closed posture belongs upstream in one place.
Design decision: handler-level refusal, not a constructor error
The issue suggested "an error from the constructor / a hard 503 from the handler."
I chose the 503 /
codes.Unavailableat the handler, deliberately:NewAuthClientcurrently returns*AuthClientwith no error. Retrofitting a(*AuthClient, error)signature is a breaking API change to every call site(README examples and consumers call it positionally) — which directly contradicts
the "opt-in, backward-compatible" requirement.
NewM2MAuthenticatorreturning anerror is not a precedent to match here; it was born with that signature.
Required=trueand amisconfig, no route ever serves unauthenticated — it returns 503 on every request
(loud, not silent). The added boot-time error log gives operators the deploy-time
signal without a signature break.
Scope note
NewGRPCAuthUnaryPolicywas already at the repo's cognitive-complexity ceiling ondevelop. To add the fail-closed branch without tripping the linter or resorting toa
//nolint, the tenant-claim propagation block that was duplicated verbatim inboth gRPC interceptors is extracted into a shared
tenantContexthelper. Existingtenant-propagation tests cover the extraction (no behavior change).
Tests
Added, all passing (full package: 124 tests green;
golangci-lint run ./...clean):TestNewAuthClient_ReadsRequiredFlag—AUTH_REQUIREDenv →Requiredfield(true / absent / non-
true).TestAuthorize_FailClosed(Fiber, viaapp.Test): required+disabled → 503;required+blank-address → 503; not-required+disabled → passes through (unchanged);
required+enabled → normal authorize flow.
TestNewGRPCAuthUnaryPolicy/TestNewGRPCAuthStreamPolicy: required+disabled andrequired+blank-address →
codes.Unavailable, handler not invoked.Closes #107
https://claude.ai/code/session_01RcQLK4qK1kbpvXWjuAR3Xh