Flask restx Debug «Несанкционированный» ответ

У меня есть приложение Flask (2.2.3) с Flask-RESTX (1.1.0), используемое в качестве API (без внешнего интерфейса). Я использую библиотеку flask-azure-oauth для аутентификации пользователей с помощью Azure AD. Настройка:

      from flask import Flask, current_app
from flask_azure_oauth import FlaskAzureOauth
from flask_restx import Api

app = Flask(__name__)
api = Api(app, <...>)
CORS(app)
auth = FlaskAzureOauth()
auth.init_app(app)

# App routes
@api.route("/foo")
class FooCollection(Resource):
    @auth('my_role')
    def get(self):
        return [<...>]

Это работало нормально, но через несколько дней я начал получать несанкционированные ответы при передаче действующего токена. К сожалению, я не могу отследить причину: токены кажутся в порядке (проверяются вручную или декодируются с помощью jwt.ms), и единственный ответ, который я получил от API:401 UNAUTHORIZEDс телом ответа{ "message": null }.

Я попытался добавить журнал ошибок и обработчики ошибок:

      # Logging request/response
@app.before_request
def log_request_info():
    app.logger.debug(f"{request.method} {request.path} {request.data}")

@app.after_request
def log_response_info(response):
    app.logger.debug(f"{response.status}")
    return response

# Error handling
@app.errorhandler(Unauthorized)
def handle_error(error):
    current_app.logger.debug(f"Oops")
    <...>

@app.errorhandler
def handle_error(error):
    current_app.logger.debug(f"Noooo...!")
    <...>

При этом запросы и ответы регистрируются, а исключения, не относящиеся к HTTP, обрабатываютсяhandle_error. Но ошибки HTTP, такие как 404, 401,... просто проходят мимо, игнорируемые как общим обработчиком ошибок, так и конкретным (@app.errorhandler(Unauthorized)).

Вот код, используемый для проверки этого:

      from werkzeug.exceptions import Unauthorized

def unauth():
    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            raise Unauthorized("No-no")  # <<<
        return wrapper
    return decorator

@api.route("/dummy")
class Dummy(Resource):
    @unauth()
    def get(self):
        return jsonify(message="Hello there!")

@app.errorhandler
def handle_error(Exception):
    current_app.logger.debug("Intercepted")

Фиктивный маршрут защищенunauthдекоратор, который отклоняет все запросы с кодом 401 — НЕАВТОРИЗОВАННО, и это именно то, что получает клиент. Однако@app.errorhandler(Exception), который должен перехватывать ВСЕ исключения, все равно его не пропускает. Заменятьraise Unauthorizedс чем-то вроде1 / 0и исключение обычно будет перехвачено. Ошибки HTTP получают специальную обработку!

Так как же мне правильно их перехватить и изучить? (с акцентом на: как мне узнать, почему было отказано в авторизации токена)

1 ответ

Учетные данные OAuth имеют пожизненный срок действия. Похоже, срок действия вашего токена истек, и вы используете его, не обновляя.

Я бы порекомендовал прочитать сценарий веб-приложения, которое регистрирует пользователей из Microsoft.

Если вы посмотрите документацию Microsoft по токенам доступа , вы увидите, что у каждого токена есть срок жизни . Когда этот срок действия истечет, вам придется запросить новый токен, используя токен обновления , который был бы вам предоставлен, когда вы впервые получили токен доступа (тот, который вы сейчас предоставляете для аутентификации).

Хорошие клиенты обычно автоматически обновляют токены (или пользователи могут просто снова войти в систему после истечения срока действия токена). Поскольку у вас еще нет клиента (Frontend), вам придется сделать это вручную. Существует хорошая документация от Microsoft о том, как получить новый токен доступа с помощью токена обновления .

Что касается того, почему ваши ошибки не обнаруживаются, это потому, чтоFlaskAzureOauthимеет свою собственную оберткуwerkzeug.exceptions.HTTPException, который вызывается в случае каких-либо ошибок со стороны Azure. Я бы посоветовал вам попробовать добавить обработчик ошибок дляflask_azure_oauth.errors._HTTPExceptionи посмотреть, поймает ли он его. Однако я не уверен на 100%, что это произойдет, поскольку код аутентификации и авторизации выполняется до запуска кода API, и я не уверен, что Flaskerrorhandlers принять этот код во внимание.

Другие вопросы по тегам