|
12 | 12 |
|
13 | 13 | - ``GATEWAY_AUTH_ISSUER`` - Auth0 issuer URL (must end with ``/``) |
14 | 14 | - ``GATEWAY_AUTH_AUDIENCE`` - Auth0 API identifier the gateway accepts |
15 | | -- ``GATEWAY_AUTH_REQUIRED`` - if truthy, missing issuer/audience is a 503 |
| 15 | +- ``GATEWAY_AUTH_REQUIRED`` - if truthy, bearer JWT auth is enforced |
16 | 16 |
|
17 | 17 | For local development and unit tests the dependency can be bypassed by |
18 | 18 | setting ``GATEWAY_AUTH_DISABLED=1``. This bypass is hard-gated by |
|
21 | 21 | missing or looks like production, and otherwise requires an explicit |
22 | 22 | ``GATEWAY_AUTH_DISABLED_ACK=I_UNDERSTAND_THIS_IS_DEV`` acknowledgement so |
23 | 23 | the bypass cannot be activated by a single stray env var. The gateway |
24 | | -also returns ``503`` to callers if auth is required but the issuer/audience |
25 | | -configuration is missing, or if only one of issuer/audience is present. |
| 24 | +also returns ``503`` to callers if only one of issuer/audience is present, or |
| 25 | +if auth is required but issuer/audience are missing. |
26 | 26 | """ |
27 | 27 |
|
28 | 28 | from __future__ import annotations |
@@ -193,18 +193,29 @@ def require_auth( |
193 | 193 | missing or invalid token produces a 403 (matching the underlying |
194 | 194 | decoder's contract). |
195 | 195 |
|
196 | | - If issuer/audience env configuration is absent, the dependency preserves |
197 | | - the legacy public gateway behavior unless ``GATEWAY_AUTH_REQUIRED`` is |
198 | | - truthy. Partial auth configuration always returns 503 because it indicates |
199 | | - an operator intended to enable auth but shipped an incomplete secret. |
| 196 | + The gateway preserves its legacy public behavior unless |
| 197 | + ``GATEWAY_AUTH_REQUIRED`` is truthy. Issuer/audience may be staged in the |
| 198 | + Modal secret ahead of enforcement; setting those values alone must not |
| 199 | + silently make the gateway private. Partial auth configuration always |
| 200 | + returns 503 because it indicates an incomplete secret. |
200 | 201 | """ |
201 | 202 |
|
202 | 203 | if _auth_disabled(): |
203 | 204 | return None |
204 | 205 |
|
205 | 206 | issuer = os.environ.get(GATEWAY_AUTH_ISSUER_ENV) |
206 | 207 | audience = os.environ.get(GATEWAY_AUTH_AUDIENCE_ENV) |
207 | | - if not issuer and not audience and not _auth_required(): |
| 208 | + if bool(issuer) != bool(audience): |
| 209 | + logger.error( |
| 210 | + "Gateway auth partially configured: issuer_present=%s audience_present=%s", |
| 211 | + bool(issuer), |
| 212 | + bool(audience), |
| 213 | + ) |
| 214 | + raise HTTPException( |
| 215 | + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, |
| 216 | + detail="Gateway authentication is not configured.", |
| 217 | + ) |
| 218 | + if not _auth_required(): |
208 | 219 | return None |
209 | 220 |
|
210 | 221 | try: |
|
0 commit comments