next-auth icon indicating copy to clipboard operation
next-auth copied to clipboard

Session not received as authenticated during a GET request

Open formerskulk4 opened this issue 2 years ago • 21 comments

Provider type

Credentials

Environment

System: OS: Windows 10 10.0.19044 CPU: (4) x64 Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz Memory: 5.06 GB / 15.87 GB Binaries: Node: 14.17.5 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.17 - ~\AppData\Roaming\npm\yarn.CMD npm: 8.1.3 - C:\Program Files\nodejs\npm.CMD Browsers: Edge: Spartan (44.19041.1266.0), Chromium (99.0.1150.46) Internet Explorer: 11.0.19041.1566 npmPackages: next: ^12.0.10 => 12.0.10 next-auth: ^4.2.1 => 4.2.1 react: 17.0.2 => 17.0.2

Reproduction URL

https://github.com/ParidV/Dua-n-pu

Describe the issue

getSession returning null in Get requests, in POST request the session is okay.

Component

`export async function getServerSideProps(context) {
  const res = await axios.get(`${process.env.API_URL}/company/jobs`);
  return {
    props: {
      jobs: res.data.jobs,
    },
  };
}`

API Request api/company/jobs/index.js

import { getSession } from "next-auth/react";

export default async (req, res) => {
  const { method } = req;
  switch (method) {
    case "GET":
      try {
        const session = await getSession({ req });

        console.log(session + " SESSION "); //RETURN NULL

        console.log(JSON.stringify(session));
        const jobs = await prisma.jobs.findMany({
          where:{
            userId: session.id
          }
        });
        return res.status(200).json({ success: true, jobs });
      } catch (error) {
        return res.status(404).json({
          success: false,
          message: error.message,
        });
      }

How to reproduce

In POST request, the session is okay, but when I make a get request to get the ID from the server to pass in the axios request come as NULL.

Expected behavior

It should return the session

formerskulk4 avatar Mar 23 '22 16:03 formerskulk4

I've setup a quick reproduction based on your API route code provided above and it seems to work for me. It will return null if you're unauthenticated. However, if you copy a request "as curl" out of your browsers network dev tools (when authenticated) which includes the token/cookie and modify the URL to the api route, I got a response and the dev server terminal logged the session successfully.

Can you double check the GET requests you're making are actually also authenticated (i.e. including the cookie or authorization header?

ndom91 avatar Mar 23 '22 19:03 ndom91

I can confirm that with the GET request, authenticated does not receive the session. I tried with POST on This request and it works perfectly. I created a job from the POST request and I received this by console.log(session)

{
  user: { name: 'Company', email: '[email protected]' },
  expires: '2022-04-22T19:23:39.673Z',
  id: 2,
  name: 'Company',
  surname: 'Surname',
  email: '[email protected]',
  role: 2
}

I tried a POST request from postman and it says that I am not logged in, which is okay, but when I am authenticated from my project, the session is retrieved successfully. So this problem happens only with GET

This is the request from cuRL from the get request:

curl "http://localhost:3000/company/jobs" ^
  -H "Connection: keep-alive" ^
  -H "sec-ch-ua: ^\^" Not A;Brand^\^";v=^\^"99^\^", ^\^"Chromium^\^";v=^\^"99^\^", ^\^"Google Chrome^\^";v=^\^"99^\^"" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "sec-ch-ua-platform: ^\^"Windows^\^"" ^
  -H "DNT: 1" ^
  -H "Upgrade-Insecure-Requests: 1" ^
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36" ^
  -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" ^
  -H "Sec-Fetch-Site: none" ^
  -H "Sec-Fetch-Mode: navigate" ^
  -H "Sec-Fetch-User: ?1" ^
  -H "Sec-Fetch-Dest: document" ^
  -H "Accept-Language: en-US,en;q=0.9,de-DE;q=0.8,de;q=0.7,sq;q=0.6" ^
  -H "Cookie: _ga=GA1.1.622907214.1643984174; next-auth.csrf-token=c2db1395200a91b1915039666fe73b4d36a5fd49a430dffac15d0d704b6613dc^%^7C07336a24d0a4cc0a2dd92548d698de66d84e5f3daf1e28b5592e3a2cb9ee871f; next-auth.callback-url=http^%^3A^%^2F^%^2Flocalhost^%^3A3000^%^2F; next-auth.session-token=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..k5rKQz-UT6gGL2O8.381fW_sK8GZ-zo8PA3gS1V7xmwBvpYMc_EkgNx9wcbxKh9KsHGVe1adOrIUfexJeoxpP_WpBc1rpp3BIUdlPcLPCbmgor_WjHLbRhmOLQd7mW2bZN8C7DyZm3JlUdBCX6iIpOicYeNJSI6dtooVQkC136X1izbyaP3E92z56CiKbEhLVLd0o-SZZItreAvWXD04RjlqAhAmqovvwrcB-A1POYDxfsw2rR3Vf.UQuUbRZhvGPXwDV8nJB7Zg" ^
  --compressed

formerskulk4 avatar Mar 23 '22 19:03 formerskulk4

Okay, alternatively you can give our experimental getServerSession a try. Something like this:

import { getServerSession } from "next-auth/next";
import { authOptions } from './auth/[...nextauth]'

export default async (req, res) => {
  const { method } = req;
  switch (method) {
    case "GET":
      try {
        const session = await getServerSession({req, res}, authOptions)
        console.log("SESSION", session)

        res.status(200).json({
          message: "Success",
          session
        })
      } catch (error) {
        console.error(error);
        res.status(500).json({ error: error.message });
      }
  }
}

I was also able to get this ^^ to successfully work.

We've released it as a preview in the latest release, and there are two open PRs which provide more documentation for it which you can take a look at:

  • https://github.com/nextauthjs/next-auth/pull/4116
  • https://github.com/nextauthjs/next-auth/pull/3982

ndom91 avatar Mar 23 '22 19:03 ndom91

Thank you for your reply, but what is authOptions because referring to my [...nextauth.js] and I do not have a authOptions, in this way can you double check the procedure if the authentication is done correctly (for security issues or any other problem such as GET request, even though I think has nothing to do with [..nextauth].js )

import NextAuth from "next-auth";
import CredentialProvider from "next-auth/providers/credentials";
import axios from "axios";

export default NextAuth({
  providers: [
    CredentialProvider({
      name: "credentials",
      async authorize(credentials) {
        try {
          const user = await axios.post(
            `${process.env.API_URL}/auth/authentication/login`,
            {
              email: credentials.email,
              password: credentials.password,
            }
          );

          if (!user.data.user) {
            return null;
          }

          if (user.data.user) {
            return {
              id: user.data.user.id,
              name: user.data.user.name,
              surname: user.data.user.surname,
              email: user.data.user.email,
              role: user.data.user.role,
            };
          }
        } catch (error) {
          console.error(error);
        }
      },
    }),
  ],
  callbacks: {
    jwt: ({ token, user }) => {
      if (user) {
        token.id = user.id;
        token.name = user.name;
        token.surname = user.surname;
        token.email = user.email;
        token.role = user.role;
      }
      // Here, check the token validity date
      if (token.tokenExpiration < Date.now()) {
        // Call the endpoint where you handle the token refresh for a user
        const user =  axios.post(
          `${process.env.API_URL}/auth/authentication/refresh`,
          {
            refreshToken: token.refreshToken,
          }
        );
        // Check for the result and update the data accordingly
        return { ...token, ...user };
      }
      return token;
    },
    session: ({ session, token }) => {
      if (token) {
        session.id = token.id;
        session.name = token.name;
        session.surname = token.surname;
        session.email = token.email;
        session.role = token.role;
      }
      return session;
    },
  },
  secret: process.env.SECRET_KEY,
  jwt: {
    secret: process.env.SECRET_KEY,
    encryption: true,
    maxAge: 5 * 60 * 1000,
  },
  pages: {
    signIn: "/auth/login",
  },
});

formerskulk4 avatar Mar 23 '22 19:03 formerskulk4

I also gave a try with getServerSession, but still no success.

API request

        const session = await getServerSession({ req, res }, authOptions);
        console.log(session);

        console.log("SESSION", session);

        if (!session) {
          return res.status(401).json({
            message: "You are not logged in",
          });
        }

[..nextauth].js

import NextAuth from "next-auth";
import CredentialProvider from "next-auth/providers/credentials";
import axios from "axios";

export const authOptions = {
  providers: [
    CredentialProvider({
      name: "credentials",
      async authorize(credentials) {
        try {
          const user = await axios.post(
            `${process.env.API_URL}/auth/authentication/login`,
            {
              email: credentials.email,
              password: credentials.password,
            }
          );

          if (!user.data.user) {
            return null;
          }

          if (user.data.user) {
            return {
              id: user.data.user.id,
              name: user.data.user.name,
              surname: user.data.user.surname,
              email: user.data.user.email,
              role: user.data.user.role,
            };
          }
        } catch (error) {
          console.error(error);
        }
      },
    }),
  ],
  callbacks: {
    jwt: ({ token, user }) => {
      if (user) {
        token.id = user.id;
        token.name = user.name;
        token.surname = user.surname;
        token.email = user.email;
        token.role = user.role;
      }
      // Here, check the token validity date
      if (token.tokenExpiration < Date.now()) {
        // Call the endpoint where you handle the token refresh for a user
        const user = axios.post(
          `${process.env.API_URL}/auth/authentication/refresh`,
          {
            refreshToken: token.refreshToken,
          }
        );
        // Check for the result and update the data accordingly
        return { ...token, ...user };
      }
      return token;
    },
    session: ({ session, token }) => {
      if (token) {
        session.id = token.id;
        session.name = token.name;
        session.surname = token.surname;
        session.email = token.email;
        session.role = token.role;
      }
      return session;
    },
  },
  secret: process.env.SECRET_KEY,
  jwt: {
    secret: process.env.SECRET_KEY,
    encryption: true,
    maxAge: 5 * 60 * 1000,
  },
  pages: {
    signIn: "/auth/login",
  },
};

export default NextAuth(authOptions);

formerskulk4 avatar Mar 23 '22 20:03 formerskulk4

I have created a new project to test that and it is still with the same problem,

  1. First I have authenticated
  2. Visited http://localhost:3000/api/hello
  3. Still the session was null

Image

formerskulk4 avatar Mar 23 '22 21:03 formerskulk4

@formerskulk4 getSession() or getServerSession() will return you null if the user is not authenticated. Have you verified that the user is logged in when calling the function?

ubbe-xyz avatar Apr 02 '22 19:04 ubbe-xyz

I am positive that the user is authenticated, it is weird because when POST request is used there is no null returned.

ParidV avatar Apr 02 '22 21:04 ParidV

Hey im having this problem as well the user is authenticated

i'm seeing a next-auth.session-token on cookies as well but when i try to request GET on getServerSideProps it will return me null on getSession on Handler(/api)

but if i use client side fetching it works perfectly

my code

/api/tasks/index.js

const handler = async (req, res) => {
    if (req.method === "GET") {
     const session = await getSession({ req });

    if (!session) {
      res.status(401).json({ message: "Not Authenticated" });
      return;
    }
    const userId = session.user.id;
    const client = await connectDb();
    const db = client.db();
    const tasks = await db
      .collection("tasks")
      .find({ user_id: userId })
      .toArray();
    res.status(200).json(tasks);
  }
};

index.js

export const getServerSideProps = async (context) => {

  const res = await fetch(`http://localhost:3000/api/tasks`);
  const data = await res.json();
  return {
    props: { data },
  };
};

where i fetch here now i get a message: "Not Authenticated" (i'm logged in 100% sure)

useEffect works perfectly fine(client fetching)

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(`http://localhost:3000/api/tasks`);
      const data = await res.json();
      console.log(data);
    };
    fetchData();
  }, []);

markmendozadev avatar Apr 04 '22 09:04 markmendozadev

@markmendozadev

When you use the POST request does it work? Because I am having the same issue, I am positive that the user is authenticated however here returns null on the server side.

ParidV avatar Apr 04 '22 13:04 ParidV

use this code in your _app.js :

function MyApp({Component, pageProps}) { return ( <SessionProvider session={pageProps.session}> <Component {...pageProps} /> </SessionProvider> ); }

jaminecode avatar Apr 04 '22 15:04 jaminecode

@jaminecode I have already that.

export default function App({ Component, pageProps }) { return ( <SessionProvider session={pageProps.session}> <Component {...pageProps} /> </SessionProvider> ); }

ParidV avatar Apr 04 '22 16:04 ParidV

Hi @ParidV Yes i think i found my answers already but not 100% sure. It works on client-side because getSession works on client-side requests.

if you do a request on your api through client side (not using any next data fetching just pure react it will 100% work) i mean the authentication.

now i did a little digging and here's what i found (https://github.com/nextauthjs/next-auth/pull/4116)

@jaminecode my _app.js already like that

import "../styles/globals.css";
import { SessionProvider } from "next-auth/react";
import Layout from "../components/layout/Layout";

function MyApp({ Component, pageProps: { session, ...pageProps } }) {
  return (
    <SessionProvider session={session}>
      <Layout>
        <Component {...pageProps} />
      </Layout>
    </SessionProvider>
  );
}
export default MyApp;

markmendozadev avatar Apr 04 '22 21:04 markmendozadev

@markmendozadev maybe this is a late response, but I faced the same issue with POST method, I could access the session, but not with POST, after doing some research, I found this is because NextJS does not send the cookies in the header for GET method, so I fixed the issue like this:

image

`let res = await fetch("http://localhost:3000/api/posts", { method: "GET", headers: {

  "Content-Type": "application/json",
  // I have to add cookie in the GET 
  Cookie: context.req.headers.cookie,
},

});`

luismbcr avatar Sep 20 '22 02:09 luismbcr