curl์ 200์ธ๋ฐ Claude ์ปค๋ฅํฐ๋ง 401 โ ์ธ์ฆ ํค๋์ Bearer๊ฐ ๋น ์ ธ ์์๋ค
1. ์ํฉ
๋ด ํํ์ด์ง์ ๊ธ ์ด์์ ๋ฐ๋ ์๋ฒ๋ฅผ ๋ง๋ค๊ณ , ๊ทธ๊ฑธ Claude์ ์ปค์คํ
์ปค๋ฅํฐ๋ก ๋ถ์ด๋ ์ค์ด์๋ค. ์๋ฒ๋ /api/mcp ์ฃผ์ ํ๋๋ก MCP(๋๊ตฌ ๋ชฉ๋ก, ๋๊ตฌ ํธ์ถ)๋ฅผ ๋ฐ๋๋ค. ์ธ์ฆ์ Authorization: Bearer ํ ํฐ ํค๋๋ก ํ๊ณ , ์๋ฒ ํ๊ฒฝ๋ณ์์ ๋ฃ์ด ๋ ๊ฐ๊ณผ ๋น๊ตํ๋ค.
ํฐ๋ฏธ๋์์ curl๋ก ํ ์คํธํ๋ฉด ๋๊ตฌ ๋ชฉ๋ก์ด ์ ๋์๋ค. ๊ทธ๋ฐ๋ฐ Claude ์ปค๋ฅํฐ์์๋ ๊ณ์ "์ฐ๊ฒฐํ ์ ์์ต๋๋ค"๋ง ๋ด๋ค.
2. ๋งํ๋ ์ง์
์๋ฒ ๋ก๊ทธ์๋ 401์ด ์ฐํ๊ณ ์์๋ค. ๋๋ "ํค๋ฅผ ์๋ชป ๋ฃ์๋?" ํ๊ณ ๋ช ๋ฒ์ ํ์ธํ๋ค. ์๋ฒ ๊ฐ๊ณผ ์ปค๋ฅํฐ์ ๋ฃ์ ๊ฐ์ ๊ธ์ ํ๋ ์ ํ๋ฆฌ๊ณ ๋๊ฐ์๋ค. ๊ฐ์ ๊ฐ์ธ๋ฐ curl์ ๋๊ณ ์ปค๋ฅํฐ๋ ์ ๋๋ ๋ ํท๊ฐ๋ ธ๋ค.
๊ทธ๋์ ์๋ฒ์ ์ง๋จ ๋ก๊ทธ๋ฅผ ๋ฃ์๋ค. ํค๋๊ฐ ์๋์ง, ๊ธธ์ด๊ฐ ๋ช์ธ์ง, Bearer ์ ๋์ด๊ฐ ์๋์ง๋ง ์ฐ๋๋ก ํ๋ค.
console.warn("[mcp] ์ธ์ฆ ์คํจ", {
authorizationHeader: authorization ? "์์" : "์์",
bearerPrefix: /^Bearer\s+/i.test(authorization ?? ""),
valueLength: (authorization ?? "").length,
});
3. ์์ธ
๋ก๊ทธ๋ฅผ ๋ณด๋ ํค๋๋ ์๋๋ฐ Bearer ์ ๋์ด๊ฐ ์์๋ค. ์ปค๋ฅํฐ์ "์์ฒญ ํค๋" ์นธ์ ๋ฃ์ ๊ฐ์ ์
๋ ฅํ ๊ทธ๋๋ก ์ ์ก๋๋ค. ๋๋ ํ ํฐ ๊ฐ๋ง ๋ฃ์๊ณ , ๊ทธ๋์ ์๋ฒ์๋ Authorization: ํ ํฐ ํํ๋ก ๋์ฐฉํ๋ค. ๊ฐ์ ๊ฐ์์ง๋ง ํ์์ด ๋ฌ๋๋ ๊ฑฐ๋ค. curl์์๋ ๋ด๊ฐ Bearer ๋ฅผ ์ง์ ๋ถ์ฌ์ ๋ณด๋์ผ๋ ๋๋ ๊ฒ์ด๋ค.
์ปค๋ฅํฐ๋ ๋ง๋ ๋ค์ ์ค์ ์ ์์ ํ ์ ์์ด์, ๊ณ ์น๋ ค๋ฉด ์ง์ฐ๊ณ ๋ค์ ๋ง๋ค์ด์ผ ํ๋ค.
4. ํด๊ฒฐ
์ปค๋ฅํฐ๋ฅผ ๋ค์ ๋ง๋๋ ๋์ ์๋ฒ๊ฐ ๋ ํ์์ ๋ค ๋ฐ๊ฒ ํ๋ค. Bearer ํ ํฐ์ด๋ ํ ํฐ๋ง ์ค๋ ๊ฐ์ ๊ฐ์ด๋ฉด ํต๊ณผ์ํจ๋ค. ๋น๊ต๋ ํด์๋ก ๋ฐ๊ฟ์ timingSafeEqual๋ก ํ๋ค.
import { createHash, timingSafeEqual } from "crypto";
export function isValidDraftToken(authorization: string | null): boolean {
const expected = process.env.DRAFT_API_TOKEN;
if (!expected || expected.length < 24) return false;
const header = authorization?.trim();
if (!header) return false;
const match = header.match(/^Bearer\s+(.+)$/i);
const candidate = (match ? match[1] : header).trim();
const given = createHash("sha256").update(candidate).digest();
const want = createHash("sha256").update(expected).digest();
return timingSafeEqual(new Uint8Array(given), new Uint8Array(want));
}
[๋ณด๊ฐ: ์๋ฒ๋ฅผ ๊ณ ์น๊ธฐ๋ก ํ ์ด์ . ์: ์ปค๋ฅํฐ๋ฅผ ๋ค์ ๋ง๋๋ ๊ฒ ๋ฒ๊ฑฐ๋ก์์ / ๋ ํ์์ ๋ค ๋ฐ์๋ ๋ณด์์ ๋ฌธ์ ๊ฐ ์๋ค๊ณ ํ๋จํด์]
5. ๋ด๊ฐ ํ ์ค์ ํ๋
์ฒ์ ๋ฃ์ ์ง๋จ ๋ก๊ทธ๋ ํค๋๋ฅผ ๊ณต๋ฐฑ์ผ๋ก ์ชผ๊ฐ์ ์๋ถ๋ถ์ scheme์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ์ฐ์๋ค. ๊ทธ๋ฐ๋ฐ ๊ณต๋ฐฑ์ด ์๋ ํค๋๋ ํต์งธ๋ก ์๋ถ๋ถ์ด ๋๊ธฐ ๋๋ฌธ์, ํ ํฐ ์ ์ฒด๊ฐ ๋ก๊ทธ์ ์ฐํ ๋ฒ๋ ธ๋ค. ๋ก๊ทธ๋ฅผ ์ฐ์ ๋๋ ๊ฐ ์์ฒด๊ฐ ์๋๋ผ "์๋์ง, ๋ช ๊ธ์์ธ์ง"๋ง ๋จ๊ฒจ์ผ ํ๋ค. ๋
ธ์ถ๋ ํ ํฐ์ ์๋ก ๋ง๋ค์ด ๋ฐ๊ฟจ๋ค. [ํ์ธ ํ์: ์ค์ ๋ก ๊ต์ฒด๋ฅผ ์๋ฃํ๋์ง ๊ธ ์ฌ๋ฆฌ๊ธฐ ์ ์ ํ์ธ]
์ ๋ฆฌ
- ๊ฐ์ ๊ฐ์ด์ด๋ ํ์์ด ๋ค๋ฅด๋ฉด ์ธ์ฆ์ ์คํจํ๋ค. ๊ฐ ๋ง๊ณ ํค๋ ๋ชจ์๋ ์์ฌํ์.
- ์ปค๋ฅํฐ ํค๋ ๊ฐ์ ์
๋ ฅํ ๊ทธ๋๋ก ๊ฐ๋ค.
Bearer๋ ์ง์ ์ ์ด์ผ ํ๋ค. - ์ง๋จ ๋ก๊ทธ์๋ ๋น๋ฐ๊ฐ์ด ์์ด์ง ์๊ฒ, ์กด์ฌ ์ฌ๋ถ์ ๊ธธ์ด๋ง ์ฐ์.
๋ค์ ๊ธ์์๋ ์ฃผ์ ์์ www๊ฐ ์๋ค๋ ์ด์ ๋ง์ผ๋ก ์ฐ๊ฒฐ์ด ์ ๋๋ ์ด์ผ๊ธฐ๋ฅผ ๋ค๋ค๋ณด๊ฒ ๋ค.
