Reputation: 97
I am trying to pass (jwt) htmlOnly cookie from .net api to frontend, but jwt fetch is in authorize method of next-auth.
When I'm trying to fetch directly from frontend (react) - cookie is being passed and everything works perfectly, but when I am doing fetch in next-auth - cookie is not passed to frontend.
.NET Cookie set
CookieOptions cookieOptions = new CookieOptions {
HttpOnly = true,
Expires = token.Expires,
Secure = true,
SameSite = SameSiteMode.None,
Path = "/",
IsEssential = true,
Domain = "localhost"
};
context.Response.Cookies.Append(CookiesNames.RefreshToken, token.Token, cookieOptions);
.NET 6.0 config:
builder.Services.ConfigureApplicationCookie(options => {
options.Cookie.Name = "refreshToken";
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
app.UseCors(x => x.WithOrigins(new string[] { "localhost:3000" })
.AllowAnyMethod()
.AllowAnyHeader().WithExposedHeaders("jwt")
.WithExposedHeaders("jwtExpireDate").WithExposedHeaders("jwtCreatedAt").WithExposedHeaders("Set-Cookie")
.SetIsOriginAllowed(origin => true) // allow any origin
.AllowCredentials()); // allow credentials
next-auth authorize
async authorize(credentials) {
const login = async () =>
fetch(`https://localhost:7183/api/login`, {
method: "POST",
credentials: "include",
body: JSON.stringify({
Email: credentials.email,
Password: credentials.password,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}).then((resp) => {
const cookies = resp.headers.get("set-cookie");
// response.setHeader("Set-Cookie", cookies);
return resp;
});
const res = await login();
const user = res.json();
if (!res.ok) {
throw new Error(user.exception);
}
if (res.status === 200 && user) {
return user;
}
return null;
},
}),
I also wrapped next-auth like this:
const authOptions = (req, response) => ({...})
export default (req, res) => {
return NextAuth(req, res, authOptions(req, res));
};
But still, cookie doesn't go to frontend and even if i set it like this:
response.setHeader("Set-Cookie", cookies);
cookie is being set, but it doesn't go back to .net api.
How to setup http only cookie route from .net api to next-auth to frontend and from frontend to next-auth and then to .net api? I don't even need it in next-auth, I just want it to be set and be accessible in .net api.
Upvotes: 2
Views: 1815
Reputation: 97
Found solution, but I don't think it is the "right one":
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
const authorize = async (credentials, req, response) => {
const res = await fetch(`https://localhost:7183/api/login`, {
method: "POST",
credentials: "include",
body: JSON.stringify({
Email: credentials.email,
Password: credentials.password,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}).then((resp) => {
const cookies = resp.headers.get("set-cookie");
response.setHeader("Set-Cookie", cookies);
return resp;
});
const user = res.json();
if (!res.ok) {
throw new Error(user.exception);
}
if (res.status === 200 && user) {
return user;
}
return null;
};
const refreshJWT = async (token, user, req, response) => {
if (user?.token) {
token = {
...token,
accessToken: user?.token?.token,
createdAt: Date.parse(new Date(user?.token?.createdAt)),
expireDate: Date.parse(new Date(user?.token?.expireDate)),
};
}
if (token.expireDate < Date.now()) {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/login/refresh`,
{
method: "POST",
body: "",
credentials: "include",
cookies: req.cookies,
headers: {
cookie: req.headers.cookie,
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${token.accessToken}`,
},
}
);
const cookies = res.headers.get("set-cookie");
response.setHeader("Set-Cookie", cookies);
return {
...token,
...user,
accessToken: res.headers.get("jwt"),
createdAt: Date.parse(new Date(res.headers.get("jwtCreatedAt"))),
expireDate: Date.parse(new Date(res.headers.get("jwtExpireDate"))),
};
}
return token;
};
const authOptions = (req, response) => ({
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: {
label: "email",
type: "email",
placeholder: "[email protected]",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
return authorize(credentials, req, response);
},
}),
// ...add more providers here
],
pages: {
signIn: "/login",
},
session: {
maxAge: 7 * 60 * 60 * 24,
},
callbacks: {
async jwt({ token, user }) {
return refreshJWT(token, user, req, response);
},
async session({ session, token }) {
session.accessToken = token.accessToken;
session.user = {
name: token.name,
email: token.email,
picture: token.picture,
};
return session;
},
},
theme: {
colorScheme: "auto", // "auto" | "dark" | "light"
brandColor: "", // Hex color code #33FF5D
logo: "/logo.png", // Absolute URL to image
},
// debug: process.env.NODE_ENV === 'development',
secret: process.env.JWT_SECRET,
});
export { authOptions };
export default (req, res) => {
return NextAuth(req, res, authOptions(req, res));
};
I wonder if setting and getting cookie is necessary - maybe not and I hope someone posts better answer, but for now something like this is working - login + auto jwt refresh
Upvotes: 2