I’m developing the frontend of an application with NextJS 14. I have configured a CredentialsProvider with NextAuth, user provides email and password and it is sent to the Backend (SpringBoot) to authenticate them and the Backend sends a JWT if the authentication is successful.
I store the access token in a cookie to use it when I make a request to the backend (using an Authentication header) but I’m not sure if there’s a better approach. And in some pages it is causing problems to retrieve the cookie.
Can I store it in the session? If so, how?
This is my credentials provider:
import type { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { cookies } from "next/headers";
const loginPath: string = process.env.NEXT_PUBLIC_LOGIN_PATH!;
const parsePayload = (token: string) => {
try {
return JSON.parse(atob(token.split('.')[1]));
} catch (e) {
return null;
}
};
export const authOptions: NextAuthOptions = {
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {label: "Username", type: "text"},
password: {label: "Password", type: "password"},
},
async authorize (credentials) {
const {username, password} = credentials as {
username: string;
password: string;
};
const formData = new URLSearchParams();
formData.append("email", encodeURIComponent(username));
formData.append("password", encodeURIComponent(password));
const res = await fetch (loginPath, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: formData.toString(),
});
const receivedToken = await res.json();
if (res.ok && receivedToken) {
const user = receivedToken;
const parsed = parsePayload(receivedToken.access_token);
user.name = parsed.name;
cookies().set({
name: "gat",
value: receivedToken.access_token,
httpOnly: true});
cookies().set({
name: "grt",
value: receivedToken.refresh_token,
httpOnly: true});
return user;
} else {
return null;
}
},
}),
],
callbacks: {
jwt: async ({token, user}: {token: any, user: any}) => {
if (user) token = user as unknown as {[key: string]: any};
return token;
},
session: async ({ session, token }: {session: any, token: any}) => {
session.user = { ...token }
return session;
},
},
pages: {
signIn: "/login",
},
};
The problem is that I have access to that "gat" cookie only from server components using:
import { cookies } from 'next/headers'
console.log("GAT: " + cookies().get("gat")?.value);
And if I need to make a call from a client component I need to pass that value to the client component as a parameter, which I find pretty awful…
2
Answers
In the end I solved it defining API Routes.
Instead of calling the SpringBoot backend from client components, I defined API Routes (https://nextjs.org/docs/pages/building-your-application/routing/api-routes) so I don't need to pass the cookie content to client components.
For example:
/api/session/route.tsx
and from client components I call this function as follows:
first create file for ActionServer:
ServerActions.ts
you can get server side:
page.ts:
and for client :