Service Account类
| 247 | |
| 248 | |
| 249 | class ServiceAccount: |
| 250 | """Service Account类""" |
| 251 | |
| 252 | def __init__( |
| 253 | self, email: str, private_key: str, project_id: str = None, scopes: List[str] = None |
| 254 | ): |
| 255 | self.email = email |
| 256 | self.private_key = private_key |
| 257 | self.project_id = project_id |
| 258 | self.scopes = scopes or [] |
| 259 | |
| 260 | # 反代配置将在使用时异步获取 |
| 261 | self.oauth_base_url = None |
| 262 | self.token_endpoint = None |
| 263 | |
| 264 | self.access_token: Optional[str] = None |
| 265 | self.expires_at: Optional[datetime] = None |
| 266 | |
| 267 | def is_expired(self) -> bool: |
| 268 | """检查token是否过期""" |
| 269 | if not self.expires_at: |
| 270 | return True |
| 271 | |
| 272 | buffer = timedelta(minutes=3) |
| 273 | return (self.expires_at - buffer) <= datetime.now(timezone.utc) |
| 274 | |
| 275 | def create_jwt(self) -> str: |
| 276 | """创建JWT令牌""" |
| 277 | now = int(time.time()) |
| 278 | |
| 279 | payload = { |
| 280 | "iss": self.email, |
| 281 | "scope": " ".join(self.scopes) if self.scopes else "", |
| 282 | "aud": self.token_endpoint, |
| 283 | "exp": now + 3600, |
| 284 | "iat": now, |
| 285 | } |
| 286 | |
| 287 | return jwt.encode(payload, self.private_key, algorithm="RS256") |
| 288 | |
| 289 | async def get_access_token(self) -> str: |
| 290 | """获取访问令牌""" |
| 291 | if not self.is_expired() and self.access_token: |
| 292 | return self.access_token |
| 293 | |
| 294 | assertion = self.create_jwt() |
| 295 | |
| 296 | data = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": assertion} |
| 297 | |
| 298 | try: |
| 299 | oauth_base_url = await get_oauth_proxy_url() |
| 300 | token_url = f"{oauth_base_url.rstrip('/')}/token" |
| 301 | response = await post_async( |
| 302 | token_url, data=data, headers={"Content-Type": "application/x-www-form-urlencoded"} |
| 303 | ) |
| 304 | response.raise_for_status() |
| 305 | |
| 306 | token_data = response.json() |
nothing calls this directly
no outgoing calls
no test coverage detected