OAuth流程类
| 168 | |
| 169 | |
| 170 | class Flow: |
| 171 | """OAuth流程类""" |
| 172 | |
| 173 | def __init__( |
| 174 | self, client_id: str, client_secret: str, scopes: List[str], redirect_uri: str = None |
| 175 | ): |
| 176 | self.client_id = client_id |
| 177 | self.client_secret = client_secret |
| 178 | self.scopes = scopes |
| 179 | self.redirect_uri = redirect_uri |
| 180 | |
| 181 | # 反代配置将在使用时异步获取 |
| 182 | self.oauth_base_url = None |
| 183 | self.token_endpoint = None |
| 184 | self.auth_endpoint = "https://accounts.google.com/o/oauth2/auth" |
| 185 | |
| 186 | self.credentials: Optional[Credentials] = None |
| 187 | |
| 188 | def get_auth_url(self, state: str = None, **kwargs) -> str: |
| 189 | """生成授权URL""" |
| 190 | params = { |
| 191 | "client_id": self.client_id, |
| 192 | "redirect_uri": self.redirect_uri, |
| 193 | "scope": " ".join(self.scopes), |
| 194 | "response_type": "code", |
| 195 | "access_type": "offline", |
| 196 | "prompt": "consent", |
| 197 | "include_granted_scopes": "true", |
| 198 | } |
| 199 | |
| 200 | if state: |
| 201 | params["state"] = state |
| 202 | |
| 203 | params.update(kwargs) |
| 204 | return f"{self.auth_endpoint}?{urlencode(params)}" |
| 205 | |
| 206 | async def exchange_code(self, code: str) -> Credentials: |
| 207 | """用授权码换取token""" |
| 208 | data = { |
| 209 | "client_id": self.client_id, |
| 210 | "client_secret": self.client_secret, |
| 211 | "redirect_uri": self.redirect_uri, |
| 212 | "code": code, |
| 213 | "grant_type": "authorization_code", |
| 214 | } |
| 215 | |
| 216 | try: |
| 217 | oauth_base_url = await get_oauth_proxy_url() |
| 218 | token_url = f"{oauth_base_url.rstrip('/')}/token" |
| 219 | response = await post_async( |
| 220 | token_url, data=data, headers={"Content-Type": "application/x-www-form-urlencoded"} |
| 221 | ) |
| 222 | response.raise_for_status() |
| 223 | |
| 224 | token_data = response.json() |
| 225 | |
| 226 | # 计算过期时间 |
| 227 | expires_at = None |