from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.schemas.user import TokenResponse, WecomLoginRequest, CasdoorLoginRequest, WecomBindRequest from app.services.auth import ( exchange_casdoor_code, get_or_create_user_from_casdoor, get_user_by_wecom_id, bind_wecom_user, build_token_for_user, ) from app.services.wecom import wecom_client from app.config import settings router = APIRouter(prefix="/auth", tags=["Authentication"]) @router.post("/casdoor-login", response_model=TokenResponse) async def casdoor_login(req: CasdoorLoginRequest, db: AsyncSession = Depends(get_db)): """Standard Casdoor OIDC login — exchange code for userinfo, get or create user, return JWT.""" userinfo = await exchange_casdoor_code(req.code) if not userinfo: raise HTTPException(status_code=400, detail="Failed to exchange code with Casdoor") casdoor_id = userinfo.get("sub") or userinfo.get("id") if not casdoor_id: raise HTTPException(status_code=400, detail="Invalid userinfo from Casdoor") name = userinfo.get("name") or userinfo.get("preferred_username") or casdoor_id role = userinfo.get("role", "manager") user = await get_or_create_user_from_casdoor(db, casdoor_id, name, role) token = build_token_for_user(user) return TokenResponse( access_token=token, user_id=str(user.id), name=user.name, role=user.role, ) @router.post("/wecom-login") async def wecom_login(req: WecomLoginRequest, db: AsyncSession = Depends(get_db)): """WeChat Work silent login — exchange wecom code for userid, find bound user, return JWT.""" userinfo = await wecom_client.get_userinfo_by_code(req.code) if not userinfo: raise HTTPException(status_code=400, detail="Failed to exchange wecom code") wecom_userid = userinfo.get("UserId") or userinfo.get("userid") if not wecom_userid: raise HTTPException(status_code=400, detail="Could not get userid from wecom") user = await get_user_by_wecom_id(db, wecom_userid) if user: token = build_token_for_user(user) return TokenResponse( access_token=token, user_id=str(user.id), name=user.name, role=user.role, ) # Not bound yet — return a redirect URL to Casdoor for binding casdoor_auth_url = ( f"{settings.CASDOOR_ENDPOINT}/login/oauth/authorize" f"?client_id={settings.CASDOOR_CLIENT_ID}" f"&response_type=code" f"&redirect_uri={settings.CORS_ORIGINS[0]}/bind-wecom" f"&scope=openid+profile" f"&state={wecom_userid}" ) return {"need_bind": True, "casdoor_url": casdoor_auth_url, "wecom_userid": wecom_userid} @router.post("/bind-wecom") async def bind_wecom(req: WecomBindRequest, db: AsyncSession = Depends(get_db)): """Bind Casdoor account with WeChat Work userid after OIDC redirect.""" userinfo = await exchange_casdoor_code(req.casdoor_code) if not userinfo: raise HTTPException(status_code=400, detail="Failed to exchange casdoor code") casdoor_id = userinfo.get("sub") or userinfo.get("id") wecom_userid = req.wecom_userid or userinfo.get("state", "") user = await bind_wecom_user(db, casdoor_id, wecom_userid) if not user: raise HTTPException(status_code=404, detail="User not found") token = build_token_for_user(user) return TokenResponse( access_token=token, user_id=str(user.id), name=user.name, role=user.role, )