diff --git a/app/api/routes/auth.py b/app/api/routes/auth.py index de900d4..e0aa8fd 100644 --- a/app/api/routes/auth.py +++ b/app/api/routes/auth.py @@ -13,6 +13,7 @@ from __future__ import annotations import hashlib import time +import urllib.parse import uuid from datetime import datetime, timedelta, timezone from typing import Literal @@ -20,6 +21,7 @@ from typing import Literal import bcrypt from cryptography.fernet import Fernet from fastapi import APIRouter, Depends, HTTPException, status +from fastapi.responses import RedirectResponse from jose import jwt from pydantic import BaseModel from sqlalchemy import select @@ -304,6 +306,31 @@ class _OAuthCallbackRequest(BaseModel): # ── OAuth routes ────────────────────────────────────────────────────── +@router.get( + "/oauth/{provider}/web-callback", + summary="Web-facing OAuth redirect — bounces to the adiuvai:// deep link", + include_in_schema=False, +) +async def oauth_web_callback( + provider: Literal["google"], + code: str, + state: str, +) -> RedirectResponse: + """Google redirects here after user consent. + + This endpoint immediately redirects to the Electron deep-link URI so the + desktop app receives the authorization code. It is intentionally simple — + no state validation here (the Electron app + backend callback do that). + + Registered in Google Cloud Console as: + http://localhost:8000/api/v1/auth/oauth/google/web-callback (dev) + https://api.adiuvai.com/api/v1/auth/oauth/google/web-callback (prod) + """ + params = urllib.parse.urlencode({"code": code, "state": state, "provider": provider}) + deep_link = f"adiuvai://oauth/callback?{params}" + return RedirectResponse(url=deep_link, status_code=302) + + @router.get( "/oauth/{provider}/authorize", response_model=_OAuthAuthorizeResponse, diff --git a/app/config/settings.py b/app/config/settings.py index 8e09de8..4058fea 100644 --- a/app/config/settings.py +++ b/app/config/settings.py @@ -45,9 +45,12 @@ class Settings(BaseSettings): # Separate from GMAIL_CLIENT_ID/SECRET (which uses gmail.readonly scope). GOOGLE_AUTH_CLIENT_ID: str = "" GOOGLE_AUTH_CLIENT_SECRET: str = "" - # Deep-link URI registered in the Google Cloud Console for the desktop app. - # Must match the protocol registered in forge.config.ts. - OAUTH_REDIRECT_URI: str = "adiuvai://oauth/callback" + # The redirect URI registered in Google Cloud Console. + # Google redirects here after consent; this backend route then bounces to + # the adiuvai:// deep link so the Electron app receives the code. + # Dev: http://localhost:8000/api/v1/auth/oauth/google/web-callback + # Prod: https://api.adiuvai.com/api/v1/auth/oauth/google/web-callback + OAUTH_REDIRECT_URI: str = "http://localhost:8000/api/v1/auth/oauth/google/web-callback" # Fernet key (URL-safe base64, 32-byte key) for at-rest encryption of OAuth # tokens stored in cloud_agent_configs.oauth_token_encrypted.