🧭 Flow การเปลี่ยน Workspace (เมื่อใช้ Database Session)
🎯 เป้าหมาย:
ให้ผู้ใช้สามารถกดเปลี่ยน Workspace แล้ว session จะอัปเดต currentWorkspace และ frontend รู้ทันที
🔄 1. ภาพรวม UX Flow
sequenceDiagram
participant UI
participant API (/api/auth/switch-ws)
participant Adapter (FirestoreAdapter)
participant Firestore
UI->>API: POST /switch-ws { workspaceId }
API->>Adapter: updateSession({ sessionToken, currentWorkspace })
Adapter->>Firestore: PATCH sessions/{sessionToken}
Firestore-->>Adapter: ok
Adapter-->>API: success
API-->>UI: 200 OK
UI->>NextAuth: session revalidate
NextAuth-->>UI: session.currentWorkspace = "new_id"
🗂 2. โครงสร้างที่เกี่ยวข้องใน Firestore
| Path |
ใช้ทำอะไร |
gohig/auth/sessions/{sessionToken} |
บันทึก field currentWorkspace |
gohig/app/users/{userId} |
มี defaultWorkspace (ใช้ตอนแรกเข้า) |
gohig/app/memberships/{id} |
ตรวจสอบว่า user เป็นสมาชิกของ workspace นั้นหรือไม่ |
🧱 3. การเปลี่ยน Workspace (ฝั่ง API Route)
// /app/api/auth/switch-ws/route.ts
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { getAuthAdapter } from '@/lib/adapter';
import { db } from '@/lib/firebase-admin';
import { collection, query, where, getDocs } from 'firebase-admin/firestore';
export async function POST(req: Request) {
const { workspaceId } = await req.json();
const session = await getServerSession(authOptions);
if (!session?.user?.id || !session.sessionToken) {
return new Response('Unauthorized', { status: 401 });
}
// ตรวจสอบว่าผู้ใช้อยู่ใน workspace นี้หรือไม่
const membershipsRef = collection(db, 'gohig/app/memberships');
const snap = await getDocs(
query(membershipsRef,
where('userId', '==', session.user.id),
where('workspaceId', '==', workspaceId)
)
);
if (snap.empty) {
return new Response('Forbidden: not a member', { status: 403 });
}
// อัปเดต session ใน Firestore
const adapter = getAuthAdapter();
await adapter.updateSession({
sessionToken: session.sessionToken,
userId: session.user.id,
expires: session.expires,
currentWorkspace: workspaceId,
});
return Response.json({ success: true });
}
🧑💻 4. ฝั่ง Client: เรียก API + Revalidate Session
'use client';
import { useSession } from 'next-auth/react';
export default function WorkspaceSwitcher() {
const { data: session, update } = useSession();
const switchWorkspace = async (wsId: string) => {
const res = await fetch('/api/auth/switch-ws', {
method: 'POST',
body: JSON.stringify({ workspaceId: wsId }),
});
if (res.ok) {
await update(); // 🔁 ดึง session ใหม่
}
};
return (
<select
onChange={(e) => switchWorkspace(e.target.value)}
value={session?.currentWorkspace ?? ''}
>
{/* ดึงจาก session หรือ fetch workspaces ก็ได้ */}
<option value="workspace_xyz">Gohig</option>
<option value="workspace_abc">My Personal</option>
</select>
);
}
🧠 5. เสริมความปลอดภัย
- ตรวจสอบ workspace scope หรือ role ใน
switch-ws API ได้เลย
- ถ้า field
currentWorkspace ไม่มีใน session → fallback เป็น defaultWorkspace
- อย่าลืมระบุ
sessionToken ใน getServerSession() ด้วย module augmentation (NextAuth ยังไม่ใส่มาให้ใน session type)
👉 module augmentation เพิ่ม sessionToken :
// next-auth.d.ts
declare module 'next-auth' {
interface Session {
sessionToken?: string;
currentWorkspace?: string;
}
}
✅ สรุปสิ่งที่ต้องทำ
| สิ่งที่ต้องมี |
เพื่ออะไร |
✅ strategy: 'database' |
ให้ session อยู่ใน Firestore และเรียก updateSession() ได้ |
✅ API /api/auth/switch-ws |
อัปเดต session ด้วย workspace ที่เลือก |
✅ field currentWorkspace ใน session |
ใช้เป็น context ตลอดการใช้งาน |
| ✅ UI สลับ workspace + revalidate session |
ให้ UX ลื่นไหล |
✅ ตรวจสิทธิใน memberships |
ป้องกันการสลับ workspace ที่ไม่มีสิทธิ |