diff --git a/app/routers/auth.py b/app/routers/auth.py
index 67e70f8..52c4ec4 100644
--- a/app/routers/auth.py
+++ b/app/routers/auth.py
@@ -115,6 +115,9 @@ async def login(request: Request, provider: str = Query("gitea")):
state = secrets.token_hex(32)
request.session["oauth_state"] = state
request.session["oauth_provider"] = provider
+ # Link mode: connect OAuth to current local account instead of creating new user
+ mode = request.query_params.get("mode", "")
+ request.session["oauth_mode"] = mode
# Dynamic redirect URI based on incoming Host header
host = request.headers.get("host", "localhost:8080")
dynamic_redirect_uri = f"http://{host}/auth/callback"
@@ -256,6 +259,24 @@ async def callback(
if not oauth_user:
return HTMLResponse("
Failed to get user
", status_code=400)
+ # Link mode: connect OAuth to current session user (for Settings → Integrations)
+ oauth_mode = request.session.pop("oauth_mode", "")
+ if oauth_mode == "link":
+ from app.auth.session import get_current_user as gcu
+ current = await gcu(request)
+ if not current:
+ return HTMLResponse("Not logged in — please log in first
", status_code=400)
+ from app.db import get_conn as _gc
+ with _gc() as conn:
+ conn.execute(
+ """INSERT OR REPLACE INTO user_oauth_tokens
+ (user_id, provider, access_token, refresh_token, expires_at, updated_at)
+ VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)""",
+ (current["id"], provider_name, access_token, token_data.get("refresh_token"), token_data.get("expires_at")),
+ )
+ conn.commit()
+ return RedirectResponse(url="/settings#integrations", status_code=302)
+
# Store user in DB
from app.db import get_conn
login_id = f"{provider_name}_{oauth_user['login']}"
diff --git a/app/templates/settings.html b/app/templates/settings.html
index 3a2e20d..e0756af 100644
--- a/app/templates/settings.html
+++ b/app/templates/settings.html
@@ -16,7 +16,7 @@
{% block content %}