Spaces:
Runtime error
Runtime error
| import os | |
| import json | |
| from google_auth_oauthlib.flow import Flow | |
| from google.oauth2.credentials import Credentials | |
| from google.auth.transport.requests import Request | |
| from dotenv import load_dotenv | |
| SCOPES = [ | |
| "https://www.googleapis.com/auth/drive.readonly", | |
| "https://www.googleapis.com/auth/userinfo.email", | |
| "openid", | |
| ] | |
| oauth_pkce_store: dict[str, str] = {} | |
| load_dotenv() | |
| CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID", "") | |
| CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET", "") | |
| REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI", "") | |
| def _client_config() -> dict: | |
| """Builds the client config dict that google_auth_oauthlib expects.""" | |
| return { | |
| "web": { | |
| "client_id": CLIENT_ID, | |
| "client_secret": CLIENT_SECRET, | |
| "redirect_uris": [REDIRECT_URI], | |
| "auth_uri": "https://accounts.google.com/o/oauth2/auth", | |
| "token_uri": "https://oauth2.googleapis.com/token", | |
| } | |
| } | |
| def get_auth_url(state: str | None = None) -> str: | |
| """ | |
| Returns the Google OAuth consent-screen URL to redirect the user to. | |
| `state` can carry any context you want back in the callback (e.g. user_email). | |
| """ | |
| flow = Flow.from_client_config(_client_config(), scopes=SCOPES) | |
| flow.redirect_uri = REDIRECT_URI | |
| auth_url, returned_state = flow.authorization_url( | |
| access_type="offline", # get refresh_token | |
| include_granted_scopes="true", | |
| prompt="consent", # force refresh_token every time during dev | |
| state=state or "", | |
| ) | |
| oauth_pkce_store[returned_state] = flow.code_verifier | |
| print(">>> Stored PKCE verifier for state:", returned_state) | |
| return auth_url | |
| def exchange_code_for_token(code: str, state: str) -> dict: | |
| """ | |
| Exchanges an authorization code (from the OAuth callback) for credentials. | |
| Returns a JSON-serialisable token dict. | |
| """ | |
| flow = Flow.from_client_config(_client_config(), scopes=SCOPES) | |
| flow.redirect_uri = REDIRECT_URI | |
| code_verifier = oauth_pkce_store.get(state) | |
| print(">>> Retrieved verifier:", code_verifier) | |
| flow.code_verifier = code_verifier | |
| flow.fetch_token(code=code) | |
| creds = flow.credentials | |
| return _creds_to_dict(creds) | |
| def credentials_from_token_dict(token_dict: dict) -> Credentials: | |
| """ | |
| Re-hydrates a Credentials object from a stored token dict, | |
| refreshing automatically if the access token is expired. | |
| """ | |
| creds = Credentials( | |
| token=token_dict.get("token"), | |
| refresh_token=token_dict.get("refresh_token"), | |
| token_uri="https://oauth2.googleapis.com/token", | |
| client_id=CLIENT_ID, | |
| client_secret=CLIENT_SECRET, | |
| scopes=token_dict.get("scopes", SCOPES), | |
| ) | |
| if creds.expired and creds.refresh_token: | |
| creds.refresh(Request()) | |
| return creds | |
| def _creds_to_dict(creds: Credentials) -> dict: | |
| return { | |
| "token": creds.token, | |
| "refresh_token": creds.refresh_token, | |
| "token_uri": creds.token_uri, | |
| "client_id": creds.client_id, | |
| "client_secret": creds.client_secret, | |
| "scopes": list(creds.scopes or SCOPES), | |
| "expiry": creds.expiry.isoformat() if creds.expiry else None, | |
| } | |