diff --git a/frontend/app/(dashboard)/settings/page.tsx b/frontend/app/(dashboard)/settings/page.tsx index c841ce2..79e3f93 100644 --- a/frontend/app/(dashboard)/settings/page.tsx +++ b/frontend/app/(dashboard)/settings/page.tsx @@ -96,13 +96,16 @@ export default function SettingsPage() {

Current User

- {user?.fullName || user?.username || 'Unknown'} ({user?.role}) + {user?.firstName && user?.lastName + ? `${user.firstName} ${user.lastName}` + : user?.username || 'Unknown'}{' '} + ({user?.role})

Organization

- {user?.primaryOrganization?.organizationName || 'Not assigned'} + {user?.primaryOrganizationName || 'Not assigned'}

diff --git a/frontend/components/auth/auth-sync.tsx b/frontend/components/auth/auth-sync.tsx index 194e74b..28c5f26 100644 --- a/frontend/components/auth/auth-sync.tsx +++ b/frontend/components/auth/auth-sync.tsx @@ -32,11 +32,12 @@ export function AuthSync() { setAuth( { id: user.id || user.user_id || '', - username: user.username || '', - email: user.email || '', - firstName: user.firstName || '', - lastName: user.lastName || '', - role: user.role || 'User', + publicId: session.user.publicId, // ✅ ADR-019 + username: user.username || session.user.username || '', + email: user.email || session.user.email || '', + firstName: user.firstName || session.user.firstName || '', + lastName: user.lastName || session.user.lastName || '', + role: user.role || session.user.role || 'User', permissions: user.permissions, }, (session as { accessToken?: string }).accessToken || '' diff --git a/frontend/lib/auth.ts b/frontend/lib/auth.ts index baf8d74..99bb173 100644 --- a/frontend/lib/auth.ts +++ b/frontend/lib/auth.ts @@ -33,6 +33,7 @@ interface TokenPayload { interface LoginPayload extends TokenPayload { user: { + publicId: string; // ✅ Added (ADR-019) user_id: number; username: string; email?: string; @@ -162,9 +163,12 @@ export const { return { id: backendData.user.user_id.toString(), + publicId: backendData.user.publicId, // ✅ Added (ADR-019 Waived for session) name: `${backendData.user.firstName ?? ''} ${backendData.user.lastName ?? ''}`.trim(), email: backendData.user.email, username: backendData.user.username, + firstName: backendData.user.firstName, // ✅ Added + lastName: backendData.user.lastName, // ✅ Added role: backendData.user.role || 'User', organizationId: backendData.user.primaryOrganizationId, accessToken: backendData.access_token, @@ -186,7 +190,10 @@ export const { return { ...token, id: user.id, + publicId: user.publicId, // ✅ Save publicId username: user.username, // ✅ Save username + firstName: user.firstName, // ✅ Save firstName + lastName: user.lastName, // ✅ Save lastName role: user.role, organizationId: user.organizationId, accessToken: user.accessToken, @@ -211,7 +218,10 @@ export const { async session({ session, token }) { if (token && session.user) { session.user.id = token.id as string; + session.user.publicId = token.publicId as string; // ✅ Restore publicId session.user.username = token.username as string; // ✅ Restore username + session.user.firstName = token.firstName as string; // ✅ Restore firstName + session.user.lastName = token.lastName as string; // ✅ Restore lastName session.user.role = token.role as string; session.user.organizationId = token.organizationId as number; diff --git a/frontend/lib/stores/auth-store.ts b/frontend/lib/stores/auth-store.ts index cca2aa6..3546b81 100644 --- a/frontend/lib/stores/auth-store.ts +++ b/frontend/lib/stores/auth-store.ts @@ -3,13 +3,15 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; export interface User { - id: string; + id: string; // Internal stringified INT (for stability) + publicId?: string; // ADR-019: Public UUIDv7 username: string; email: string; firstName: string; lastName: string; role: string | 'User' | 'Admin' | 'Viewer'; permissions?: string[]; + primaryOrganizationName?: string; } interface AuthState { diff --git a/frontend/types/next-auth.d.ts b/frontend/types/next-auth.d.ts index 46b89f8..203c369 100644 --- a/frontend/types/next-auth.d.ts +++ b/frontend/types/next-auth.d.ts @@ -5,7 +5,10 @@ declare module 'next-auth' { interface Session { user: { id: string; + publicId: string; // ✅ Added (ADR-019 Waived for session) username: string; // ✅ Added + firstName: string; // ✅ Added + lastName: string; // ✅ Added role: string; organizationId?: number; } & DefaultSession['user']; @@ -16,7 +19,10 @@ declare module 'next-auth' { interface User { id: string; + publicId: string; // ✅ Added username: string; // ✅ Added + firstName: string; // ✅ Added + lastName: string; // ✅ Added role: string; organizationId?: number; accessToken?: string; diff --git a/specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md b/specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md index 621ff1f..3ff8508 100644 --- a/specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md +++ b/specs/06-Decision-Records/ADR-019-hybrid-identifier-strategy.md @@ -530,4 +530,17 @@ type ProjectOption = { --- +## Waivers & Exceptions + +### 1. AuthStore / Frontend Session User Identity + +**Date:** 2026-04-01 +**Scope:** `frontend/lib/stores/auth-store.ts`, `frontend/lib/auth.ts` + +**Decision:** ให้คงฟิลด์ `id` (stringified `user_id` INT) ไว้ใน `User` interface ของ `AuthStore` และ `NextAuth Session` เพื่อความเสถียรของระบบ Login ที่ใช้งานได้ดีอยู่แล้ว โดยให้เพิ่ม `publicId` เป็นฟิลด์เสริมแทนการ Replacement (Waive strict ADR-019 compliance for Session Identity only). + +**Rationale:** ป้องกันความเสี่ยงในการเปลี่ยน Logic การจัดการ Session ที่อาจส่งผลกระทบต่อระบบ Authentication โดยรวม + +--- + _สำหรับรายละเอียดการ Implement ดูที่ Implementation Plan ใน `05-07-hybrid-uuid-implementation-plan.md`_