From 4214583ae590d6f1fcffac8ef6486f1e5a40e783 Mon Sep 17 00:00:00 2001 From: GareArc Date: Sun, 26 Apr 2026 23:26:13 -0700 Subject: [PATCH] refactor(api): hoist bearer_feature_required to libs/oauth_bearer (Phase A.3) The decorator was defined inline in console/auth/oauth_device.py. Phase D will move approve/deny to controllers/openapi/oauth_device/ and the new SSO branch under the same group needs the same gate. Hoist it to libs/oauth_bearer.py now so the move stays a pure file rename later. Behavior unchanged: 503 'bearer_auth_disabled' when ENABLE_OAUTH_BEARER is off. console/auth/oauth_device.py imports it from libs and drops the now-unused dify_config / wraps / ServiceUnavailable imports. Plan: docs/superpowers/plans/2026-04-26-openapi-migration.md (in difyctl repo). --- api/controllers/console/auth/oauth_device.py | 22 +------------------- api/libs/oauth_bearer.py | 17 +++++++++++++++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/api/controllers/console/auth/oauth_device.py b/api/controllers/console/auth/oauth_device.py index 80ef8c8d5b..46f32e2df9 100644 --- a/api/controllers/console/auth/oauth_device.py +++ b/api/controllers/console/auth/oauth_device.py @@ -5,36 +5,16 @@ from __future__ import annotations import logging -from functools import wraps - from flask_login import login_required from flask_restx import Resource, reqparse -from werkzeug.exceptions import ServiceUnavailable -from configs import dify_config from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, setup_required from extensions.ext_database import db from extensions.ext_redis import redis_client from libs.login import current_account_with_tenant -from libs.oauth_bearer import SubjectType +from libs.oauth_bearer import SubjectType, bearer_feature_required from libs.rate_limit import LIMIT_APPROVE_CONSOLE, rate_limit - - -def bearer_feature_required(fn): - """503 if ENABLE_OAUTH_BEARER is off — minted tokens would be unusable - without the authenticator, so fail fast instead of approving silently. - """ - - @wraps(fn) - def inner(*args, **kwargs): - if not dify_config.ENABLE_OAUTH_BEARER: - raise ServiceUnavailable( - "bearer_auth_disabled: set ENABLE_OAUTH_BEARER=true to enable" - ) - return fn(*args, **kwargs) - - return inner from services.oauth_device_flow import ( ACCOUNT_ISSUER_SENTINEL, PREFIX_OAUTH_ACCOUNT, diff --git a/api/libs/oauth_bearer.py b/api/libs/oauth_bearer.py index 6eb7aedab0..01f320d90b 100644 --- a/api/libs/oauth_bearer.py +++ b/api/libs/oauth_bearer.py @@ -21,6 +21,7 @@ from sqlalchemy import update from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden, ServiceUnavailable, Unauthorized +from configs import dify_config from models import OAuthAccessToken logger = logging.getLogger(__name__) @@ -387,6 +388,22 @@ def validate_bearer(*, accept: frozenset[Accepts]) -> Callable: return wrap +def bearer_feature_required(fn: Callable) -> Callable: + """503 if ENABLE_OAUTH_BEARER is off — minted tokens would be unusable + without the authenticator, so fail fast instead of approving silently. + """ + + @wraps(fn) + def inner(*args, **kwargs): + if not dify_config.ENABLE_OAUTH_BEARER: + raise ServiceUnavailable( + "bearer_auth_disabled: set ENABLE_OAUTH_BEARER=true to enable" + ) + return fn(*args, **kwargs) + + return inner + + # ============================================================================ # Wiring — called once from the app factory # ============================================================================