feat(frontend): 로그인 토큰을 Bearer 로 들고 다니게 — Django 백엔드 연결

Django(OpenCode 중계) 백엔드는 토큰을 쿠키가 아니라 응답 body 로 주고, Tauri 앱은
서버와 origin 이 달라 쿠키가 안 붙는다. 기존 tokenProvider seam 에 refresh 토큰과
localStorage 영속을 얹고, client/sse 의 refresh 를 body {refreshToken} 방식으로 바꿈.
로그인 때 저장하고 로그아웃 때 비움. 토큰 없으면 예전 쿠키 모드 그대로.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 21:24:16 +09:00
co-authored by Claude Fable 5.1
parent 858ee9e9da
commit 54f75aa089
9 changed files with 247 additions and 30 deletions
+34 -3
View File
@@ -1,13 +1,22 @@
import { describe, it, expect, afterEach } from "vitest"
import { getAccessToken, setAccessToken } from "./tokenProvider"
import {
getAccessToken,
getAccessTokenExpMs,
getRefreshToken,
setAccessToken,
setTokens,
clearTokens,
} from "./tokenProvider"
afterEach(() => setAccessToken(null))
afterEach(() => clearTokens())
describe("tokenProvider", () => {
it("기본값은 null (쿠키 모드)", () => {
expect(getAccessToken()).toBeNull()
expect(getRefreshToken()).toBeNull()
expect(getAccessTokenExpMs()).toBeNull()
})
it("set 하면 그 토큰을 돌려준다", () => {
it("setAccessToken 하면 그 토큰을 돌려준다 (하위호환)", () => {
setAccessToken("abc")
expect(getAccessToken()).toBe("abc")
})
@@ -16,4 +25,26 @@ describe("tokenProvider", () => {
setAccessToken(null)
expect(getAccessToken()).toBeNull()
})
it("setTokens 는 access·refresh·만료를 저장하고 localStorage 에 영속", () => {
setTokens({ token: "a", refreshToken: "r", tokenExpirationTime: 1_700_000_000 })
expect(getAccessToken()).toBe("a")
expect(getRefreshToken()).toBe("r")
expect(getAccessTokenExpMs()).toBe(1_700_000_000_000)
expect(JSON.parse(localStorage.getItem("ca.tokens") ?? "{}")).toMatchObject({
token: "a",
refreshToken: "r",
})
})
it("clearTokens 는 메모리·localStorage 둘 다 비움", () => {
setTokens({ token: "a", refreshToken: "r" })
clearTokens()
expect(getAccessToken()).toBeNull()
expect(localStorage.getItem("ca.tokens")).toBeNull()
})
it("setAccessToken 은 refresh 토큰을 유지한다", () => {
setTokens({ token: "a", refreshToken: "r" })
setAccessToken("b")
expect(getAccessToken()).toBe("b")
expect(getRefreshToken()).toBe("r")
})
})