Authlib Flask OAuth 2.0 授权服务器实现指南:从授权码、令牌端点到 OpenID Connect
【免费下载链接】authlibThe ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS, JWE, JWK, JWA, JWT included.项目地址https://gitcode.com/gh_mirrors/au/authlib点击查看免费下载本指南以 Authlib 仓库中docs/oauth2/authorization-server/flask/文档为主体系统讲解如何用 Flask 搭建一个完整的 OAuth 2.0 授权服务器涵盖 Resource Owner、Client、Token 三大核心模型AuthorizationServer的初始化与配置授权端点与令牌端点的实现RFC 6749 五种内置授权类型Authorization Code、Implicit、Password、Client Credentials、Refresh Token的注册与自定义扩展以及基于授权码/隐式/混合三种流程的 OpenID Connect 1.0 服务端集成。读完本文你将能基于 Authlib 的 Flask 集成模块authlib.integrations.flask_oauth2从零构建可运行的授权服务器并理解每个端点的底层调用链。开篇理解 Flask OAuth 2.0 Provider 的工作方式官方文档明确指出这一节不是如何在 Flask 里创建 OAuth 2.0 Provider的逐步教程而是帮助你理解Flask 集成层是如何工作的以及一个 OAuth 2.0 Provider 背后的技术细节。在开始之前建议先对 OAuth 2.0 基本概念 有所了解。如果你在localhost上开发调试请务必先设置环境变量让 Authlib 允许非 HTTPS 的传输export AUTHLIB_INSECURE_TRANSPORTtrue快速上手的官方示例可以参考仓库外的authlib/example-oauth2-serverOAuth 2.0 服务器与authlib/example-oidc-serverOpenID Connect 服务器两个示例项目。如果你要开发的是 Flask OAuth 2.0客户端或资源服务器则对应查阅 Flask 客户端 与 Flask 资源服务器。授权服务器的三个核心概念创建授权服务器之前必须先理解三个概念Resource Owner、Client 与 Token。它们在仓库中分别有对应的抽象基类与 SQLAlchemy 混入类实现。Resource Owner资源所有者Resource Owner 就是使用你服务的用户可以通过用户名/邮箱加密码或其他方式登录你的网站。资源所有者模型必须实现get_user_id()方法以 SQLAlchemy 模型为例class User(Model): id Column(Integer, primary_keyTrue) # other columns def get_user_id(self): return self.id在authlib.integrations.sqla_oauth2提供的create_save_token_func帮助函数中正是通过request.user.get_user_id()取得用户 ID 来关联 Token见 functions.py因此实现该方法是必须的。ClientOAuth 客户端应用Client 是代表资源所有者、经其授权后访问受保护资源的应用。一个 Client 至少包含三类信息Client Identifier通常叫client_idClient Password通常叫client_secretClient Token Endpoint Authentication Method令牌端点认证方式Authlib 为 SQLAlchemy 提供了现成的混入类OAuth2ClientMixinfrom authlib.integrations.sqla_oauth2 import OAuth2ClientMixin class Client(Model, OAuth2ClientMixin): id Column(Integer, primary_keyTrue) user_id Column( Integer, ForeignKey(user.id, ondeleteCASCADE) ) user relationship(User)Client 通常由开发者用户在你的网站上注册产生。如果你决定完全自己实现 Client 的所有方法可以深入authlib.oauth2.rfc6749的ClientMixinAPI 参考源码位于 models.py。Token访问令牌目前只支持Bearer Token。MAC Token 仍处于草案阶段进入 RFC 后才会提供。Token 用于访问用户资源签发时带有有效时长、受限的 scope 等属性至少包含access_token用于授权 HTTP 请求的令牌refresh_token可选用于换取新 access_token 的令牌client_id该令牌签发给哪个 Clientexpires_at令牌何时过期scope该令牌可访问的受限资源范围同样可以使用 SQLAlchemy 混入类from authlib.integrations.sqla_oauth2 import OAuth2TokenMixin class Token(db.Model, OAuth2TokenMixin): id db.Column(db.Integer, primary_keyTrue) user_id db.Column( db.Integer, db.ForeignKey(user.id, ondeleteCASCADE) ) user db.relationship(User)Token 与资源所有者关联字段名不固定文档中叫user你也可以用其他命名。若自行实现全部方法可参考authlib.oauth2.rfc6749的TokenMixinAPI。初始化 AuthorizationServer 与内置配置Authlib 提供了开箱即用的authlib.integrations.flask_oauth2.AuthorizationServer内置了处理请求与响应的工具。其底层继承自authlib.oauth2.rfc6749.AuthorizationServer在 authorization_server.py 中可以看到它把query_client、save_token与 Flask app 实例绑定在一起。基础初始化同时绑定 appfrom authlib.integrations.flask_oauth2 import AuthorizationServer def query_client(client_id): return Client.query.filter_by(client_idclient_id).first() def save_token(token_data, request): if request.user: user_id request.user.get_user_id() else: # client_credentials grant_type 场景 user_id request.client.user_id # 或者按你的业务约定置为 None user_id None token Token( client_idrequest.client.client_id, user_iduser_id, **token_data ) db.session.add(token) db.session.commit() server AuthorizationServer( app, query_clientquery_client, save_tokensave_token )使用 SQLAlchemy 帮助函数简化authlib.integrations.sqla_oauth2提供了现成的工厂函数from authlib.integrations.sqla_oauth2 import ( create_query_client_func, create_save_token_func ) query_client create_query_client_func(db.session, Client) save_token create_save_token_func(db.session, Token) server AuthorizationServer( app, query_clientquery_client, save_tokensave_token )其中create_query_client_func生成按client_id查询首个 Client的函数create_save_token_func生成将 token 数据写入 Token 模型并提交的函数见 functions.py。仓库测试套件中的 Flask OAuth2 服务器正是这样搭建的见 tests/flask/test_oauth2/oauth2_server.py。延迟初始化init_app也可以先创建空实例再通过init_app绑定server AuthorizationServer() server.init_app(app, query_clientquery_client, save_tokensave_token)配置参数一览不配置也能正常工作但以下配置项可以精细控制行为配置项说明OAUTH2_TOKEN_EXPIRES_IN定义每种 grant 的expires_in的 dictOAUTH2_ACCESS_TOKEN_GENERATOR一个函数或可导入该函数的模块路径字符串用于生成access_tokenOAUTH2_REFRESH_TOKEN_GENERATOR同上用于生成refresh_token也可以是True/FalseOAUTH2_ERROR_URIS(error, error_uri)元组列表为错误响应附加error_uriOAUTH2_TOKEN_EXPIRES_IN示例OAUTH2_TOKEN_EXPIRES_IN { authorization_code: 864000, implicit: 3600, password: 864000, client_credentials: 864000 }OAUTH2_ACCESS_TOKEN_GENERATOR示例def gen_access_token(client, grant_type, user, scope): return create_some_random_string()OAUTH2_REFRESH_TOKEN_GENERATOR接受同样的参数。底层机制从源码看这些配置在AuthorizationServer.load_config()中读取见 authorization_server.py并经由create_bearer_token_generator()组装出一个BearerTokenGenerator见同文件 L93-L136OAUTH2_ACCESS_TOKEN_GENERATOR默认为True此时用authlib.common.security.generate_token生成 42 位随机字符串OAUTH2_REFRESH_TOKEN_GENERATOR默认为False即默认不生成 refresh_token需要显式开启配置值也支持字符串形式的模块路径通过werkzeug.utils.import_string导入或直接传入可调用对象expires_in的取值顺序是先合并内置的GRANT_TYPES_EXPIRES_IN默认值再用你的 dict 覆盖最终对未命中的 grant 类型回退到DEFAULT_EXPIRES_IN。实现授权端点与令牌端点授权端点/oauth/authorize该端点被authorization_code与implicit两种 grant 使用。核心原则授权页必须知道当前登录的资源所有者因此未登录时应重定向到登录页或在授权页上展示登录表单。from authlib.oauth2 import OAuth2Error from flask import request, render_template from your_project.auth import current_user app.route(/oauth/authorize, methods[GET, POST]) def authorize(): try: grant server.get_consent_grant(end_usercurrent_user) except OAuth2Error as error: return authorization.handle_error_response(request, error) if request.method GET: scope grant.client.get_allowed_scope(grant.request.payload.scope) # 你可以把 scope 提取为带丰富信息的列表例如 scopes describe_scope(scope) # returns [{key: email, icon: ...}] return render_template( authorize.html, grantgrant, usercurrent_user, scopesscopes, ) confirmed request.form[confirm] if confirmed: # 资源所有者同意授权 return server.create_authorization_response(grantgrant, grant_usercurrent_user) # 资源所有者拒绝授权 return server.create_authorization_response(grantgrant, grant_userNone)底层流程get_consent_grant()会创建FlaskOAuth2Request、按response_type匹配到对应 grant 并执行授权请求校验见 authorization_server.py随后create_authorization_response()内部调用grant.validate_authorization_request()与grant.create_authorization_response(redirect_uri, grant_user)见同文件 L361-L390。授权码流程成功时返回302并把code、state追加到 redirect_uri 查询串中见 grants/authorization_code.py。令牌端点/oauth/token令牌端点要简单得多app.route(/oauth/token, methods[POST]) def issue_token(): return server.create_token_response()create_token_response()内部先按grant_type匹配 token grant再执行validate_token_request()与create_token_response()见 authorization_server.py。不过要注意光有路由还不够必须为它们注册受支持的 grant 类型否则授权端点会返回UnsupportedResponseTypeError、令牌端点会返回UnsupportedGrantTypeError。仓库测试中注册 grant 的典型写法见 tests/flask/test_oauth2/test_authorization_code_grant.pyserver.register_grant(AuthorizationCodeGrant)注册 RFC 6749 内置 Grant 类型RFC 6749 定义了四种标准 grantAuthlib 另附 Refresh Token grant你也可以创建自定义扩展 grant并通过server.register_grant()注册。每个 grant 都有自己的TOKEN_ENDPOINT_AUTH_METHODS默认值即令牌端点客户端认证方式可在子类中覆盖。Authorization Code Grant授权码授权码是最常见的 grant几乎所有 OAuth 2 Provider 都支持它用授权码换取 access token。为此需要一个地方存储授权码数据库或 redis 之类的缓存。先定义存储模型from authlib.integrations.sqla_oauth2 import OAuth2AuthorizationCodeMixin class AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin): id db.Column(db.Integer, primary_keyTrue) user_id db.Column( db.Integer, db.ForeignKey(user.id, ondeleteCASCADE) ) user db.relationship(User)再子类化AuthorizationCodeGrant并实现四个缺失方法from authlib.oauth2.rfc6749 import grants class AuthorizationCodeGrant(grants.AuthorizationCodeGrant): def save_authorization_code(self, code, request): client request.client auth_code AuthorizationCode( codecode, client_idclient.client_id, redirect_urirequest.redirect_uri, scoperequest.payload.scope, user_idrequest.user.id, ) db.session.add(auth_code) db.session.commit() return auth_code def query_authorization_code(self, code, client): item AuthorizationCode.query.filter_by( codecode, client_idclient.client_id).first() if item and not item.is_expired(): return item def delete_authorization_code(self, authorization_code): db.session.delete(authorization_code) db.session.commit() def authenticate_user(self, authorization_code): return User.query.get(authorization_code.user_id) # 注册到 grant 端点 server.register_grant(AuthorizationCodeGrant)注意AuthorizationCodeGrant是最复杂的 grant。这四个方法分别对应源码中的抽象基类AuthorizationCodeGrant见 grants/authorization_code.pysave_authorization_code、query_authorization_code、delete_authorization_code、authenticate_user均须在子类中实现。在令牌端点的validate_token_request里Authlib 还会校验 code 是否存在、redirect_uri 是否与签发时一致见同文件 L222-L242。默认允许的客户端认证方式client_auth_methods有三种client_secret_basic、client_secret_post、none。如需移除none例如准备支持 OpenID Connect 时这一步很重要class AuthorizationCodeGrant(grants.AuthorizationCodeGrant): TOKEN_ENDPOINT_AUTH_METHODS [client_secret_basic, client_secret_post]另外授权码长度默认 48 位AUTHORIZATION_CODE_LENGTH 48可通过子类属性覆盖。Implicit Grant隐式隐式流程通常用于浏览器端资源所有者授权后 access token 直接出现在 redirect_uri 中。没有任何缺失实现注册即可from authlib.oauth2.rfc6749 import grants # 注册到 grant 端点 server.register_grant(grants.ImplicitGrant)Implicit Grant 面向没有client_secret的 public 客户端默认客户端认证方式为none。从源码看它不会签发 refresh_tokentoken 会以 fragment 形式追加到重定向 URI见 grants/implicit.py。Resource Owner Password Credentials Grant密码凭证资源所有者用自己的用户名和密码换取 access token。只应在客户端可信的场景使用通过子类化实现from authlib.oauth2.rfc6749 import grants class PasswordGrant(grants.ResourceOwnerPasswordCredentialsGrant): def authenticate_user(self, username, password): user User.query.filter_by(usernameusername).first() if user.check_password(password): return user # 注册到 grant 端点 server.register_grant(PasswordGrant)默认客户端认证方式为client_secret_basic可在子类中增加更多方式class PasswordGrant(grants.ResourceOwnerPasswordCredentialsGrant): TOKEN_ENDPOINT_AUTH_METHODS [ client_secret_basic, client_secret_post ]底层实现中validate_token_request会强制检查username与password参数是否存在见 grants/resource_owner_password_credentials.py。Client Credentials Grant客户端凭证客户端凭证流程可以访问公开资源也可能访问客户端创建者的资源取决于你如何为这个 grant 签发 token。注册同样简单from authlib.oauth2.rfc6749 import grants # 注册到 grant 端点 server.register_grant(grants.ClientCredentialsGrant)默认客户端认证方式为client_secret_basic可扩展class ClientCredentialsGrant(grants.ClientCredentialsGrant): TOKEN_ENDPOINT_AUTH_METHODS [ client_secret_basic, client_secret_post ]注意此类 grant 不会签发 refresh_tokeninclude_refresh_tokenFalse见 grants/client_credentials.py。Refresh Token Grant刷新令牌很多 OAuth 2 Provider 不实现刷新令牌端点Authlib 把它作为一个 grant 类型提供from authlib.oauth2.rfc6749 import grants class RefreshTokenGrant(grants.RefreshTokenGrant): def authenticate_refresh_token(self, refresh_token): item Token.query.filter_by(refresh_tokenrefresh_token).first() # 自行定义 is_refresh_token_valid # 通常应检查 refresh token 是否过期、是否已被吊销 if item and item.is_refresh_token_valid(): return item def authenticate_user(self, credential): return User.query.get(credential.user_id) def revoke_old_credential(self, credential): credential.revoked True db.session.add(credential) db.session.commit() # 注册到 grant 端点 server.register_grant(RefreshTokenGrant)默认客户端认证方式为client_secret_basic可扩展class RefreshTokenGrant(grants.RefreshTokenGrant): TOKEN_ENDPOINT_AUTH_METHODS [ client_secret_basic, client_secret_post ]默认情况下RefreshTokenGrant 不会在令牌响应中签发新的refresh_token可通过如下配置改变class RefreshTokenGrant(grants.RefreshTokenGrant): INCLUDE_NEW_REFRESH_TOKEN True这对应源码中的INCLUDE_NEW_REFRESH_TOKEN False类属性见 grants/refresh_token.py。刷新时 Authlib 还会校验请求的 scope 必须被原 token 的 scope 覆盖见同文件 L60-L71。自定义 Grant 类型与 Grant 扩展创建自定义 Grant在 Authlib 中一个Grant支持两种端点Authorization Endpoint处理带response_type的请求Token Endpoint签发 token 的端点。用BaseGrant创建自定义 grantfrom authlib.oauth2.rfc6749.grants import ( BaseGrant, AuthorizationEndpointMixin, TokenEndpointMixin ) class MyCustomGrant(BaseGrant, AuthorizationEndpointMixin, TokenEndpointMixin): GRANT_TYPE custom-grant-type-name def validate_authorization_request(self): # 仅在使用 AuthorizationEndpointMixin 时需要 def create_authorization_response(self, grant_user): # 仅在使用 AuthorizationEndpointMixin 时需要 def validate_token_request(self): # 仅在使用 TokenEndpointMixin 时需要 def create_token_response(self): # 仅在使用 TokenEndpointMixin 时需要注册自定义 grant 后AuthorizationServer.register_grant()会根据类上是否有check_authorization_endpoint/check_token_endpoint方法由两个 Mixin 提供自动决定把它挂到授权端点还是令牌端点见 authorization_server.py。若想加深理解可以直接阅读内置 grant 的源码仓库还内置了其他规范定义的扩展 grant例如 RFC 7523 JWT Bearer Grant。Grant 扩展版本 0.10Grant 可以接受扩展。注册 grant 时传入扩展列表authorization_server.register_grant(AuthorizationCodeGrant, [extension])最典型的例子是 Authlib 内置的CodeChallenge扩展RFC 7636 PKCEserver.register_grant(AuthorizationCodeGrant, [CodeChallenge(requiredFalse)])关于CodeChallenge的更多细节见 RFC 7636 规范文档。从源码看该扩展通过register_hook挂到授权请求校验校验code_challenge格式与code_challenge_method支持plain与S256和令牌请求校验校验code_verifier两个钩子上见 challenge.py。仓库测试 tests/flask/test_oauth2/test_code_challenge.py 中以requiredTrue注册并验证了整个 PKCE 流程。注册其他令牌端点Flask OAuth 2.0 授权服务器通过authorization_server.register_endpoint注册其他令牌端点可用的内置端点包括撤销端点Revocation EndpointRFC 7009authlib.oauth2.rfc7009.RevocationEndpoint内省端点Introspection EndpointRFC 7662authlib.oauth2.rfc7662.IntrospectionEndpointregister_endpoint()接受端点类或实例端点内部通过ENDPOINT_NAME注册到端点注册表见 authorization_server.py请求最终由create_endpoint_response()统一处理。使用 SQLAlchemy 时sqla_oauth2还提供了create_revocation_endpoint与create_query_token_func等帮助函数见 functions.py。注册错误文档 URIOAUTH2_ERROR_URIS为了提升开发者调试体验建议为错误编写文档。Authlib 内置了一组 RFC 6749 错误定义。例如为invalid_client设计一个错误文档页面https://developer.your-company.com/errors#invalid-client然后通过OAUTH2_ERROR_URIS配置注册OAUTH2_ERROR_URIS [ (invalid_client, https://developer.your-company.com/errors#invalid-client), # 其他错误 URI ]如果未配置OAUTH2_ERROR_URIS错误响应将不包含任何error_uri数据。底层实现中AuthorizationServer.get_error_uri()会把该配置转成 dict 并按error.error查表见 authorization_server.py。错误的 I18N从版本 0.8 起error_description已支持国际化i18n但文档提示该功能仍有待完善的工作。扩展为 OpenID Connect 1.0 ProviderOpenID Connect 1.0 自版本 0.6 起被支持。OIDC 集成建立在自定义 grant自定义 Grant 类型与 grant 扩展Grant 扩展之上由于 OIDC 构建于 OAuth 2.0 框架之上请先通读前文的授权服务器部分。先理解 JWT 的四个概念OpenID Connect 大量使用 JWT建议先具备 Authlib JOSE 基础。对 OIDC 而言至少要理解四个概念algJWT 使用的签名算法keyJWT 使用的私钥issJWT 中的签发者Issuer值expJWT 过期时间algJWT 签名算法alg是 JWS 头部定义的值{alg: RS256}RFC 7518 定义的可用算法见 rfc7518.rstHS256 / HS384 / HS512HMAC 使用 SHA-256 / SHA-384 / SHA-512RS256 / RS384 / RS512RSASSA-PKCS1-v1_5 使用 SHA-256 / SHA-384 / SHA-512ES256 / ES384 / ES512ECDSA 使用 P-256 / P-384 / P-521PS256 / PS384 / PS512RSASSA-PSS 使用 SHA-256 / SHA-384 / SHA-512及对应 MGF1不建议使用 HMAC 类算法因为需要在服务器与客户端之间共享密钥。大多数 OpenID Connect 服务使用RS256。keyJWT 私钥生成 JWT 需要私钥具体使用哪种私钥取决于alg。例如alg为RS256时使用 RSA 私钥key -----BEGIN RSA PRIVATE KEY-----\nMIIEog... # 或使用 JWK 格式 key {kty: RSA, n: ...}issJWT 签发者iss是 JWT payload 中的签发者值可以是你的网站名或 URL。例如 Google 使用{iss: https://accounts.google.com}Code Flow授权码流程OIDC 授权码流程依赖并扩展 OAuth 2.0 授权码流程流程相同但带有扩展特性。把OpenIDCode扩展应用到AuthorizationCodeGrant即可。第一步实现OpenIDCode缺失的方法from joserfc.jwk import KeySet from authlib.oidc.core import grants, UserInfo class OpenIDCode(grants.OpenIDCode): def resolve_client_private_key(self, client): with open(jwks_file_path) as f: data json.load(f) return KeySet.import_key_set(data) def get_client_claims(self, client): return { iss: https://example.com, } def exists_nonce(self, nonce, request): exists AuthorizationCode.query.filter_by( client_idrequest.payload.client_id, noncenonce ).first() return bool(exists) def generate_user_info(self, user, scope): return UserInfo( subuser.id, nameuser.name, emailuser.email, ).filter(scope)第二步由于AuthorizationCode数据中多了一个nonce值需要更新AuthorizationCodeGrant的save_authorization_code方法把这个值存进数据库class AuthorizationCodeGrant(_AuthorizationCodeGrant): def save_authorization_code(self, code, request): # openid 请求可能携带 nonce 参数 nonce request.payload.data.get(nonce) auth_code AuthorizationCode( codecode, client_idrequest.client.client_id, redirect_urirequest.redirect_uri, scoperequest.payload.scope, user_idrequest.user.id, noncenonce, ) db.session.add(auth_code) db.session.commit() return auth_code # ...第三步将AuthorizationCodeGrant与OpenIDCode扩展一起注册# 注册到 grant 端点 server.register_grant(AuthorizationCodeGrant, [OpenIDCode(require_nonceTrue)])OIDC Code Flow 与标准 Code Flow 的区别在于OpenID Connect 请求带有openidscopeGET /authorize? response_typecode scopeopenid%20profile%20email client_ids6BhdRkqt3 stateaf0ifjsldkj redirect_urihttps%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1 Host: server.example.com按上面的示例你还需要把应用里 Client 的 scope 改成类似openid profile email的值。一旦应用添加了openidscope只要客户端以openidscope 请求 tokenAuthlib 就会为它签发 OpenID token。仓库测试 tests/flask/test_oauth2/test_openid_code_grant.py 中展示了完整的注册方式。Implicit Flow隐式流程隐式流程主要用于浏览器端脚本语言实现的客户端。注册前同样需要实现OpenIDImplicitGrant的缺失方法from joserfc.jwk import KeySet from authlib.oidc.core import grants class OpenIDImplicitGrant(grants.OpenIDImplicitGrant): def resolve_client_private_key(self, client): with open(jwks_file_path) as f: data json.load(f) return KeySet.import_key_set(data) def get_client_claims(self, client): return { iss: https://example.com, } def exists_nonce(self, nonce, request): exists AuthorizationCode.query.filter_by( client_idrequest.payload.client_id, noncenonce ).first() return bool(exists) def generate_user_info(self, user, scope): return UserInfo( subuser.id, nameuser.name, emailuser.email, ).filter(scope) server.register_grant(OpenIDImplicitGrant)Hybrid Flow混合流程混合流程是 Code Flow 与 Implicit Flow 的混合。只需实现授权端点部分令牌端点由 Authorization Code Flow 处理。OpenIDHybridGrant是OpenIDImplicitGrant的子类所以缺失方法相同唯一区别是OpenIDHybridGrant还多一个缺失方法save_authorization_codefrom joserfc.jwk import KeySet from authlib.oidc.core import grants from authlib.common.security import generate_token class OpenIDHybridGrant(grants.OpenIDHybridGrant): def resolve_client_private_key(self, client): with open(jwks_file_path) as f: data json.load(f) return KeySet.import_key_set(data) def get_client_claims(self, client): return { iss: https://example.com, } def get_client_algorithm(self, client): return RS512 def save_authorization_code(self, code, request): nonce request.payload.data.get(nonce) item AuthorizationCode( codecode, client_idrequest.client.client_id, redirect_urirequest.redirect_uri, scoperequest.payload.scope, user_idrequest.user.id, noncenonce, ) db.session.add(item) db.session.commit() return code def exists_nonce(self, nonce, request): exists AuthorizationCode.query.filter_by( client_idrequest.payload.client_id, noncenonce ).first() return bool(exists) def generate_user_info(self, user, scope): return UserInfo( subuser.id, nameuser.name, emailuser.email, ).filter(scope) # 注册到 grant 端点 server.register_grant(OpenIDHybridGrant)由于所有 OpenID Connect 流程都需要exists_nonce、resolve_client_private_key、get_client_claims、get_client_algorithm与generate_user_info方法建议为它们编写共享函数。附API 参考与信号authlib.integrations.flask_oauth2.AuthorizationServer对外提供的关键方法详见 api.rstregister_grant注册 grant 类可携带扩展列表register_endpoint注册额外端点如撤销、内省create_bearer_token_generator创建 Bearer Token 生成器get_consent_grant校验当前请求并返回用于授权页的 grantcreate_authorization_response创建授权响应create_token_response创建令牌响应create_endpoint_response创建端点响应同模块还导出了ResourceProtector与current_token资源服务器上受require_oauth保护的路由可通过current_token访问当前令牌实例定义见 resource_protector.py。信号SignalsFlask 集成层定义了三个信号见 signals.pyclient_authenticated客户端认证成功时触发token_revoked令牌被吊销时触发token_authenticated令牌被认证时触发另外需要提醒sqla_oauth2模块将在版本 1.0 被移除见 api.rst其中提供的create_query_client_func、create_save_token_func、create_query_token_func、create_revocation_endpoint、create_bearer_token_validator帮助函数请留意迁移计划。总结从docs/oauth2/authorization-server/flask/文档出发我们完整梳理了 Authlib Flask OAuth 2.0 授权服务器的搭建路径先理解 Resource Owner / Client / Token 三个模型并准备query_client与save_token初始化AuthorizationServer并通过四个OAUTH2_*配置项控制令牌生成与错误文档再实现/oauth/authorize与/oauth/token两个端点并注册所需 grant最后通过 grant 扩展叠加 PKCE 或 OpenID Connect 能力。掌握这些内容后你既能快速搭建一个符合 RFC 6749 的授权服务器也能基于源码调用链register_grant→get_token_grant→validate_token_request→create_token_response深入排查与定制自己的流程。赞分享【免费下载链接】authlibThe ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS, JWE, JWK, JWA, JWT included.项目地址https://gitcode.com/gh_mirrors/au/authlib点击查看免费下载相关推荐Authlib 授权服务器Authorization Server构建指南OAuth 2.0 与 OpenID Connect Provider 实战Authlib 授权服务器Authorization Server构建指南OAuth 2.0 与 OpenID Connect Provider 实战 AAuthlib Flask OAuth 2.0 授权服务器令牌端点注册实战Revocation 与 Introspection 深度解析Authlib Flask OAuth 2.0 授权服务器令牌端点注册实战Revocation 与 Introspection 深度解析 本篇技术指南聚焦于Authlib Flask 授权服务器实战从模型设计到授权码与令牌端点完整配置Authlib Flask 授权服务器实战从模型设计到授权码与令牌端点完整配置 Authlib 为 Flask 提供了开箱即用的 OAuth 2.0 授权服务上一篇Wand-Enhancer解锁游戏修改器本地化增强的全新体验下一篇3分钟终极指南免费解锁WeMod完整Pro功能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考