Files

87 lines
3.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""GitHub OAuth 授权码模式端点(SPA 友好的 JSON 流程)。
流程:前端调 authorize 拿 authorize_url → 浏览器跳转 GitHub →
GitHub 回跳前端页(带 code+state)→ 前端调 callback 换平台 token。
"""
import logging
import secrets
from urllib.parse import urlencode
from chongming_worker import rpc_method
from ..models import GithubAuthorizeGetResponse, GithubCallbackPostRequest, LoginPostResponse
from ..providers import github as github_provider
from .. import store
logger = logging.getLogger("plugins.auth_oauth.github")
_STATE_TTL = 300 # state 有效期(秒),一次性消费防 CSRF
@rpc_method(
subject="auth_oauth.github_authorize",
method="GET",
summary="GitHub 授权登录:生成授权跳转 URL(state 存缓存防 CSRF)",
)
async def github_authorize() -> GithubAuthorizeGetResponse:
from ..global_var import cache, plugin_config
gh = plugin_config.get("github") or {}
client_id = gh.get("client_id", "")
if not client_id or client_id.startswith("your-"):
return GithubAuthorizeGetResponse(
result="fail", authorize_url="",
state="GitHub OAuth 未配置(config/workers/auth_oauth/config.toml 的 [workers.config.github])",
)
state = secrets.token_urlsafe(24)
cache.set(f"auth_oauth:state:{state}", "1", expire=_STATE_TTL)
params = urlencode({
"client_id": client_id,
"redirect_uri": gh.get("redirect_uri", ""),
"scope": gh.get("scope", "read:user user:email"),
"state": state,
})
return GithubAuthorizeGetResponse(
result="success",
authorize_url=f"https://github.com/login/oauth/authorize?{params}",
state=state,
)
@rpc_method(
subject="auth_oauth.github_callback",
method="POST",
summary="GitHub 授权回调:校验 state、换取用户信息,find-or-create 后签发平台 token",
)
async def github_callback(req: GithubCallbackPostRequest) -> LoginPostResponse:
from ..global_var import cache, db, plugin_config
if db is None or cache is None:
return store.fail_response("插件未初始化")
# state 一次性消费(防 CSRF / 重放)
state_key = f"auth_oauth:state:{req.state}"
if not cache.get(state_key):
return store.fail_response("state 无效或已过期,请重新发起授权")
cache.delete(state_key)
gh = plugin_config.get("github") or {}
try:
access_token = await github_provider.exchange_code(
gh.get("client_id", ""), gh.get("client_secret", ""),
req.code, gh.get("redirect_uri", ""),
)
profile = await github_provider.fetch_user(access_token)
except RuntimeError as e:
logger.warning("GitHub 回调失败: %s", e)
return store.fail_response(str(e))
user, message = await store.find_or_create_user(
db, "github", profile["uid"], profile
)
if user is None:
return store.fail_response(message)
return store.issue_login_response(user)