mcpField
A
auth0-flask

auth0-flask

github.com/auth0安全55411 次安装1 天前更新
代码仓库

关于 auth0-flask

auth0-flask 是为 Flask Web 应用接入 Auth0 登录、登出和用户资料的技能,使用 auth0-server-python。它说明基于 Flask session 的无状态配置,也介绍 Redis 服务端会话方案。

主要功能

  1. 01配置 Auth0 交互式登录与回调
  2. 02读取用户资料并保护路由
  3. 03获取访问令牌并执行登出
  4. 04使用 Cookie 会话保存加密数据
  5. 05可选配置 Redis 服务端会话

适用场景

  1. 01为 Flask 应用添加登录和登出
  2. 02保护需要身份验证的页面
  3. 03在应用中获取用户资料或访问令牌
  4. 04会话数据超出 Cookie 限制时改用 Redis

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/auth0/agent-skills --skill auth0-flask

安装后在 Claude Code 中输入/auth0-flask确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
integration.md
setup.md
tests/
benchmark-config.json
evals.json
graders.json
package.json
prompt.md
run-evals.mjs

详细介绍

Auth0 Flask Web 应用集成

使用 auth0-server-python 为 Flask Web 应用添加登录、登出和用户资料功能。


前提条件

  • Flask 应用
  • 已配置 Auth0 Regular Web Application(不是 API,必须是 Application)
  • 如果尚未设置 Auth0,请先使用 auth0-quickstart 技能

不适用场景

  • 使用 JWT Bearer 验证的 Python API — FastAPI 请使用 auth0-fastapi-api,或参阅 Django REST Framework 快速入门
  • 带有登录/登出界面的 FastAPI Web 应用 — 目前还没有专用技能;请参阅 FastAPI 快速入门
  • 单页应用 — 客户端身份验证请使用 auth0-react、auth0-vue 或 auth0-angular
  • Next.js 应用 — 请使用同时处理客户端和服务端的 auth0-nextjs
  • Node.js Web 应用 — 基于会话的身份验证请使用 auth0-express 或 auth0-fastify

快速入门工作流

1. 安装 SDK
pip install auth0-server-python "flask[async]" python-dotenv

重要: 必须安装 flask[async](不能只安装 flask)。[async] 扩展会安装 asgiref,这是 Flask 2.0+ 支持 async def 路由处理函数所必需的。缺少它时,异步路由将无法运行。在 requirements.txt 中使用 flask[async]>=2.0.0。

2. 配置环境

创建 .env:

AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_SECRET=your_generated_app_secret
AUTH0_REDIRECT_URI=http://localhost:5000/callback

AUTH0_DOMAIN 是你的 Auth0 租户域名(不含 https://)。AUTH0_CLIENT_ID 和 AUTH0_CLIENT_SECRET 来自 Auth0 Application 设置。AUTH0_SECRET 用于加密会话数据,可通过 openssl rand -hex 64 生成。

3. 配置 Auth0 控制面板

在 Auth0 Application 设置中:

  • 允许的回调 URL:http://localhost:5000/callback
  • 允许的登出 URL:http://localhost:5000
4. 创建身份验证模块

创建 auth.py,使用基于 Flask 会话的存储初始化 ServerClient。存储使用 Flask 内置的 session(默认基于 Cookie),实现无状态配置,无需外部数据库:

import os
from flask import session as flask_session
from auth0_server_python.auth_server.server_client import ServerClient
from auth0_server_python.auth_types import StateData, TransactionData
from auth0_server_python.store import StateStore, TransactionStore
from dotenv import load_dotenv

load_dotenv()  # Uses .env by default; pass load_dotenv(".env.local") if credentials are in .env.local


class FlaskSessionStateStore(StateStore):
    """State store that uses Flask's session for persistence."""

    def __init__(self, secret: str):
        super().__init__({"secret": secret})

    async def set(self, identifier, state, remove_if_expires=False, options=None):
        data = state.dict() if hasattr(state, "dict") else state
        flask_session[identifier] = self.encrypt(identifier, data)

    async def get(self, identifier, options=None):
        data = flask_session.get(identifier)
        if data is None:
            return None
        decrypted = self.decrypt(identifier, data)
        # Ensure to not return a dict, as the underlying SDK expects a StateData instance, not a dict
        return StateData(**decrypted) if isinstance(decrypted, dict) else decrypted

    async def delete(self, identifier, options=None):
        flask_session.pop(identifier, None)

    async def delete_by_logout_token(self, claims, options=None):
        pass


class FlaskSessionTransactionStore(TransactionStore):
    """Transaction store that uses Flask's session for persistence."""

    def __init__(self, secret: str):
        super().__init__({"secret": secret})

    async def set(self, identifier, state, remove_if_expires=False, options=None):
        data = state.dict() if hasattr(state, "dict") else state
        flask_session[identifier] = self.encrypt(identifier, data)

    async def get(self, identifier, options=None):
        data = flask_session.get(identifier)
        if data is None:
            return None
        decrypted = self.decrypt(identifier, data)
        # Ensure to not return a dict, as the underlying SDK expects a TransactionData instance, not a dict
        return TransactionData(**decrypted) if isinstance(decrypted, dict) else decrypted

    async def delete(self, identifier, options=None):
        flask_session.pop(identifier, None)


secret = os.getenv("AUTH0_SECRET")

auth0 = ServerClient(
    domain=os.getenv("AUTH0_DOMAIN"),
    client_id=os.getenv("AUTH0_CLIENT_ID"),
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),
    secret=secret,
    redirect_uri=os.getenv("AUTH0_REDIRECT_URI"),
    state_store=FlaskSessionStateStore(secret=secret),
    transaction_store=FlaskSessionTransactionStore(secret=secret),
    authorization_params={"scope": "openid profile email"},
)

创建一个 ServerClient 实例并重复使用。绝不要将凭证硬编码,始终使用环境变量。

工作原理: Flask 的默认会话基于 Cookie(无状态)。SDK 会先使用 JWE 加密会话数据(令牌、用户资料),再将其存储到会话中,因此 Cookie 中的数据既经过签名也经过加密。无需服务端数据库。

无需使用 store_options 或 before_request: SDK 支持向存储方法传入 store_options(例如请求/响应对象)。由于这些存储使用 flask.session(它在请求期间全局可用),因此不需要从 store_options 获取任何内容,你可以在调用 SDK 方法时不传入该参数。如果你实现了直接管理 Cookie 的自定义存储(而不是使用 flask.session),则需要重新引入 store_options,并传入 {"request": request, "response": response}。

Cookie 大小提示: 无状态会话会将所有数据存储在 Cookie 中(限制约为 4KB)。对于大多数应用来说,这已足够。如果你存储了大量会话数据或达到 Cookie 大小限制,请切换到有状态配置。

5. 配置 Flask 应用

在 app.py 中,使用密钥和会话配置设置 Flask:

import os
from flask import Flask, redirect, request
from auth import auth0
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv("AUTH0_SECRET")
app.config.update(
    SESSION_COOKIE_SECURE=False,  # Set to True in production (requires HTTPS)
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
)

重要: 必须设置 app.secret_key,Flask 会话管理才能正常工作。没有它,会话将无法使用。

用于生产环境: 使用 HTTPS 部署时,请设置 SESSION_COOKIE_SECURE=True。在生产环境中将其设为 False,会导致会话 Cookie 通过未加密连接发送。

6. 添加主页路由
@app.route("/")
async def home():
    user = await auth0.get_user()
    if user:
        return f"Hello, {user['name']}! <a href='/profile'>Profile</a> | <a href='/logout'>Logout</a>"
    return "Welcome! <a href='/login'>Login</a>"
7. 添加登录路由
@app.route("/login")
async def login():
    authorization_url = await auth0.start_interactive_login()
    return redirect(authorization_url)

start_interactive_login() 会返回一个指向 Auth0 Universal Login 页面的 URL 字符串。必须使用 redirect() 包装该 URL。授权参数(scope、redirect_uri)已在 ServerClient 上配置。

8. 添加回调路由
@app.route("/callback")
async def callback():
    try:
        await auth0.complete_interactive_login(str(request.url))
        return redirect("/")
    except Exception as e:
        return f"Authentication error: {str(e)}", 400

将 str(request.url) 作为第一个参数传入——这是包含授权码查询参数的完整回调 URL。务必使用 try/except 包装,因为令牌交换可能失败(例如授权码过期、CSRF 不匹配)。

9. 添加个人资料路由(受保护)
@app.route("/profile")
async def profile():
    user = await auth0.get_user()
    if user is None:
        return redirect("/login")
    return (
        f"<h1>{user['name']}</h1>"
        f"<p>Email: {user['email']}</p>"
        f"<img src='{user['picture']}' alt='{user['name']}' width='100' />"
        f"<p><a href='/logout'>Logout</a></p>"
    )

get_user() 会从会话中返回用户的个人资料;如果用户未登录,则返回 None。

10. 添加登出路由
@app.route("/logout")
async def logout():
    url = await auth0.logout()
    return redirect(url)

logout() 会返回 Auth0 登出 URL。将用户重定向到该 URL。

11. 测试应用
flask run

访问 http://localhost:5000/login 以启动登录流程。


使用 Redis 进行有状态配置

对于生产应用,或者会话数据超过 Cookie 大小限制时,请使用 Flask-Session 和 Redis 在服务端存储会话。Cookie 中只存储会话 ID。

1. 安装依赖项
pip install flask-session redis
2. 配置 Flask-Session

更新 app.py,以使用由 Redis 支持的会话:

import os
from flask import Flask, redirect, request
from flask_session import Session
from auth import auth0
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv("AUTH0_SECRET")
app.config.update(
    SESSION_TYPE="redis",
    SESSION_PERMANENT=True,
    SESSION_KEY_PREFIX="auth0:",
    SESSION_COOKIE_SECURE=False,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
)
Session(app)
3. 无需更改存储

auth.py 中相同的 FlaskSessionStateStore 和 FlaskSessionTransactionStore 无需修改即可使用。Flask-Session 会透明地将 flask.session 后端从 Cookie 切换到 Redis——存储仍像之前一样使用 flask.session。

路由与无状态配置完全相同——无需更改代码。


常见错误

错误修复方法
在源代码中硬编码 domain、client_id 或 client_secret始终从环境变量读取,绝不要在代码中嵌入凭据
直接使用 Authlib 或 python-jose无需使用;auth0-server-python 会处理所有 OAuth/OIDC 流程
使用 Flask-Login 或 Flask-Dance无需使用;SDK 会管理会话和身份验证
使用 jwt.decode() 手动解析 JWTSDK 会在内部处理令牌验证
安装 flask 时未添加 [async] 附加项必须在 requirements.txt 中使用 flask[async]>=2.0.0——否则,异步路由处理程序会静默失败
使用同步路由处理程序所有调用 SDK 方法的路由都必须使用 async def 并配合 await
忘记设置 app.secret_keyFlask 会话管理必需——未设置时,会话会静默失败
在 Flask 中使用 auth0-fastapi-api该包用于 FastAPI API;Flask 应使用 auth0-server-python
将 domain 作为包含 https:// 的完整 URL 传入domain 应为纯域名,例如 my-tenant.us.auth0.com,而不是 https://my-tenant.us.auth0.com
未在 Auth0 Dashboard 中配置回调 URL必须将 http://localhost:5000/callback 添加到 Allowed Callback URLs
直接返回 start_interactive_login()它返回的是 URL 字符串,而不是响应;必须用 redirect() 包装
未处理 /callback 中的错误complete_interactive_login() 可能会失败;务必使用 try/except 包装
调用 SDK 方法时未使用 await所有 SDK 方法都是异步的;忘记使用 await 会返回协程,而不是结果
将选项作为位置参数传给 logout()使用 logout(store_options=...)——第一个位置参数是 LogoutOptions,而不是存储选项
预期后端通道登出能够正常工作基于 Cookie 的会话不支持此功能——delete_by_logout_token 不执行任何操作。请使用标准的 /logout 路由
部署时设置 SESSION_COOKIE_SECURE=False生产环境中必须设为 True,否则 Cookie 会通过 HTTP 发送

SDK 关键方法

所有方法均为异步方法:

方法签名用途
start_interactive_loginawait auth0.start_interactive_login()返回授权 URL 字符串 — 使用 redirect() 包装
complete_interactive_loginawait auth0.complete_interactive_login(str(request.url))处理回调 URL,并将授权码交换为令牌
get_userawait auth0.get_user()返回当前会话的用户字典或 None
get_access_tokenawait auth0.get_access_token()返回用于调用外部 API 的访问令牌
logoutawait auth0.logout()返回 Auth0 登出 URL 字符串

相关技能

  • auth0-express — 适用于带登录/登出会话的服务端渲染 Express Web 应用
  • auth0-fastify — 适用于基于会话认证的 Fastify Web 应用
  • auth0-cli — 在终端中管理 Auth0 资源

快速参考

ServerClient 配置:

auth0 = ServerClient(
    domain=os.getenv("AUTH0_DOMAIN"),                    # required
    client_id=os.getenv("AUTH0_CLIENT_ID"),              # required
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),      # required
    secret=os.getenv("AUTH0_SECRET"),                    # required (encryption secret)
    redirect_uri=os.getenv("AUTH0_REDIRECT_URI"),        # required
    state_store=FlaskSessionStateStore(secret=secret),   # required
    transaction_store=FlaskSessionTransactionStore(secret=secret),  # required
    authorization_params={"scope": "openid profile email"},  # recommended
)

路由保护模式:

user = await auth0.get_user()
if user is None:
    return redirect("/login")

环境变量:

  • AUTH0_DOMAIN — 你的 Auth0 租户域名(例如 tenant.us.auth0.com)
  • AUTH0_CLIENT_ID — 你的应用客户端 ID
  • AUTH0_CLIENT_SECRET — 你的应用客户端密钥
  • AUTH0_SECRET — 加密和会话密钥
  • AUTH0_REDIRECT_URI — 回调 URL(例如 http://localhost:5000/callback)

详细文档

  • 设置指南 - 自动化设置脚本、环境配置、Auth0 CLI 用法
  • 集成指南 - 受保护路由、调用 API、会话管理、错误处理
  • API 参考 - 完整的 ServerClient API、配置选项、存储实现、安全性

参考资料

评论

支持 Markdown