diff --git a/devfront/src/components/layout/AppLayout.tsx b/devfront/src/components/layout/AppLayout.tsx
index 515fe28e..fbe92580 100644
--- a/devfront/src/components/layout/AppLayout.tsx
+++ b/devfront/src/components/layout/AppLayout.tsx
@@ -1,11 +1,9 @@
-import { BadgeCheck, LogOut, Moon, ShieldHalf, Sun, User } from "lucide-react";
+import { BadgeCheck, LogOut, Moon, ShieldHalf, Sun } from "lucide-react";
import { useEffect, useState } from "react";
import { useAuth } from "react-oidc-context";
import { NavLink, Outlet } from "react-router-dom";
-import { useQuery } from "@tanstack/react-query";
import { t } from "../../lib/i18n";
import { Toaster } from "../ui/toaster";
-import { fetchMe } from "../../features/auth/authApi";
const navItems = [
{
@@ -18,11 +16,16 @@ const navItems = [
function AppLayout() {
const auth = useAuth();
- const { data: profile } = useQuery({
- queryKey: ["me"],
- queryFn: fetchMe,
- enabled: auth.isAuthenticated,
- });
+
+ // OIDC ID Token에서 프로필 정보 추출
+ // auth.user?.profile에는 OIDC 표준 클레임(sub, email, name 등)이 포함됨
+ const profile = auth.user?.profile ? {
+ id: auth.user.profile.sub,
+ email: auth.user.profile.email,
+ name: (auth.user.profile.name as string) || (auth.user.profile.preferred_username as string) || auth.user.profile.email,
+ tenantId: auth.user.profile.tenant_id as string,
+ tenant: auth.user.profile.tenant as any,
+ } : null;
const [theme, setTheme] = useState<"light" | "dark">(() => {
const stored = window.localStorage.getItem("admin_theme");
diff --git a/docs/devfront_auth_flow_explanation.md b/docs/devfront_auth_flow_explanation.md
new file mode 100644
index 00000000..9b9a17fa
--- /dev/null
+++ b/docs/devfront_auth_flow_explanation.md
@@ -0,0 +1,91 @@
+# DevFront OIDC 인증 흐름 및 무한 루프 해결 보고
+
+이 문서는 `devfront` 개발자 포털의 OIDC 인증 구현 방식, 무한 루프 문제의 원인 및 해결 방법, 그리고 클라이언트 자동 등록 원리에 대해 설명합니다.
+
+## 1. DevFront 로그인 동작 플로우 (OIDC Authorization Code Flow)
+
+### 시퀀스 다이어그램
+```mermaid
+sequenceDiagram
+ actor User
+ participant DF as DevFront (RP)
+ participant HY as Ory Hydra (OP)
+ participant UF as UserFront (Login UI)
+ participant KR as Ory Kratos (Identity)
+
+ User->>DF: 로그인 버튼 클릭
+ DF->>HY: 인증 요청 (/oauth2/auth)
+ HY->>UF: 로그인 UI 리다이렉트
+ UF->>User: 로그인 페이지 표시
+ User->>UF: 자격 증명 입력 (Email/PW)
+ UF->>KR: 인증 수행
+ KR-->>UF: 인증 성공 (Session 생성)
+ UF->>HY: 로그인 승인 요청
+ HY->>User: 권한 동의(Consent) 화면 표시
+ User->>HY: '허용' 클릭
+ HY-->>DF: 인증 코드와 함께 리다이렉트 (/callback?code=...)
+ DF->>HY: 토큰 교환 요청 (Code -> ID/Access Token)
+ HY-->>DF: 토큰 발급
+ Note over DF: [FIX] 백엔드 /api/me 호출 대신
ID Token에서 프로필 정보 직접 추출
+ DF->>User: 대시보드 및 프로필 표시
+```
+
+### 단계별 설명
+
+1. **인증 요청 (Login Request)**:
+ * 사용자가 `devfront` (localhost:5174)의 로그인 버튼을 클릭합니다.
+ * `react-oidc-context` 라이브러리가 Hydra의 `/oauth2/auth` 엔드포인트로 사용자를 리다이렉트합니다.
+ * 이때 클라이언트 ID(`devfront`), 리다이렉트 URI, Scope 등을 파라미터로 전달합니다.
+
+2. **사용자 인증 (Authentication)**:
+ * Hydra는 현재 세션이 없음을 확인하고, 설정된 로그인 UI(`userfront`)로 사용자를 보냅니다.
+ * 사용자는 `userfront`에서 아이디/비밀번호를 입력하여 Kratos를 통해 인증을 마칩니다.
+
+3. **권한 동의 (Consent)**:
+ * 인증이 완료되면 Hydra는 사용자에게 `devfront` 앱이 요청한 권한(openid, profile, email 등)을 허용할지 묻는 Consent 화면을 띄웁니다.
+ * 사용자가 '허용'을 누르면 Hydra는 `devfront`가 신뢰할 수 있는 앱임을 기록합니다.
+
+4. **인증 코드 전달 및 토큰 교환 (Callback)**:
+ * Hydra는 사용자를 `devfront`의 콜백 페이지(`http://localhost:5174/callback?code=...`)로 보냅니다.
+ * `devfront`는 이 코드를 Hydra의 토큰 엔드포인트로 보내 **ID Token**과 **Access Token**을 발급받습니다.
+
+5. **사용자 정보 로드 (Profile Recovery)**:
+ * `devfront`는 발급받은 **ID Token**의 Payload를 디코딩하여 사용자 이름, 이메일 등의 프로필 정보를 즉시 화면에 렌더링합니다.
+
+---
+
+## 2. 클라이언트 자동 등록의 원리
+
+사용자가 직접 `devfront`를 클라이언트로 등록하지 않았음에도 로그인이 가능한 이유는 인프라 설정 파일인 `compose.ory.yaml`에 정의된 **`init-rp` 컨테이너** 덕분입니다.
+
+### `init-rp` 서비스의 역할
+* Ory 스택(Kratos, Hydra, Keto)이 모두 정상 가동된 직후 실행됩니다.
+* Hydra Admin API를 호출하여 서비스 운영에 필수적인 기본 클라이언트들을 자동으로 생성합니다.
+
+### 자동 등록된 `devfront` 명세
+```bash
+hydra clients create
+ --endpoint http://hydra:4445
+ --id devfront
+ --grant-types authorization_code,refresh_token
+ --response-types code
+ --scope openid,offline_access,profile,email
+ --token-endpoint-auth-method none \ # Public Client (PKCE 사용)
+ --callbacks http://localhost:5174/callback;
+```
+이 설정으로 인해 `devfront`라는 ID의 클라이언트가 미리 존재하게 되며, `localhost:5174`로의 리다이렉션이 안전하게 허용됩니다.
+
+---
+
+## 3. 무한 루프 문제 해결 분석
+
+### 발생 원인 (Problem)
+* `devfront`가 로그인 성공 후 사용자 정보를 가져오기 위해 백엔드 API인 `/api/v1/user/me`를 호출했습니다.
+* 이 API는 백엔드 세션 쿠키를 기반으로 동작하도록 설계되어 있었습니다.
+* 하지만 브라우저 보안 정책(SameSite/Cross-Domain)으로 인해 `localhost`에서 보낸 요청에는 `sso-test.hmac.kr` 도메인의 쿠키가 포함되지 않았습니다.
+* **결과**: 백엔드는 401 Unauthorized를 반환 -> `devfront`는 401을 받으면 다시 로그인을 시도하도록 구현되어 있어 무한 루프가 발생했습니다.
+
+### 해결 방법 (Solution)
+* **쿠키 의존성 제거**: `AppLayout.tsx`에서 백엔드 호출(`fetchMe`)을 삭제했습니다.
+* **ID Token 직접 활용**: 이미 OIDC 인증 성공 시점에 전달받은 **ID Token(`auth.user.profile`)**에 필요한 모든 사용자 정보가 들어있으므로, 이를 직접 사용하도록 수정했습니다.
+* **표준 RP 구조 확립**: 이제 `devfront`는 백엔드의 세션 쿠키를 전혀 사용하지 않으며, API 요청 시에도 `Authorization: Bearer ` 헤더를 사용하여 정당한 권한을 증명합니다.