Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions backend/app/auth/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import logging

from fastapi import Depends, HTTPException
from google.auth.exceptions import RefreshError
from sqlalchemy.ext.asyncio import AsyncSession

from app.auth.service import build_credentials
Expand Down Expand Up @@ -40,13 +41,16 @@ async def get_google_user(
await asyncio.to_thread(credentials.refresh, Request())
user.google_oauth_token = encrypt_value(credentials.token)
await db.commit()
except Exception as exc:
except RefreshError as exc:
logger.warning(f"Google 토큰 갱신 실패 (user={user.id}): {exc}")
raise HTTPException(
status_code=401,
detail={
"code": "token_expired",
"message": "Google 토큰이 만료되었습니다. 다시 로그인해주세요.",
"message": (
"Google 인증 정보가 만료되었거나 취소되었습니다. "
"다시 로그인해야 합니다."
),
},
) from exc

Expand Down
1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ requires-python = ">=3.13"
dependencies = [
"fastapi[standard]",
"sqlalchemy",
"greenlet",
"aiosqlite",
"python-dotenv",
"httpx",
Expand Down
49 changes: 49 additions & 0 deletions backend/tests/test_auth_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from __future__ import annotations

from types import SimpleNamespace

import pytest
from fastapi import HTTPException
from google.auth.exceptions import RefreshError

from app.auth.dependencies import get_google_user


class _FailingCredentials:
expired = True
refresh_token = "refresh-token"
token = "stale-token"

def refresh(self, _request) -> None:
raise RefreshError("refresh token revoked")


class _UnusedDbSession:
async def commit(self) -> None:
raise AssertionError("commit should not run when refresh fails")


@pytest.mark.asyncio
async def test_get_google_user_returns_401_on_refresh_error(monkeypatch):
user = SimpleNamespace(
id=123,
google_oauth_token="encrypted-token",
google_refresh_token="encrypted-refresh-token",
)

async def fake_to_thread(func, *args, **kwargs):
return func(*args, **kwargs)

monkeypatch.setattr("app.auth.dependencies.decrypt_value", lambda value: value)
monkeypatch.setattr(
"app.auth.dependencies.build_credentials",
lambda token, refresh_token: _FailingCredentials(),
)
monkeypatch.setattr("app.auth.dependencies.asyncio.to_thread", fake_to_thread)

with pytest.raises(HTTPException) as exc_info:
await get_google_user(user=user, db=_UnusedDbSession())

assert exc_info.value.status_code == 401
assert exc_info.value.detail["code"] == "token_expired"
assert "다시 로그인" in exc_info.value.detail["message"]
5 changes: 5 additions & 0 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading