0

So I currently have a nextjs app that uses trpc and prisma. I had previously had a user able to invite another person to their 'household', by entering their email into a form, and a secret token would be generated and emailed to the person.

They would then go to the homepage of the app, and when logging in first time with next/auth, would be redirected to /firstLogin, a page where you can either create your own Household or join another household that you were invited to by inputting the secret code that was emailed to you.

What I am now trying to achieve is to instead of sending a code via email, have a Join Household button in the email that sends the token as url params and then joins them to the household when clicked.

The problem I am having currently is that when a user clicks on the link with the token in the params, they are redirected to the site and the api call is blocked as unauthorized because it is a protectedProcedure, as it needs access to the session object.

Is there a way to use a publicProcedure and still obtain access to the session object? Or should I be going about this a different way?

Here is the procedure in question:

joinByInviteCode: protectedProcedure
    .input(z.object({ inviteCode: z.string() }))
    .mutation(async ({ ctx: { prisma, session }, input: { inviteCode } }) => {
      const currUser = await prisma.invite.findUnique({
        where: {
          token: inviteCode,
        },
      });
      if (!currUser)
        throw new Error("This email address hasn't been sent an invitation!");

      if (currUser.token !== inviteCode)
        throw new Error("The invite code is incorrect!");

      if (!currUser.householdId) throw new Error("No householdId!");

      await prisma.household.update({
        where: {
          householdId: currUser.householdId,
        },
        data: {
          members: {
            connect: {
              id: session.user.id,
            },
          },
        },
      });

      await prisma.user.update({
        where: {
          id: session.user.id,
        },
        data: {
          householdId: currUser.householdId,
        },
      });

      await prisma.invite.delete({
        where: { token: inviteCode },
      });
      return currUser.householdId;
    }),

and here is where it's called:

/firstLogin/joinByEmail/[token].tsx

const HouseholdAccessPage = () => {
  const router = useRouter();
  const token = Array.isArray(router.query.token)
    ? router.query.token[0]
    : (router.query.token as string);

  const { setHouseholdId, householdId } = useContext(GlobalContext);

  console.log(token);

  const joinByInviteCodeRoute = api.useContext().household;

  const joinByInviteCode = api.invite.joinByInviteCode.useMutation({
    onSuccess: async () => {
      toast.success("Successfully joined household!");
      joinByInviteCode.data !== undefined &&
        setHouseholdId(joinByInviteCode.data);
      await joinByInviteCodeRoute.getHouseholdId.invalidate();
      await router.push(`/household/${householdId}`);
    },
  });

  token && joinByInviteCode.mutate({ inviteCode: token });

  return <div>Joining household...</div>;
};
Mossy82
  • 115
  • 1
  • 9

0 Answers0