from __future__ import annotations from typing import Any class WeChatAuthAdapter: def get_dm_exposure(self, config: dict[str, Any]) -> str: return config.get("dm_policy", "pairing") def get_authorization_url(self, config: dict[str, Any]) -> str | None: if config.get("wecom_auth_url"): return config["wecom_auth_url"] return None def build_wecom_oauth_url( self, corp_id: str, redirect_uri: str, state: str = "", scope: str = "snsapi_base", ) -> str: params = { "appid": corp_id, "redirect_uri": redirect_uri, "response_type": "code", "scope": scope, } query = "&".join(f"{k}={v}" for k, v in params.items()) url = f"https://open.weixin.qq.com/connect/oauth2/authorize?{query}" if state: url += f"&state={state}" url += "#wechat_redirect" return url def build_mp_oauth_url( self, app_id: str, redirect_uri: str, state: str = "", scope: str = "snsapi_userinfo", ) -> str: params = { "appid": app_id, "redirect_uri": redirect_uri, "response_type": "code", "scope": scope, } query = "&".join(f"{k}={v}" for k, v in params.items()) url = f"https://open.weixin.qq.com/connect/oauth2/authorize?{query}" if state: url += f"&state={state}" url += "#wechat_redirect" return url async def exchange_wecom_code(self, code: str, corp_id: str, corp_secret: str) -> dict[str, Any]: import httpx access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken" async with httpx.AsyncClient() as client: token_resp = await client.get( access_token_url, params={"corpid": corp_id, "corpsecret": corp_secret}, ) token_data = token_resp.json() access_token = token_data.get("access_token", "") if not access_token: return {"success": False, "error": f"Failed to get access token: {token_data}"} userinfo_url = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo" user_resp = await client.get( userinfo_url, params={"access_token": access_token, "code": code}, ) user_data = user_resp.json() if user_data.get("errcode") == 0: return {"success": True, "user_info": user_data, "access_token": access_token} return {"success": False, "error": str(user_data)} async def exchange_mp_code(self, code: str, app_id: str, app_secret: str) -> dict[str, Any]: import httpx url = "https://api.weixin.qq.com/sns/oauth2/access_token" async with httpx.AsyncClient() as client: resp = await client.get( url, params={ "appid": app_id, "secret": app_secret, "code": code, "grant_type": "authorization_code", }, ) data = resp.json() if "access_token" in data and "openid" in data: userinfo_url = "https://api.weixin.qq.com/sns/userinfo" user_resp = await client.get( userinfo_url, params={ "access_token": data["access_token"], "openid": data["openid"], "lang": "zh_CN", }, ) user_data = user_resp.json() return { "success": True, "user_info": user_data, "access_token": data["access_token"], "refresh_token": data.get("refresh_token"), "openid": data["openid"], } return {"success": False, "error": str(data)} def get_oauth_enabled(self, config: dict[str, Any]) -> bool: return bool(config.get("wecom_oauth_enabled", False) or config.get("mp_oauth_enabled", False))