Log Secrets Manager failures in web UI auth token fetch

The web UI auth gate correctly fails closed when the shared token
cannot be read, but _get_auth_token() swallowed every exception
silently. A Secrets Manager permission or config error then made
every request 401 with no operational signal, leaving an outage
indistinguishable from ordinary unauthenticated traffic.

Add a module-level logger to both web_ui handlers and log the
fetch failure with logger.exception() in the except block before
returning None. Behavior is unchanged (still fails closed); the
failure is now visible in CloudWatch. The secret value is never
logged. The two handlers stay byte-consistent in the mirrored
_get_auth_token() region.

The companion finding on the CDK import of the shared
procurement-ingest/web-ui-auth-token secret was evaluated and left
as-is: the token is a single secret shared by both the PO and WO
stacks, so from_secret_name_v2 (which scopes grant_read via the
standard 6-char suffix wildcard) is correct; making it a CDK-managed
Secret in both stacks would collide the two stacks on the same
explicit secret name at deploy time.

Refs: INFRA-74
This commit is contained in:
Adam Moussa 2026-07-15 19:43:36 -04:00
parent 2802d04368
commit 2c2cfcc538
No known key found for this signature in database
2 changed files with 24 additions and 0 deletions

View file

@ -7,6 +7,7 @@ Accessed via Lambda Function URL.
import hmac import hmac
import json import json
import logging
import os import os
import time import time
from decimal import Decimal from decimal import Decimal
@ -14,6 +15,9 @@ from html import escape as esc
import boto3 import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
dynamodb = boto3.resource("dynamodb") dynamodb = boto3.resource("dynamodb")
PO_TABLE = os.environ.get("PO_TABLE", "purchase-orders") PO_TABLE = os.environ.get("PO_TABLE", "purchase-orders")
@ -58,6 +62,14 @@ def _get_auth_token() -> str | None:
_auth_token_cached_at = now _auth_token_cached_at = now
return _auth_token_cache return _auth_token_cache
except Exception: except Exception:
# Fail closed (return None -> caller 401s) but surface the failure: a
# Secrets Manager permission/config error would otherwise make every
# request 401 with no operational signal. The secret value is never
# logged.
logger.exception(
"Failed to fetch web UI auth token from Secrets Manager; "
"denying request (failing closed)"
)
return None return None

View file

@ -7,12 +7,16 @@ Accessed via Lambda Function URL.
import hmac import hmac
import json import json
import logging
import os import os
import time import time
from html import escape as esc from html import escape as esc
import boto3 import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
dynamodb = boto3.resource("dynamodb") dynamodb = boto3.resource("dynamodb")
WORK_ORDERS_TABLE = os.environ.get("WORK_ORDERS_TABLE", "WorkOrders") WORK_ORDERS_TABLE = os.environ.get("WORK_ORDERS_TABLE", "WorkOrders")
@ -57,6 +61,14 @@ def _get_auth_token() -> str | None:
_auth_token_cached_at = now _auth_token_cached_at = now
return _auth_token_cache return _auth_token_cache
except Exception: except Exception:
# Fail closed (return None -> caller 401s) but surface the failure: a
# Secrets Manager permission/config error would otherwise make every
# request 401 with no operational signal. The secret value is never
# logged.
logger.exception(
"Failed to fetch web UI auth token from Secrets Manager; "
"denying request (failing closed)"
)
return None return None