Guest User

Auth Setup

a guest
Aug 29th, 2025
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { createAuthClient } from "better-auth/react";
  2. import { inferAdditionalFields } from "better-auth/client/plugins";
  3. import { inferOrgAdditionalFields } from "better-auth/client/plugins";
  4. import { adminClient } from "better-auth/client/plugins";
  5. import { emailOTPClient } from "better-auth/client/plugins";
  6. import { organizationClient } from "better-auth/client/plugins";
  7.  
  8. export const authClient = createAuthClient({
  9.   /** The base URL of the server (optional if you're using the same domain) */
  10.   baseURL: import.meta.env.VITE_BACKEND_URL,
  11.  
  12.   plugins: [
  13.     inferAdditionalFields(),
  14.     emailOTPClient(),
  15.     adminClient(),
  16.     organizationClient({
  17.       schema: inferOrgAdditionalFields({
  18.         organization: {
  19.           additionalFields: {
  20.             businessName: {
  21.               type: "string",
  22.               input: true,
  23.             },
  24.             businessCategory: {
  25.               type: "string",
  26.               input: true,
  27.             },
  28.             storeUrl: {
  29.               type: "string",
  30.               input: true,
  31.             },
  32.             storeTag: {
  33.               type: "string",
  34.               input: true,
  35.             },
  36.             banner: {
  37.               type: "string",
  38.               input: true,
  39.             },
  40.             status: {
  41.               type: "string",
  42.               input: true,
  43.             },
  44.             description: {
  45.               type: "string",
  46.               input: true,
  47.             },
  48.  
  49.             whitelabel: {
  50.               type: "string",
  51.               input: true,
  52.             },
  53.             phoneNumber: {
  54.               type: "string",
  55.               input: true,
  56.             },
  57.             website: {
  58.               type: "string",
  59.               input: true,
  60.             },
  61.             city: {
  62.               type: "string",
  63.               input: true,
  64.             },
  65.             region: {
  66.               type: "string",
  67.               input: true,
  68.             },
  69.             address: {
  70.               type: "string",
  71.               input: true,
  72.             },
  73.             country: {
  74.               type: "string",
  75.               input: true,
  76.             },
  77.             zipCode: {
  78.               type: "string",
  79.               input: true,
  80.             },
  81.             currency: {
  82.               type: "string",
  83.               input: true,
  84.             },
  85.             facebook: {
  86.               type: "string",
  87.               input: true,
  88.             },
  89.             instagram: {
  90.               type: "string",
  91.               input: true,
  92.             },
  93.             twitter: {
  94.               type: "string",
  95.               input: true,
  96.             },
  97.             tiktok: {
  98.               type: "string",
  99.               input: true,
  100.             },
  101.             linkedin: {
  102.               type: "string",
  103.               input: true,
  104.             },
  105.             storeBaseCurrency: {
  106.               type: "string",
  107.               input: true,
  108.             },
  109.  
  110.             modifyProductState: {
  111.               type: "boolean",
  112.               input: true,
  113.             },
  114.             storeApproval: {
  115.               type: "boolean",
  116.               input: true,
  117.             },
  118.           },
  119.         },
  120.       }),
  121.     }),
  122.   ],
  123. });
  124.  
  125. // export const { signIn, signUp, signOut, useSession } = createAuthClient();
  126.  
  127.  
  128. import { betterAuth } from "better-auth";
  129. import { prismaAdapter } from "better-auth/adapters/prisma";
  130. import { admin as adminPlugin } from "better-auth/plugins";
  131. import { emailOTP } from "better-auth/plugins";
  132. import { organization } from "better-auth/plugins";
  133. import prisma from "./db.js";
  134. import { sendEmail } from "../services/EmailService.js";
  135. import {
  136.   // the statement control
  137.   ac,
  138.   // Admin roles
  139.   admin,
  140.   app_developer,
  141.  
  142.   // Store roles
  143.   owner,
  144.   manager,
  145.   cashier,
  146.   marketer,
  147.   customer_support,
  148. } from "../auth/permissioins.js";
  149.  
  150. export const auth = betterAuth({
  151.   database: prismaAdapter(prisma, {
  152.     provider: "postgresql",
  153.   }), // or "mysql", "postgresql", ...
  154.  
  155.   // Trusted origin (app url) for the system here
  156.   trustedOrigins: [process.env.FRONTEND_URL],
  157.  
  158.   // Email and password setup
  159.   emailAndPassword: {
  160.     enabled: true,
  161.  
  162.     // Reset Password Setup & Emailing
  163.     sendResetPassword: async ({ user, url, token }, request) => {
  164.       await sendEmail({
  165.         to: user.email,
  166.         subject: "Reset your password",
  167.         text: `Click the link to reset your password: ${url}`,
  168.       });
  169.     },
  170.   },
  171.  
  172.   // Social providers , login here
  173.   // socialProviders: {
  174.   //   google: {
  175.   //     clientId: process.env.GOOGLE_CLIENT_ID,
  176.   //     clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  177.   //   },
  178.   // },
  179.  
  180.   // Account management setup
  181.   account: {
  182.     accountLinking: {
  183.       enabled: true,
  184.     },
  185.   },
  186.  
  187.   // Session management setup
  188.   session: {
  189.     cookieCache: {
  190.       enabled: true,
  191.       maxAge: 20 * 60 * 60, // Cache duration in seconds
  192.     },
  193.   },
  194.  
  195.   // Email verification setup
  196.   emailVerification: {
  197.     autoSignInAfterVerification: true,
  198.   },
  199.  
  200.   // Rate limiting on authentication.
  201.   rateLimit: {
  202.     window: 10, // time window in seconds
  203.     max: 10, // max requests in the window
  204.   },
  205.  
  206.   // Allow user to delete .
  207.   deleteUser: {
  208.     enabled: true,
  209.   },
  210.  
  211.   // Plugins setup here
  212.   plugins: [
  213.     // Email OTP Setup
  214.     emailOTP({
  215.       otpLength: 6,
  216.       expiresIn: 6000,
  217.     }),
  218.  
  219.     // Admin plugin setup
  220.     adminPlugin({
  221.       adminRoles: ["admin", "superadmin"],
  222.       adminUserIds: ["", ""],
  223.       defaultRole: "owner",
  224.       bannedUserMessage:
  225.         "Your account has been banned, if its wrong reach out to support",
  226.       ac,
  227.       admin,
  228.       app_developer,
  229.     }),
  230.  
  231.     // Organization plugin setup
  232.     organization({
  233.       // Organization (store) invite email setup
  234.       async sendInvitationEmail(data) {
  235.         const inviteLink = `https://indendex.com/accept-invitation/${data?.id}`;
  236.         sendOrganizationInvitation({
  237.           email: data?.email,
  238.           invitedByUsername: data?.inviter?.user?.firstName,
  239.           invitedByEmail: data?.inviter?.user?.email,
  240.           // teamName: data.organization?.name,
  241.           inviteLink,
  242.         });
  243.       },
  244.  
  245.       // The role and statement setup
  246.       ac,
  247.       roles: {
  248.         owner,
  249.         manager,
  250.         cashier,
  251.         marketer,
  252.         customer_support,
  253.       },
  254.       schema: {
  255.         organization: {
  256.           additionalFields: {
  257.             businessName: {
  258.               type: "string",
  259.               input: true,
  260.               required: false,
  261.               defaultValue: null,
  262.             },
  263.             businessCategory: {
  264.               type: "string",
  265.               input: true,
  266.               required: false,
  267.               defaultValue: null,
  268.             },
  269.             storeUrl: {
  270.               type: "string",
  271.               input: true,
  272.               required: false,
  273.               defaultValue: null,
  274.             },
  275.             storeTag: {
  276.               type: "string",
  277.               input: true,
  278.               required: false,
  279.               defaultValue: null,
  280.             },
  281.             banner: {
  282.               type: "string",
  283.               input: true,
  284.               required: false,
  285.               defaultValue: null,
  286.             },
  287.             status: {
  288.               type: "string",
  289.               input: true,
  290.               required: false,
  291.               defaultValue: null,
  292.             },
  293.             description: {
  294.               type: "string",
  295.               input: true,
  296.               required: false,
  297.               defaultValue: null,
  298.             },
  299.             whitelabel: {
  300.               type: "string",
  301.               input: true,
  302.               required: false,
  303.               defaultValue: null,
  304.             },
  305.             phoneNumber: {
  306.               type: "string",
  307.               input: true,
  308.               required: false,
  309.               defaultValue: null,
  310.             },
  311.             website: {
  312.               type: "string",
  313.               input: true,
  314.               required: false,
  315.               defaultValue: null,
  316.             },
  317.             city: {
  318.               type: "string",
  319.               input: true,
  320.               required: false,
  321.               defaultValue: null,
  322.             },
  323.             region: {
  324.               type: "string",
  325.               input: true,
  326.               required: false,
  327.               defaultValue: null,
  328.             },
  329.             address: {
  330.               type: "string",
  331.               input: true,
  332.               required: false,
  333.               defaultValue: null,
  334.             },
  335.             country: {
  336.               type: "string",
  337.               input: true,
  338.               required: false,
  339.               defaultValue: null,
  340.             },
  341.             zipCode: {
  342.               type: "string",
  343.               input: true,
  344.               required: false,
  345.               defaultValue: null,
  346.             },
  347.             currency: {
  348.               type: "string",
  349.               input: true,
  350.               required: false,
  351.               defaultValue: null,
  352.             },
  353.             facebook: {
  354.               type: "string",
  355.               input: true,
  356.               required: false,
  357.               defaultValue: null,
  358.             },
  359.             instagram: {
  360.               type: "string",
  361.               input: true,
  362.               required: false,
  363.               defaultValue: null,
  364.             },
  365.             twitter: {
  366.               type: "string",
  367.               input: true,
  368.               required: false,
  369.               defaultValue: null,
  370.             },
  371.             tiktok: {
  372.               type: "string",
  373.               input: true,
  374.               required: false,
  375.               defaultValue: null,
  376.             },
  377.             linkedin: {
  378.               type: "string",
  379.               input: true,
  380.               required: false,
  381.               defaultValue: null,
  382.             },
  383.             storeBaseCurrency: {
  384.               type: "string",
  385.               input: true,
  386.               required: false,
  387.               defaultValue: null,
  388.             },
  389.  
  390.             modifyProductState: {
  391.               type: "boolean",
  392.               input: true,
  393.               required: false,
  394.               defaultValue: null,
  395.             },
  396.             storeApproval: {
  397.               type: "boolean",
  398.               input: true,
  399.               required: false,
  400.               defaultValue: null,
  401.             },
  402.           },
  403.         },
  404.       },
  405.     }),
  406.   ],
  407.  
  408.   //   User reset password here
  409.   // sendResetPassword: async ({ user, url, token }, request) => {
  410.   //   await sendEmail({
  411.   //     to: user?.email,
  412.   //     subject: "Reset Your Password",
  413.   //     text: `Click the link to reset your password: ${url}`,
  414.   //   });
  415.   // },
  416.  
  417.   // Infering user setup
  418.   user: {
  419.     // Allow user to change email.
  420.     changeEmail: {
  421.       enabled: true,
  422.       sendChangeEmailVerification: async (
  423.         { user, newEmail, url, token },
  424.         request
  425.       ) => {
  426.         await sendEmail({
  427.           to: user.email, // verification email must be sent to the current user email to approve the change
  428.           subject: "Approve email change",
  429.           text: `Click the link to approve the change: ${url}`,
  430.         });
  431.       },
  432.     },
  433.  
  434.     // Additional fields for users
  435.     additionalFields: {
  436.       firstName: {
  437.         type: "string",
  438.       },
  439.       middleName: {
  440.         type: "string",
  441.       },
  442.       lastName: {
  443.         type: "string",
  444.       },
  445.       phoneNumber: {
  446.         type: "string",
  447.       },
  448.     },
  449.   },
  450.  
  451.   // Database hooks setup
  452.   databaseHooks: {
  453.     // Session setup
  454.     session: {
  455.       create: {
  456.         before: async (session) => {
  457.           const member = await prisma.member.findFirst({
  458.             where: {
  459.               userId: session?.userId ?? "",
  460.             },
  461.             select: {
  462.               organizationId: true,
  463.             },
  464.           });
  465.  
  466.           // console.log("Session Before Hook:", {
  467.           //   sessionId: session?.userId,
  468.           //   member,
  469.           // });
  470.  
  471.           return {
  472.             // The organizationId is added to the User's Session
  473.             data: {
  474.               ...session,
  475.               ...(member?.organizationId && {
  476.                 activeOrganizationId: member?.organizationId,
  477.               }),
  478.               status: "active",
  479.             },
  480.           };
  481.         },
  482.       },
  483.     },
  484.   },
  485. });
  486.  
Advertisement
Add Comment
Please, Sign In to add comment