fix(api,web): post-review hardening for OAuth device flow

- api: account-flow stores subject_issuer="dify:account" sentinel
  instead of NULL so the rotate-in-place unique index collides as
  intended (Postgres treats NULLs as distinct in unique indices).
  mint_oauth_token validates prefix-specific issuer rules.
- api: enterprise_only inverts to an allowlist (ACTIVE / EXPIRING) so
  any future LicenseStatus value defaults to denial.
- api: consume_on_poll moved to a single Lua script (GET + status-check
  + DEL) so concurrent pollers can't both observe APPROVED.
- web: typed DeviceFlowError + central error-copy mapping; page
  surfaces rate_limited / lookup_failed view states; URL params
  scrubbed after consumption (RFC 8628 §5.4).
This commit is contained in:
GareArc
2026-04-26 23:05:07 -07:00
parent fe8510ad1a
commit 813da349ec
10 changed files with 208 additions and 58 deletions

View File

@ -36,6 +36,7 @@ def bearer_feature_required(fn):
return inner
from services.oauth_device_flow import (
ACCOUNT_ISSUER_SENTINEL,
PREFIX_OAUTH_ACCOUNT,
DeviceFlowRedis,
DeviceFlowStatus,
@ -90,7 +91,7 @@ class DeviceApproveApi(Resource):
db.session,
redis_client,
subject_email=account.email,
subject_issuer=None,
subject_issuer=ACCOUNT_ISSUER_SENTINEL,
account_id=str(account.id),
client_id=state.client_id,
device_label=state.device_label,
@ -104,7 +105,7 @@ class DeviceApproveApi(Resource):
device_code,
subject_email=account.email,
account_id=str(account.id),
subject_issuer=None,
subject_issuer=ACCOUNT_ISSUER_SENTINEL,
minted_token=mint.token,
token_id=str(mint.token_id),
poll_payload=poll_payload,