diff --git a/docs/clients/dingding.md b/docs/clients/dingding.md new file mode 100644 index 0000000..977777e --- /dev/null +++ b/docs/clients/dingding.md @@ -0,0 +1,5 @@ +## 创建第三方应用 + +登录登录钉钉开发者后台:[open-dev.dingtalk](https://open-dev.dingtalk.com/) + +... diff --git a/docs/clients/douyin.md b/docs/clients/douyin.md new file mode 100644 index 0000000..7c1f4ac --- /dev/null +++ b/docs/clients/douyin.md @@ -0,0 +1,5 @@ +## 创建第三方应用 + +访问抖音开放平台:[open.douyin](https://open.douyin.com/) + +... diff --git a/docs/clients/feishu.md b/docs/clients/feishu.md new file mode 100644 index 0000000..0db8bde --- /dev/null +++ b/docs/clients/feishu.md @@ -0,0 +1,9 @@ +## 注册账号 + +地址:[gitee](https://gitee.com/) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +登录已注册的账号... diff --git a/docs/clients/gitee.md b/docs/clients/gitee.md new file mode 100644 index 0000000..0db8bde --- /dev/null +++ b/docs/clients/gitee.md @@ -0,0 +1,9 @@ +## 注册账号 + +地址:[gitee](https://gitee.com/) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +登录已注册的账号... diff --git a/docs/clients/github.md b/docs/clients/github.md new file mode 100644 index 0000000..5f0ab42 --- /dev/null +++ b/docs/clients/github.md @@ -0,0 +1,42 @@ +## 注册账号 + +地址:[GitHub](https://github.com/) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +### 登录 + +登录已注册的账号,通过主页右上角进入个人设置页:[profile](https://github.com/settings/profile) + +![settings.png](../public/images/github/settings.png) + +### 创建应用 + +进入开发者界面:[Developer settings](https://github.com/settings/apps) + +![dev.png](../public/images/github/dev.png) + +创建 OAuth app:[OAuth apps](https://github.com/settings/developers) + +![oauth.png](../public/images/github/oauth.png) + +- `Application name` 填写自己的网站名称 +- `Homepage URL` 填写自己的网站首页地址 +- `Application description` 填写自己的应用描述 +- `Authorization callback URL` 用户授权后跳转到自己平台的地址。通常情况下,开发者需要在此路由代码中实现自己平台用户的注册、绑定等操作 +- `Enable Device Flow` 不需要勾选 + +信息输入完成后,点击下方绿色的 ==`Register applaction`== 按钮创建应用 + +![register.png](../public/images/github/register.png) + +### 创建密钥 + +创建完成后,进入应用详情页,`Client secrets` + +![secrets.png](../public/images/github/secrets.png) + +创建完成后,记录 `Client ID`、`Client Secret`、`Authorization callback URL`,这三个东西在我们集成的时候都用得到,请妥善保管 +ID 和 Secret diff --git a/docs/clients/google.md b/docs/clients/google.md new file mode 100644 index 0000000..37ba1ec --- /dev/null +++ b/docs/clients/google.md @@ -0,0 +1,5 @@ +## 创建第三方应用 + +登录 Google 开发者中心:[console.developers.google](https://console.developers.google.com/apis/dashboard) + +... diff --git a/docs/clients/linuxdo.md b/docs/clients/linuxdo.md new file mode 100644 index 0000000..bc8a7f6 --- /dev/null +++ b/docs/clients/linuxdo.md @@ -0,0 +1,33 @@ +## 注册账号 + +地址:[LinuxDo](https://linux.do/) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +### 登录 + +登录已注册的账号,通过主页左侧 Connect 进入 connect 页面:[connect](https://connect.linux.do/) + +![connect.png](../public/images/linuxdo/connect.png) + +### 创建应用 + +进入开发者界面:[sso](https://connect.linux.do/dash/sso) + +![dev.png](../public/images/linuxdo/dev.png) + +申请新接入:[new](https://connect.linux.do/dash/sso/new) + +![new.png](../public/images/linuxdo/new.png) + +信息输入完成后,点击下方蓝色的 ==`保存`== 按钮创建应用 + +![save.png](../public/images/linuxdo/save.png) + +创建完成后,进入应用详情页 + +![secrets.png](../public/images/linuxdo/secrets.png) + +记录 `Client ID`、`Client Secret`、`回调地址`,这三个东西在我们集成的时候都用得到,请妥善保管 ID 和 Secret diff --git a/docs/clients/oschina.md b/docs/clients/oschina.md new file mode 100644 index 0000000..38785ed --- /dev/null +++ b/docs/clients/oschina.md @@ -0,0 +1,9 @@ +## 注册账号 + +地址:[oschina](https://www.oschina.net/) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +登录已注册的账号... diff --git a/docs/clients/qq.md b/docs/clients/qq.md new file mode 100644 index 0000000..adfb474 --- /dev/null +++ b/docs/clients/qq.md @@ -0,0 +1,5 @@ +## 创建第三方应用 + +登录QQ互联平台:[QQ](https://connect.qq.com/) + +... diff --git a/docs/clients/wechat_mp.md b/docs/clients/wechat_mp.md new file mode 100644 index 0000000..036917c --- /dev/null +++ b/docs/clients/wechat_mp.md @@ -0,0 +1,5 @@ +## 创建第三方应用 + +登录微信小程序控制台:[mp.weixin](https://mp.weixin.qq.com/) + +... diff --git a/docs/clients/wechat_open.md b/docs/clients/wechat_open.md new file mode 100644 index 0000000..e2c7bee --- /dev/null +++ b/docs/clients/wechat_open.md @@ -0,0 +1,9 @@ +## 注册账号 + +地址:[open.weixin](https://open.weixin.qq.com/) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +登录已注册的账号... diff --git a/docs/clients/wechat_work.md b/docs/clients/wechat_work.md new file mode 100644 index 0000000..f0cd6d2 --- /dev/null +++ b/docs/clients/wechat_work.md @@ -0,0 +1,5 @@ +## 创建第三方应用 + +登录微信企业版控制台:[work.weixin](https://work.weixin.qq.com/wework_admin/loginpage_wx?from=myhome_openApi) + +... diff --git a/docs/clients/weibo.md b/docs/clients/weibo.md new file mode 100644 index 0000000..c5288bc --- /dev/null +++ b/docs/clients/weibo.md @@ -0,0 +1,9 @@ +## 注册账号 + +地址:[weibo](https://open.weibo.com/apps) + +如果已有则忽略该步骤,直接进入第二步 + +## 创建第三方应用 + +登录已注册的账号... diff --git a/docs/explanation.md b/docs/explanation.md index 6096c2a..8ba6c61 100644 --- a/docs/explanation.md +++ b/docs/explanation.md @@ -1 +1,25 @@ -TODO... +!!! note + + - `开发者` 指使用JustAuth的开发者 + - `第三方` 指开发者对接的第三方客户端,比如:Google、GitHub、Gitee 等 + - `用户` 指最终服务的真实用户 + +- ==client_id== 客户端身份标识符(应用id),一般在申请完 `OAuth` 应用后,由第三方客户端颁发 +- ==client_secret== 客户端密钥,一般在申请完 `OAuth` 应用后,由第三方客户端颁发 +- ==redirect_uri== 开发者项目中真实有效的 API 地址。用户在确认第三方客户端授权(登录)后,第三方客户端会自动重定向到该地址,并携带 + code 等参数 +- ==state== 用于在请求和回调之间维护状态,主要用于防止跨站请求伪造(CSRF)攻击 +- ==source== 支持的第三方客户端,比如:GitHub、LinuxDo 等 +- ==sid== 第三方客户端的用户 ID。以下是关于各平台的 sid 存储逻辑: + +!!! warning + + 建议通过 `sid` + `source` 的方式确定唯一用户,这样可以解决用户身份归属的问题。因为 单个用户ID + 在某一平台中是唯一的,但不能保证在所有平台中都是唯一的。 + +## 参考资料 + +关于 OAuth2 相关的内容、原理可以自行参阅以下资料: + +- [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749) +- [OAuth 2.0](https://oauth.net/2/) diff --git a/docs/fastapi.md b/docs/fastapi.md new file mode 100644 index 0000000..57055b7 --- /dev/null +++ b/docs/fastapi.md @@ -0,0 +1,46 @@ +提供的实用程序可简化 FastAPI 中 OAuth2 流程的集成 + +## `FastAPIOAuth20` + +依赖关系可调用,用于处理授权回调,它读取查询参数并返回访问令牌和状态 + +```python +from fastapi import FastAPI, Depends +from fastapi_oauth20 import FastAPIOAuth20, LinuxDoOAuth20 + +client = LinuxDoOAuth20("CLIENT_ID", "CLIENT_SECRET") +linuxdo_oauth2_callback = FastAPIOAuth20(client, "oauth2-callback") + +app = FastAPI() + + +@app.get("/oauth2-callback", name="oauth-callback") +async def oauth2_callback(access_token_state=Depends(linuxdo_oauth2_callback)): + token, state = access_token_state + # Do something useful +``` + +## 自定义异常 + +如果回调逻辑内部发生错误(用户拒绝访问、授权代码无效......),依赖关系将引发 `OAuth20AuthorizeCallbackError` 错误 + +它继承自 FastAPI 的 [HTTPException](https://fastapi.tiangolo.com/reference/exceptions/#fastapi.HTTPException),因此默认的 +FastAPI 异常处理程序会自动对其进行处理。您可以通过为 `OAuth20AuthorizeCallbackError` 实现自己的异常处理程序来自定义此行为 + +```python +from fastapi import FastAPI, Request +from fastapi.responses import JSONResponse +from fastapi_oauth20.integrations.fastapi import OAuth20AuthorizeCallbackError + +app = FastAPI() + + +@app.exception_handler(OAuth20AuthorizeCallbackError) +async def oauth2_authorize_callback_error_handler(request: Request, exc: OAuth20AuthorizeCallbackError): + detail = exc.detail + status_code = exc.status_code + return JSONResponse( + status_code=status_code, + content={"message": "The OAuth2 callback failed", "detail": detail}, + ) +``` diff --git a/docs/index.md b/docs/index.md index c2b25f3..f435039 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,7 +16,7 @@ 我们的目标是集成多个 CN 第三方客户端,敬请期待(🐦)... -你可以在 [集成状态](integration.md) 获取当前集成情况 +你可以在 [客户端状态](status.md) 获取当前集成情况 ## 互动 diff --git a/docs/install.md b/docs/install.md index 9407eff..e39d606 100644 --- a/docs/install.md +++ b/docs/install.md @@ -7,20 +7,20 @@ ## 安装 -=== ":simple-piped: pip" +=== ":simple-python: pip" ```sh pip install fastapi-oauth20 ``` -=== ":simple-pdm: pdm" +=== ":simple-uv: uv" ```sh - pdm add fastapi-oauth20 + uv add fastapi-oauth20 ``` -=== ":simple-uv: uv" +=== ":simple-pdm: pdm" ```sh - uv add fastapi-oauth20 + pdm add fastapi-oauth20 ``` diff --git a/docs/integration.md b/docs/integration.md deleted file mode 100644 index 172f48e..0000000 --- a/docs/integration.md +++ /dev/null @@ -1,21 +0,0 @@ -如果你有更多需求,请在仓库内创建 Issues,我们将尽力完成所有目标 - -- [x] 表示已集成 -- [ ] 表示待集成 - ---- - -- [x] [Linux Do](https://connect.linux.do/) -- [x] [GitHub](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app) -- [x] [Gitee](https://gitee.com/api/v5/oauth_doc#/) -- [x] [开源中国](https://www.oschina.net/openapi) -- [ ] [微信小程序](https://mp.weixin.qq.com/) -- [ ] [微信开放平台](https://open.weixin.qq.com/) -- [ ] [企业微信二维码登录](https://work.weixin.qq.com/wework_admin/loginpage_wx?from=myhome_openApi) -- [x] [飞书](https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/web-app-overview) -- [ ] [钉钉](https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information) -- [ ] [阿里云](https://help.aliyun.com/zh/ram/user-guide/access-alibaba-cloud-apis-from-a-web-application?spm=a2c4g.11186623.0.i2) -- [ ] [QQ](https://connect.qq.com/) -- [ ] [微博](https://open.weibo.com/apps) -- [ ] [抖音](https://open.douyin.com/) -- [x] [Google](https://developers.google.cn/identity/protocols/oauth2/javascript-implicit-flow?hl=en) diff --git a/docs/public/images/github/dev.png b/docs/public/images/github/dev.png new file mode 100644 index 0000000..ad29ac9 Binary files /dev/null and b/docs/public/images/github/dev.png differ diff --git a/docs/public/images/github/oauth.png b/docs/public/images/github/oauth.png new file mode 100644 index 0000000..41e5ef0 Binary files /dev/null and b/docs/public/images/github/oauth.png differ diff --git a/docs/public/images/github/register.png b/docs/public/images/github/register.png new file mode 100644 index 0000000..2cddd97 Binary files /dev/null and b/docs/public/images/github/register.png differ diff --git a/docs/public/images/github/secrets.png b/docs/public/images/github/secrets.png new file mode 100644 index 0000000..72d0963 Binary files /dev/null and b/docs/public/images/github/secrets.png differ diff --git a/docs/public/images/github/settings.png b/docs/public/images/github/settings.png new file mode 100644 index 0000000..02c2ad8 Binary files /dev/null and b/docs/public/images/github/settings.png differ diff --git a/docs/public/images/linuxdo/connect.png b/docs/public/images/linuxdo/connect.png new file mode 100644 index 0000000..ce7c6db Binary files /dev/null and b/docs/public/images/linuxdo/connect.png differ diff --git a/docs/public/images/linuxdo/dev.png b/docs/public/images/linuxdo/dev.png new file mode 100644 index 0000000..abfc948 Binary files /dev/null and b/docs/public/images/linuxdo/dev.png differ diff --git a/docs/public/images/linuxdo/new.png b/docs/public/images/linuxdo/new.png new file mode 100644 index 0000000..dcd3267 Binary files /dev/null and b/docs/public/images/linuxdo/new.png differ diff --git a/docs/public/images/linuxdo/save.png b/docs/public/images/linuxdo/save.png new file mode 100644 index 0000000..08fa00f Binary files /dev/null and b/docs/public/images/linuxdo/save.png differ diff --git a/docs/public/images/linuxdo/secrets.png b/docs/public/images/linuxdo/secrets.png new file mode 100644 index 0000000..cec7dd8 Binary files /dev/null and b/docs/public/images/linuxdo/secrets.png differ diff --git a/docs/status.md b/docs/status.md new file mode 100644 index 0000000..c5a36c1 --- /dev/null +++ b/docs/status.md @@ -0,0 +1,24 @@ +下面展示了我们的计划,如果你有更多需求,请在仓库内创建 Issues,我们将尽力完成所有目标 + +!!! danger + + 对于强制要求【实名 + 人脸认证】的平台,植入变得困难,所以它们不会很快到来 + +## END + +- [x] [LinuxDo](clients/linuxdo.md) +- [x] [GitHub](clients/github.md) +- [x] [Gitee](clients/gitee.md) +- [x] [开源中国](clients/oschina.md) +- [x] [飞书](clients/feishu.md) +- [x] [Google](clients/google.md) + +## TODO + +- [ ] [微信小程序](clients/wechat_open) +- [ ] [微信开放平台](clients/wechat_mp) +- [ ] [企业微信二维码登录](clients/wechat_work) +- [ ] [钉钉](clients/dingding.md) +- [ ] [QQ](clients/qq.md) +- [ ] [微博](clients/weibo.md) +- [ ] [抖音](clients/douyin.md) diff --git a/mkdocs.yml b/mkdocs.yml index 8b696b7..795fb60 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,14 +6,29 @@ site_author: Wu Clan repo_name: fastapi-oauth20 repo_url: https://github.com/wu-clan/fastapi-oauth20 nav: - - Home: - - 主页: index.md - - 集成状态: integration.md - - Install: install.md - - Explanation: explanation.md - - Usage: usage.md - - Advanced: advanced.md - - Changelog: changelog.md + - 主页: index.md + - 安装: install.md + - 名词解释: explanation.md + - 用法: usage.md + - 高级用法: advanced.md + - 集成: + - FastAPI: fastapi.md + - 客户端状态: status.md + - 客户端申请: + - LinuxDo: clients/linuxdo.md + - GitHub: clients/github.md + - Gitee: clients/gitee.md + - 开源中国: clients/oschina.md + - 微信小程序: clients/wechat_open.md + - 微信开放平台: clients/wechat_mp.md + - 企业微信二维码登录: clients/wechat_work.md + - 飞书: clients/feishu.md + - 钉钉: clients/dingding.md + - QQ: clients/qq.md + - 微博: clients/weibo.md + - 抖音: clients/douyin.md + - Google: clients/google.md + - 变更日志: changelog.md theme: name: material font: @@ -42,7 +57,6 @@ theme: - navigation.instant.progress - navigation.path - navigation.tracking - - navigation.tabs - navigation.tabs.sticky - navigation.top - navigation.footer @@ -50,6 +64,7 @@ theme: - toc.follow plugins: - search + - glightbox markdown_extensions: - toc: permalink: true @@ -58,9 +73,17 @@ markdown_extensions: - attr_list - def_list - md_in_html - - pymdownx.snippets + - admonition + - pymdownx.details - pymdownx.superfences + - pymdownx.snippets - pymdownx.inlinehilite + - pymdownx.critic + - pymdownx.caret + - pymdownx.keys + - pymdownx.mark + - pymdownx.tilde + - pymdownx.blocks.caption - pymdownx.highlight: anchor_linenums: true line_spans: __span diff --git a/pdm.lock b/pdm.lock index 5d61b6c..55d1f4b 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "lint"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:2fa2e086cddb12e69177b0912ae3ab7df1ce946ea65d3c6e871932ab162bcc8f" +content_hash = "sha256:7ea94b4a905c3ab9894b7e3c242aacc6227cf36ad2f36b916a863dcba8159b5e" [[metadata.targets]] requires_python = ">=3.10" @@ -46,7 +46,7 @@ name = "certifi" version = "2025.1.31" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -129,7 +129,7 @@ name = "h11" version = "0.14.0" requires_python = ">=3.7" summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "typing-extensions; python_version < \"3.8\"", ] @@ -143,7 +143,7 @@ name = "httpcore" version = "1.0.7" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "certifi", "h11<0.15,>=0.13", @@ -158,7 +158,7 @@ name = "httpx" version = "0.28.1" requires_python = ">=3.8" summary = "The next generation HTTP client." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "anyio", "certifi", @@ -437,6 +437,20 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "respx" +version = "0.22.0" +requires_python = ">=3.8" +summary = "A utility for mocking out the Python HTTPX and HTTP Core libraries." +groups = ["dev"] +dependencies = [ + "httpx>=0.25.0", +] +files = [ + {file = "respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0"}, + {file = "respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91"}, +] + [[package]] name = "ruff" version = "0.9.10" diff --git a/pyproject.toml b/pyproject.toml index 6d26c04..7425dfb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dev = [ "pytest>=8.1.1", "pytest-asyncio>=0.23.6", "fastapi>=0.100.0", + "respx>=0.22.0", ] lint = [ "ruff>=0.9.9", diff --git a/requirements_docs.txt b/requirements_docs.txt index a946676..70b63c1 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -1 +1,2 @@ mkdocs-material==9.6.7 +mkdocs-glightbox==0.4.0