diff --git a/backend/app/api/endpoints/oauth.py b/backend/app/api/endpoints/oauth.py index ba81d2f0..cbc5aedf 100644 --- a/backend/app/api/endpoints/oauth.py +++ b/backend/app/api/endpoints/oauth.py @@ -104,3 +104,20 @@ async def google_callback( refresh_token=create_refresh_token({"sub": str(user.id)}), is_new_user=is_new, ) + + +@router.post("/microsoft/callback", response_model=OAuthCallbackResponse) +async def microsoft_callback( + payload: OAuthCallbackPayload, + db: Annotated[AsyncSession, Depends(get_admin_db)], +) -> OAuthCallbackResponse: + if not settings.MS_CLIENT_ID: + raise HTTPException(status_code=503, detail="Microsoft sign-in not configured") + redirect_uri = f"{settings.OAUTH_REDIRECT_BASE}/auth/microsoft/callback" + profile = await microsoft_exchange_code(payload.code, redirect_uri) + user, is_new = await _sign_in_or_register(db, "microsoft", profile) + return OAuthCallbackResponse( + access_token=create_access_token({"sub": str(user.id)}), + refresh_token=create_refresh_token({"sub": str(user.id)}), + is_new_user=is_new, + ) diff --git a/backend/tests/test_oauth_callbacks.py b/backend/tests/test_oauth_callbacks.py index 41064214..f31e1688 100644 --- a/backend/tests/test_oauth_callbacks.py +++ b/backend/tests/test_oauth_callbacks.py @@ -93,3 +93,28 @@ async def test_google_callback_503_when_unconfigured(client, monkeypatch): "/api/v1/auth/google/callback", json={"code": "x"} ) assert response.status_code == 503 + + +@pytest.mark.asyncio +async def test_microsoft_callback_creates_user(client, test_db, monkeypatch): + from app.core.config import settings + monkeypatch.setattr(settings, "MS_CLIENT_ID", "client_dummy") + monkeypatch.setattr(settings, "MS_CLIENT_SECRET", "secret_dummy") + + profile = OAuthProfile( + provider_subject="ms_subject_789", + email="msuser@example.com", + name="MS User", + ) + with patch("app.api.endpoints.oauth.microsoft_exchange_code", return_value=profile): + response = await client.post( + "/api/v1/auth/microsoft/callback", json={"code": "auth_code"} + ) + assert response.status_code == 200, response.json() + user = (await test_db.execute( + select(User).where(User.email == "msuser@example.com") + )).scalar_one() + identity = (await test_db.execute( + select(OAuthIdentity).where(OAuthIdentity.user_id == user.id) + )).scalar_one() + assert identity.provider == "microsoft"