Update all references across source files, documentation, and configuration to reflect the new package scope and name. This includes updating `.npmrc` registry config, install instructions, module examples, and all import path comments throughout the codebase.
9.0 KiB
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.
// 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 (
<div>
<h1>Dashboard</h1>
<DashboardClient initialUser={session.user} />
</div>
);
}
redirectTo: where to send the user if not authenticated (default:'/auth/login').protect()returns the session (withsession.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:
// 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 (
<div>
<header>
{session?.user ? (
<UserMenu user={session.user} accountHref="/dashboard/account" logoutHref="/auth/logout" />
) : (
<a href="/auth/login">Log in</a>
)}
</header>
{children}
</div>
);
}
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:
'use client';
import { UserMenu } from '@zen/core/auth/components';
export function Header() {
return (
<UserMenu
accountHref="/dashboard/account"
logoutHref="/auth/logout"
/>
);
}
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:
// 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 (
<div>
<h1>My account</h1>
<AccountSection initialUser={session.user} />
</div>
);
}
initialUser– Optional. If you passsession.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/profilefor namePOST /zen/api/users/profile/picturefor uploadDELETE /zen/api/users/profile/picturefor 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:
import { checkAuth } from '@zen/core/auth';
export default async function Page() {
const session = await checkAuth();
return session ? <div>Hello, {session.user.name}</div> : <div>Please log in</div>;
}
Use requireRole() when a page is only for certain roles:
import { requireRole } from '@zen/core/auth';
export default async function ManagerPage() {
const session = await requireRole(['admin', 'manager'], {
redirectTo: '/auth/login',
forbiddenRedirect: '/dashboard',
});
return <div>Manager content</div>;
}
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 thatuserId(or admin). - Protection: Use
protect()orrequireRole()in server components so unauthenticated or unauthorized users never see sensitive dashboard content.
6. Minimal dashboard example
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:
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 (
<div>
<header className="flex justify-between items-center p-4 border-b">
<Link href="/dashboard">Dashboard</Link>
<UserMenu
user={session.user}
accountHref="/dashboard/account"
logoutHref="/auth/logout"
/>
</header>
<main className="p-4">{children}</main>
</div>
);
}
dashboard/page.js:
import { protect } from '@zen/core/auth';
export default async function DashboardPage() {
const session = await protect({ redirectTo: '/auth/login' });
return (
<div>
<h1>Welcome, {session.user.name}</h1>
<p><a href="/dashboard/account">Edit my account</a></p>
</div>
);
}
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 (
<div>
<h1>My account</h1>
<AccountSection initialUser={session.user} />
</div>
);
}
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
ClientInvoicesSectionfrom@zen/core/invoice/dashboardon a protected page (e.g./dashboard/invoices).
See the Invoice module dashboard guide for the full setup (API details, page example, linking users to clients, and security).