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>;
};