Next.js Discord

Discord Forum

Client side auth/token state

Unanswered
Citrus blackfly parasitoid posted this in #help-forum
Open in Discord
Citrus blackfly parasitoidOP
I prefer to use Next.js as a backend-for-frontend to have SSR and all its features, but have a full standalone external backend. For Auth I use Keycloak and I use the access/refresh token from keycloak as the user session. The validity of those tokens is confirmed via JWKs.

I want to make requests to the backend API from both Next.js Server Actions/API routes (that apply further modifications to the request/response) and the client side. Access and refresh token are currently stored in a http only cookie, but the access token should be revealed to the client with an HTTP endpoint and stored in memory.

Usually I would use a react context for this and that would be totally fine. But I kinda want to have something like an axios client (or similar library) that has interceptors that automatically add the token. I dont want the axios client to be only available with a hook, so I would like to have the token accessible outside of the react scope.

I don't know what the best option would be here?

- Use zustand, since zustand state can be queried outside of the react scope. But how is the state initialized? Have some sort of wrapper component that does the fetching/refreshing and updates the zustand state? It sort of feels like an antipattern. With react context you cannot have your state without the wrapper, but this feels so "disconnected" from the component? Nothing guarantees that there is a component that does indeed provide the user
- Just don't have a static API client and always manually get the token from a react context and add it. This would kinda remove the possibility of doing stuff like retrying with a new token since I'd need to add that on every http call
- Make a hook that creates the api client as that hook would have access to the auth state. Would definitely work, but I'm not sure I like this approach either?
- Mabye something else I'm missing?

I think the zustand approach might be the best one here tho. Would love to hear your opinions

8 Replies

Silky ant
I ran into the same issue with Axios it can’t read httpOnly cookies in Next.js SSR, and I prefer not to rely on extra packages. So I made a fetch wrapper that reads the token per request and runs safely on the server (server actions, route handlers, server components), returning responses in a consistent format and handling things like automatic redirects on unauthorized. For client-side requests, I expose a separate wrapper that uses a memory-only access token, so we get safe, reusable API clients in both environments.
@Silky ant I ran into the same issue with Axios it can’t read httpOnly cookies in Next.js SSR, and I prefer not to rely on extra packages. So I made a fetch wrapper that reads the token per request and runs safely on the server (server actions, route handlers, server components), returning responses in a consistent format and handling things like automatic redirects on unauthorized. For client-side requests, I expose a separate wrapper that uses a memory-only access token, so we get safe, reusable API clients in both environments.
Citrus blackfly parasitoidOP
For client-side requests, I expose a separate wrapper that uses a memory-only access token, so we get safe, reusable API clients in both environments.
How exactly did you design that client side wrapper/how does it get access to the memory-only access token? Did you make e.g. a custom hook to get that fetch client? Or do you have any method to get your access token outside of the react scope (so outside of a react component) Cuz that's what I'm struggling with
Silky ant
i did not have same use-case as you but i guess u can do it this way:
Create a Zustand store then Export a helper like authStore = { get: () => useAuthStore.getState().token }

Initialize in Layout use a small Client Component in your root layout that fetches the token from your API and calls authStore.set(token) once on mount returning null.

and u can keep axios just import that authStore helper. In your interceptor, you can call authStore.get() directly
and because getState() is just a plain JS function, you can call it inside your Axios interceptor file without being 'trapped' inside a React Hook or Component
@Silky ant and because getState() is just a plain JS function, you can call it inside your Axios interceptor file without being 'trapped' inside a React Hook or Component
Citrus blackfly parasitoidOP
Thats what I was thinking about doing, yeah. I was just kinda worried that there is nothing ensuring that this component is actually mounted. (e.g. with react context you'd get an error if you tried to use the hook without it being mounted)
But I guess I am over-complicating things for myself. The only reason I am thinking about having an error is because I want to make a little boilerplate project (Since the stack I mentioned (Next.js, Keycloak auth, external backend) is sort of my default stack right now) to get these things started more easily. But I guess I can just document that this specific component needs to be mounted for client side auth.

Also, I mentioned axios just as an example. Anything like it will do. I am probably going to use something different anyway but I guess it was the most popular example to explain what I wanted to do.
Silky ant
i hope i helped u even if this helps a bit.
and yeah you just need to make sure that initializer component is mounted in your root layout
For api calls anything like axios will work but for me i just builded on top of native fetch for my use cases