Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing iron-session with TRPC #638

Open
Sealis04 opened this issue Nov 24, 2023 · 9 comments
Open

Implementing iron-session with TRPC #638

Sealis04 opened this issue Nov 24, 2023 · 9 comments

Comments

@Sealis04
Copy link

Sealis04 commented Nov 24, 2023

So I'm having issues trying to slam iron-session with TRPC. I'm using create-t3-app to start with and before v8, I just used to define (or redefine) IronSession when creating my TRPC context. I assumed V8 was pretty much the same but for some reason, running session.save() inside my procedures doesn't save anything. I tried creating a separate route from trpc to try and see if I can save runs properly, and yes it works. And yes, I can access the session values inside my procedures (but save doesn't work). Any ideas why?

My TRPC Context

export const createTRPCContext = async (opts: { headers: Headers }) => {
  const session = await getIronSession<{ user: string }>(cookies(), sessionOptions);
  return {
    db,
    ...opts,
    session,
  };
};

My procedure (from create-t3 boilerplate)

create: publicProcedure
    .input(z.object({ name: z.string().min(1) }))
    .mutation(async ({ ctx, input }) => {
      // simulate a slow db call
      await new Promise((resolve) => setTimeout(resolve, 1000));
      //This prints out the session.user variable I have set in my custom route
      console.log('old session',ctx.session);
      //this also works
      ctx.session.user = 'new user';
      // This doesn't work
      await ctx.session.save();
      return true;
    })

Edit*
To add, I already tried calling getIronSession inside the procedure instead. Still didn't work.

@vvo
Copy link
Owner

vvo commented Nov 24, 2023

@Sealis04 Thank you, would you be able to create a very simple GitHub repository where I can npm install && npm run dev and see the issue myself?

Are you running this locally? HTTP or HTTPS?
What the sessionOptions in your case (hide the password 👍 )

@vvo
Copy link
Owner

vvo commented Nov 24, 2023

I tried creating a separate route from trpc to try and see if I can save runs properly, and yes it works.

This is reassuring at least, let's see why in some other cases it doesn't.

@Sealis04
Copy link
Author

@Sealis04 Thank you, would you be able to create a very simple GitHub repository where I can npm install && npm run dev and see the issue myself?

Are you running this locally? HTTP or HTTPS? What the sessionOptions in your case (hide the password 👍 )

https://github.com/Sealis04/testironsession

Made two ways to change the session here, one is just the submit in home which goes to the procedure made with TRPC, the one that doesn't save the session. The other is with a separate route from TRPC, just visit /setSession. Also, made /getSession to check if the session values change (which doesn't).

This was with HTTP, as for sessionOptions it's just these ones

export const sessionOptions = {
    password: my_super_secret_cookie_password,
    cookieName: "Random Cookie Name",
    cookieOptions: {
      maxAge: undefined,
      secure: false,
    },
}

@yifever
Copy link

yifever commented Nov 29, 2023

I am having the same problem, following

@marinofranz
Copy link

Was there any solution to this? I am attempting to run save on the session in a tRPC mutation, and when I read the session from another tRPC mutation after I call save, the session object remains unchanged.

@jokull
Copy link

jokull commented Mar 11, 2024

I have a working example of this. I have a special loginProcedure that is used on procedures that write cookies (login, signup, otp verify etc.)

export const loginProcedure = t.procedure.use(
  t.middleware(async ({ ctx, next }) => {
    const result = await next({ ctx });
    const resultCtx = "ctx" in result ? (result.ctx as Context) : undefined;
    if (resultCtx?.sessionUser) {
      // resultCtx is not the ctx passed to `responseMeta` where we need
      // `_session` to set the cookie. We would do it all in `responseMeta` but
      // it is not and awaitable unfortunately.
      ctx._session = await sealData(
        {
          email: resultCtx.sessionUser.email,
          id: resultCtx.sessionUser.id,
        } satisfies SessionUser,
        { password: ctx.env.SECRET, ttl: 60 * 60 * 24 * 365 }, // 1 year
      );
    }
    return result;
  }),
);

Then my fetchRequestHandler has this responseMeta:

responseMeta({ ctx, paths, errors }) {
  const allOk = errors.length === 0;
  if (
    allOk &&
    ctx?._session &&
    paths?.find((path) =>
      ["auth.setPassword", "auth.login", "auth.verifyOtpCode"].includes(
        path,
      ),
    )
  ) {
    return {
      headers: {
        "set-cookie": `__session=${
          ctx._session
        }; Max-Age=2592000; SameSite=Strict; Path=/; ${
          ENV === "development" ? "" : "Secure; "
        }HttpOnly`,
      },
    };
  }
  return {};
}

Not the most elegant way to set cookies, but it works.

@austinwoon
Copy link

So I'm having issues trying to slam iron-session with TRPC. I'm using create-t3-app to start with and before v8, I just used to define (or redefine) IronSession when creating my TRPC context. I assumed V8 was pretty much the same but for some reason, running session.save() inside my procedures doesn't save anything. I tried creating a separate route from trpc to try and see if I can save runs properly, and yes it works. And yes, I can access the session values inside my procedures (but save doesn't work). Any ideas why?

My TRPC Context

export const createTRPCContext = async (opts: { headers: Headers }) => {
  const session = await getIronSession<{ user: string }>(cookies(), sessionOptions);
  return {
    db,
    ...opts,
    session,
  };
};

My procedure (from create-t3 boilerplate)

create: publicProcedure
    .input(z.object({ name: z.string().min(1) }))
    .mutation(async ({ ctx, input }) => {
      // simulate a slow db call
      await new Promise((resolve) => setTimeout(resolve, 1000));
      //This prints out the session.user variable I have set in my custom route
      console.log('old session',ctx.session);
      //this also works
      ctx.session.user = 'new user';
      // This doesn't work
      await ctx.session.save();
      return true;
    })

Edit* To add, I already tried calling getIronSession inside the procedure instead. Still didn't work.

Hey to whoever is having this problem, simply add this to headers in httpLink or httpBatchLink and you should be good

Gist here

@iStorry
Copy link

iStorry commented Apr 17, 2024

I am facing the exact same issue.

@volfadar
Copy link

for anyone that still have the problem, maybe you can try my solution.

after trying so many ways, and of course bcoz i'm too lazy to understand the TRPC concept, at the end i choose to use Server Action for doing mutation the session (save and destroy)

it is very easy to implement rather than trying to understand the whole TRPC concept and React Query (if you use t3 like me).

the cons is you have to manually setup all the necessary React Query stuff to get the benefit of using react query itself like onSuccess, onError, isPending, etc.

or if you're beginner in React Query and you're in hurry just manually setup hooks for handing state or callback after success and error.

you can watch this video from Lama Dev, if you don't know how to Server Action.
https://www.youtube.com/watch?v=p_FiVGxyksI&t=967s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants