Real World Example

Login Like Swiggy and Zomato

Indian consumer apps mostly use phone + OTP or Google login, keep users signed in for weeks, and lock the admin and seller panels behind roles. ShopEase uses Google + email/password for customers and roles for staff:

AreaCheck
/products, /Public
/cartGuest cart in a cookie; merged into the user cart on login
/checkout, /account/*Signed in
/seller/*Role seller + owns the product
/admin/*Role admin

Admin Page

// app/admin/orders/page.tsx
import { requireRole } from '@/lib/authz';
import { prisma } from '@/lib/prisma';
import RefundButton from './RefundButton';

export default async function AdminOrders() {
  await requireRole('admin');
  const orders = await prisma.order.findMany({ orderBy: { createdAt: 'desc' }, take: 50, include: { user: true } });

  return (
    <table>
      <tbody>
        {orders.map((o) => (
          <tr key={o.id}>
            <td>{o.id}</td>
            <td>{o.user.email}</td>
            <td>₹{o.total}</td>
            <td>{o.status}</td>
            <td><RefundButton orderId={o.id} /></td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}
// app/admin/orders/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { requireRole } from '@/lib/authz';

export async function refundOrder(orderId: string) {
  const admin = await requireRole('admin'); // checked again: actions are public endpoints
  await prisma.order.update({ where: { id: orderId }, data: { status: 'REFUNDED', refundedBy: admin.id } });
  revalidatePath('/admin/orders');
}