| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- import logging
- import time
- from typing import Any
- import httpx
- import typer
- from pydantic import BaseModel
- from fastapi_cloud_cli.config import Settings
- from fastapi_cloud_cli.utils.api import APIClient
- from fastapi_cloud_cli.utils.auth import AuthConfig, write_auth_config
- from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
- logger = logging.getLogger(__name__)
- class AuthorizationData(BaseModel):
- user_code: str
- device_code: str
- verification_uri: str
- verification_uri_complete: str
- interval: int = 5
- class TokenResponse(BaseModel):
- access_token: str
- def _start_device_authorization(
- client: httpx.Client,
- ) -> AuthorizationData:
- settings = Settings.get()
- response = client.post(
- "/login/device/authorization", data={"client_id": settings.client_id}
- )
- response.raise_for_status()
- return AuthorizationData.model_validate(response.json())
- def _fetch_access_token(client: httpx.Client, device_code: str, interval: int) -> str:
- settings = Settings.get()
- while True:
- response = client.post(
- "/login/device/token",
- data={
- "device_code": device_code,
- "client_id": settings.client_id,
- "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
- },
- )
- if response.status_code not in (200, 400):
- response.raise_for_status()
- if response.status_code == 400:
- data = response.json()
- if data.get("error") != "authorization_pending":
- response.raise_for_status()
- if response.status_code == 200:
- break
- time.sleep(interval)
- response_data = TokenResponse.model_validate(response.json())
- return response_data.access_token
- def login() -> Any:
- """
- Login to FastAPI Cloud. 🚀
- """
- with get_rich_toolkit() as toolkit, APIClient() as client:
- toolkit.print_title("Login to FastAPI Cloud", tag="FastAPI")
- toolkit.print_line()
- with toolkit.progress("Starting authorization") as progress:
- with handle_http_errors(progress):
- authorization_data = _start_device_authorization(client)
- url = authorization_data.verification_uri_complete
- progress.log(f"Opening [link={url}]{url}[/link]")
- toolkit.print_line()
- with toolkit.progress("Waiting for user to authorize...") as progress:
- typer.launch(url)
- with handle_http_errors(progress):
- access_token = _fetch_access_token(
- client, authorization_data.device_code, authorization_data.interval
- )
- write_auth_config(AuthConfig(access_token=access_token))
- progress.log("Now you are logged in! 🚀")
|