Kamil Brodziak
Kamil Brodziak

Reputation: 97

Can't pass http only cookie from .net api to next-auth and then to frontend

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

Answers (1)

Kamil Brodziak
Kamil Brodziak

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

Related Questions