# Client dashboard and user features This guide explains how to build a **client dashboard** in your Next.js app using Zen auth: protect routes, show the current user (name, avatar), add an account section to edit profile and avatar, and redirect to login when the user is not connected. ## What is available | Need | Solution | |------|----------| | Require login on a page | `protect()` in a **server component** – redirects to login if not authenticated | | Get current user on server | `getSession()` from `@zen/core/auth/actions` | | Check auth without redirect | `checkAuth()` from `@zen/core/auth` | | Require a role | `requireRole(['admin', 'manager'])` from `@zen/core/auth` | | Show user in client (header/nav) | `UserMenu` or `UserAvatar` + `useCurrentUser` from `@zen/core/auth/components` | | Edit account (name + avatar) | `AccountSection` from `@zen/core/auth/components` | | Call user API from client | `GET /zen/api/users/me`, `PUT /zen/api/users/profile`, `POST/DELETE /zen/api/users/profile/picture` (with `credentials: 'include'`) | All user APIs are **session-based**: the session cookie is read on the server. No token in client code. Avatar and profile updates are scoped to the current user; the API validates the session on every request. --- ## 1. Protect a dashboard page (redirect if not logged in) Use `protect()` in a **server component**. If there is no valid session, the user is redirected to the login page. ```js // app/dashboard/page.js (Server Component) import { protect } from '@zen/core/auth'; import { DashboardClient } from './DashboardClient'; export default async function DashboardPage() { const session = await protect({ redirectTo: '/auth/login' }); return (

Dashboard

); } ``` - `redirectTo`: where to send the user if not authenticated (default: `'/auth/login'`). - `protect()` returns the **session** (with `session.user`: `id`, `email`, `name`, `role`, `image`, etc.). --- ## 2. Display the current user in the layout (name, avatar) **Option A – Server: pass user into a client component** In your layout or header (server component), get the session and pass `user` to a client component that shows avatar and name: ```js // app/layout.js or app/dashboard/layout.js import { getSession } from '@zen/core/auth/actions'; import { UserMenu } from '@zen/core/auth/components'; export default async function Layout({ children }) { const session = await getSession(); return (
{session?.user ? ( ) : ( Log in )}
{children}
); } ``` **Option B – Client only: fetch user with `useCurrentUser`** If you prefer not to pass user from the server, use the hook in a client component. It calls `GET /zen/api/users/me` with the session cookie: ```js 'use client'; import { UserMenu } from '@zen/core/auth/components'; export function Header() { return ( ); } ``` `UserMenu` with no `user` prop will call `useCurrentUser()` itself and show a loading state until the request finishes. If the user is not logged in, it renders nothing (you can show a “Log in” link elsewhere). **Components:** - **`UserMenu`** – Avatar + name + dropdown with “My account” and “Log out”. Props: `user` (optional), `accountHref`, `logoutHref`, `className`. - **`UserAvatar`** – Only the avatar (image or initials). Props: `user`, `size` (`'sm' | 'md' | 'lg'`), `className`. - **`useCurrentUser()`** – Returns `{ user, loading, error, refetch }`. Use when you need the current user in a client component without receiving it from the server. --- ## 3. Account page (edit profile and avatar) Use **`AccountSection`** on a page that is already protected (e.g. `/dashboard/account`). It shows: - Profile picture (upload / remove) - Full name (editable) - Email (read-only) - Optional “Account created” date **Server page:** ```js // app/dashboard/account/page.js import { protect } from '@zen/core/auth'; import { AccountSection } from '@zen/core/auth/components'; export default async function AccountPage() { const session = await protect({ redirectTo: '/auth/login' }); return (

My account

); } ``` - **`initialUser`** – Optional. If you pass `session.user`, the section uses it immediately and does not need an extra API call on load. - **`onUpdate`** – Optional callback after profile or avatar update; you can use it to refresh parent state or revalidate. `AccountSection` uses: - `PUT /zen/api/users/profile` for name - `POST /zen/api/users/profile/picture` for upload - `DELETE /zen/api/users/profile/picture` for remove All with `credentials: 'include'` (session cookie). Ensure your app uses **ToastProvider** (from `@zen/core/toast`) if you want toasts. --- ## 4. Check if the user is connected (without redirect) Use **`checkAuth()`** in a server component when you only need to know whether someone is logged in: ```js import { checkAuth } from '@zen/core/auth'; export default async function Page() { const session = await checkAuth(); return session ?
Hello, {session.user.name}
:
Please log in
; } ``` Use **`requireRole()`** when a page is only for certain roles: ```js import { requireRole } from '@zen/core/auth'; export default async function ManagerPage() { const session = await requireRole(['admin', 'manager'], { redirectTo: '/auth/login', forbiddenRedirect: '/dashboard', }); return
Manager content
; } ``` --- ## 5. Security summary - **Session cookie**: HttpOnly, validated on the server for every protected API call. - **User APIs**: - `GET /zen/api/users/me` – current user only. - `PUT /zen/api/users/profile` – update only the authenticated user’s name. - Profile picture upload/delete – scoped to the current user; storage path includes `users/{userId}/...`. - **Storage**: User files under `users/{userId}/...` are only served if the request session matches that `userId` (or admin). - **Protection**: Use `protect()` or `requireRole()` in server components so unauthenticated or unauthorized users never see sensitive dashboard content. --- ## 6. Minimal dashboard example ```text app/ layout.js # Root layout with ToastProvider if you use it auth/ [...auth]/page.js # Zen default auth page (login, register, logout, etc.) dashboard/ layout.js # Optional: layout that shows UserMenu and requires login page.js # Protected dashboard home account/ page.js # Protected account page with AccountSection ``` **dashboard/layout.js:** ```js import { protect } from '@zen/core/auth'; import { UserMenu } from '@zen/core/auth/components'; import Link from 'next/link'; export default async function DashboardLayout({ children }) { const session = await protect({ redirectTo: '/auth/login' }); return (
Dashboard
{children}
); } ``` **dashboard/page.js:** ```js import { protect } from '@zen/core/auth'; export default async function DashboardPage() { const session = await protect({ redirectTo: '/auth/login' }); return (

Welcome, {session.user.name}

Edit my account

); } ``` **dashboard/account/page.js:** ```js import { protect } from '@zen/core/auth'; import { AccountSection } from '@zen/core/auth/components'; export default async function AccountPage() { const session = await protect({ redirectTo: '/auth/login' }); return (

My account

); } ``` This gives you a protected dashboard, user display in the header, and a dedicated account page to modify profile and avatar, with redirect to login when the user is not connected. --- ## 7. Facturation (invoices) section If you use the **Invoice** module and want logged-in users to see their own invoices in the dashboard: - **User–client link**: In the admin, link a user to a client (User edit → Client). Only invoices for that client are shown. - **API**: `GET /zen/api/invoices/me` (session required) returns the current user’s linked client and that client’s invoices. - **Component**: Use `ClientInvoicesSection` from `@zen/core/invoice/dashboard` on a protected page (e.g. `/dashboard/invoices`). See the [Invoice module dashboard guide](../../modules/invoice/README-dashboard.md) for the full setup (API details, page example, linking users to clients, and security).