mickasmt / next-saas-stripe-starter

Open-source SaaS Starter with User Roles & Admin Panel. Built using Next.js 14, Prisma, Neon, Auth.js v5, Resend, React Email, Shadcn/ui, Stripe, Server Actions.
https://next-saas-stripe-starter.vercel.app
MIT License
1.23k stars 196 forks source link

API Endpoint cannot recognise user #30

Closed AdnanHussainTurki closed 1 month ago

AdnanHussainTurki commented 1 month ago

Hi,

I am using the starter kit and I must say, It is really awesome and made the startup of the project, absolutely an ease.

Recently, I am facing an issue where the API endpoint is not able to fetch the authenticated user through getToken().

package.json

{
  "name": "saas-starter",
  "version": "0.2.0",
  "private": true,
  "author": {
    "name": "mickasmt",
    "url": "https://twitter.com/mickasmt"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "vercel-build": "prisma generate && prisma migrate deploy && next build",
    "turbo": "next dev --turbo",
    "start": "next start",
    "lint": "next lint",
    "preview": "next build && next start",
    "postinstall": "prisma generate",
    "email": "email dev --dir emails --port 3333"
  },
  "dependencies": {
    "@auth/prisma-adapter": "^2.1.0",
    "@hookform/resolvers": "^3.3.4",
    "@next-auth/prisma-adapter": "^1.0.7",
    "@prisma/client": "^5.14.0",
    "@radix-ui/react-accessible-icon": "^1.0.3",
    "@radix-ui/react-accordion": "^1.1.2",
    "@radix-ui/react-alert-dialog": "^1.0.5",
    "@radix-ui/react-aspect-ratio": "^1.0.3",
    "@radix-ui/react-avatar": "^1.0.4",
    "@radix-ui/react-checkbox": "^1.0.4",
    "@radix-ui/react-collapsible": "^1.0.3",
    "@radix-ui/react-context-menu": "^2.1.5",
    "@radix-ui/react-dialog": "^1.0.5",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-hover-card": "^1.0.7",
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-menubar": "^1.0.4",
    "@radix-ui/react-navigation-menu": "^1.1.4",
    "@radix-ui/react-popover": "^1.0.7",
    "@radix-ui/react-progress": "^1.0.3",
    "@radix-ui/react-radio-group": "^1.1.3",
    "@radix-ui/react-scroll-area": "^1.0.5",
    "@radix-ui/react-select": "^2.0.0",
    "@radix-ui/react-separator": "^1.0.3",
    "@radix-ui/react-slider": "^1.1.2",
    "@radix-ui/react-slot": "^1.0.2",
    "@radix-ui/react-switch": "^1.0.3",
    "@radix-ui/react-tabs": "^1.0.4",
    "@radix-ui/react-toast": "^1.1.5",
    "@radix-ui/react-toggle": "^1.0.3",
    "@radix-ui/react-toggle-group": "^1.0.4",
    "@radix-ui/react-tooltip": "^1.0.7",
    "@react-email/button": "0.0.15",
    "@react-email/components": "0.0.17",
    "@react-email/html": "0.0.8",
    "@t3-oss/env-nextjs": "^0.10.1",
    "@types/uuid": "^9.0.8",
    "@typescript-eslint/parser": "^7.8.0",
    "@vercel/analytics": "^1.2.2",
    "@vercel/og": "^0.6.2",
    "axios": "^1.7.2",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "cmdk": "^1.0.0",
    "concurrently": "^8.2.2",
    "contentlayer2": "^0.4.6",
    "date-fns": "^3.6.0",
    "html-react-parser": "^5.1.10",
    "lucide-react": "^0.377.0",
    "luxon": "^3.4.4",
    "ms": "^2.1.3",
    "next": "14.2.3",
    "next-auth": "5.0.0-beta.18",
    "next-contentlayer2": "^0.4.6",
    "next-sitemap": "^4.2.3",
    "next-themes": "^0.3.0",
    "nodemailer": "^6.9.13",
    "prop-types": "^15.8.1",
    "react": "18.3.1",
    "react-aiwriter": "^1.0.0",
    "react-day-picker": "^8.10.1",
    "react-dom": "18.3.1",
    "react-email": "2.1.2",
    "react-hook-form": "^7.51.3",
    "react-markdown": "^9.0.1",
    "react-scroll": "^1.9.0",
    "react-textarea-autosize": "^8.5.3",
    "react-toastify": "^10.0.5",
    "react-typed": "^2.0.12",
    "request-ip": "^3.3.0",
    "resend": "^3.2.0",
    "sharp": "^0.33.3",
    "shiki": "^1.4.0",
    "stripe": "^15.4.0",
    "tailwind-merge": "^2.3.0",
    "tailwindcss-animate": "^1.0.7",
    "uuidv4": "^6.2.13",
    "vaul": "^0.9.0",
    "zod": "^3.23.5",
    "zustand": "^4.5.2"
  },
  "devDependencies": {
    "@commitlint/cli": "^19.3.0",
    "@commitlint/config-conventional": "^19.2.2",
    "@ianvs/prettier-plugin-sort-imports": "^4.2.1",
    "@tailwindcss/line-clamp": "^0.4.4",
    "@tailwindcss/typography": "^0.5.13",
    "@types/node": "^20.12.8",
    "@types/react": "18.3.1",
    "@types/react-dom": "18.3.0",
    "autoprefixer": "^10.4.19",
    "eslint": "^8.57.0",
    "eslint-config-next": "14.2.3",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-react": "^7.34.1",
    "eslint-plugin-tailwindcss": "^3.15.1",
    "husky": "^9.0.11",
    "mdast-util-toc": "^7.0.1",
    "postcss": "^8.4.38",
    "prettier": "^3.2.5",
    "prettier-plugin-tailwindcss": "^0.5.14",
    "pretty-quick": "^4.0.0",
    "prisma": "^5.14.0",
    "rehype": "^13.0.1",
    "rehype-autolink-headings": "^7.1.0",
    "rehype-pretty-code": "^0.13.1",
    "rehype-slug": "^6.0.0",
    "remark": "^15.0.1",
    "remark-gfm": "^4.0.0",
    "tailwindcss": "^3.4.3",
    "typescript": "5.4.5",
    "unist-util-visit": "^5.0.0"
  }
}

components/services/service-input-card.tsx

....

 const axResponse = await axios.post(
        "/api/jobs",
        {
          slug: service.slug,
          input: query,
          ip: null,
          token: visit.token,
        },
        {
          headers: {
            "Content-Type": "application/json",
          },
          withCredentials: true,
        },
      );
      const response: {
        message: any;
        response: any;
      } = (await axResponse.data) as any;

....

api/jobs/route.tsx

export async function POST(req: NextRequest) {
  let { slug, input, ip, token } = await req.json();
  ip = getIp(req);
  if (!ip || (!!token && token.length != 36)) {
    return new Response(JSON.stringify({ message: "Invalid request" }), {
      status: 400,
    });
  }

  const secret = process.env.NEXTAUTH_SECRET;
  console.log("🎩 Secret:", secret);
  const bareUser = (await getToken({
    req,
    secret,
    encryption: true,
  } as any)) as any;
  console.log("🎩 Bare User:", bareUser);
  let user: any = null;
  if (bareUser) {
    user = await prisma.user.findUnique({
      where: {
        email: bareUser.email,
      },
    });
  }
  console.log("🎩 User:", user);
}

The bareUser and user both come out to be null.

Note: In development, things seem to be working but on Vercel it is not. Possibly some issue with the transition from next-auth to auth.js.

Please guide.

mickasmt commented 1 month ago

Hi @AdnanHussainTurki thanks for your interest on the project! I updated your code to work with next auth v5. The route will be protected and no need to bring back the env variable or the token. I don't know if this is what you are looking for. More information here

import { auth } from "@/auth";
import { prisma } from "@/lib/db";
import { getIp } from "change/with/your/path";

export const POST = auth(async (req) => {
  if (!req.auth) {
    return Response.json({ message: "Not authenticated" }, { status: 401 });
  }

  const currentUser = req.auth.user;
  let { slug, input, ip } = await req.json();

  ip = getIp(req);
  if (!ip) {
    return Response.json({ message: "Invalid request" }, { status: 400 });
  }

  let user: any = null;
  if (currentUser) {
    user = await prisma.user.findUnique({
      where: {
        id: currentUser.id,
      },
    });
  }

  return Response.json({ message: "ok" }, { status: 200 });
});