User Authentication with Next.js and Strapi

at the end of the day, I quit, i am having too many problems with next-auth. So I am using nookies.

FYI: This post is using next-auth V3, it is currrently at V4 which may give you errors if you use this code in an existing project and just npm install next-auth

There are many things wrong in the article and unmentioned. Most notably:

  1. The article states it was updated for Strapi v4 yet the docker-compose YAML refers to the Strapi v3 Docker image. Which essentially makes the entire article for Strapi v3. Looking further it is stated that a v4 Docker image is delayed cause of other priorities. Probably the same ones as making Node v16 still mandatory.
  2. The article uses next-auth v3 but the newest next-auth version is v4 which introduced breaking changes.

Please either take the article offline or update it because as we can see here it only brings confusion and wastes peoples time in figuring out the right stuff - which should have been mentored in the article provided by Strapi.

5 Likes

I got the google auth working with next-auth 4.5.0 and strapi v4, i have done many big and small changes as following:

pages/api/auth/[…nextauth].js

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

const options = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  database: process.env.NEXT_PUBLIC_DATABASE_URL,
  callbacks: {
    async session({ session, token, user }) {
      session.jwt = token.jwt;
      session.id = token.id;
      return session;
    },
    async jwt({ token, user, account }) {
      if (user) {
        const response = await fetch(
          `${process.env.NEXT_PUBLIC_API_URL}/api/auth/google/callback?access_token=${account.access_token}`
        );
        const data = await response.json();
        token.jwt = data.jwt;
        token.id = data.user.id;
      }
      return token;
    },
  },
};

const Auth = (req, res) => NextAuth(req, res, options);

export default Auth;

.env

NEXT_PUBLIC_API_URL=http://localhost:1337
NEXT_PUBLIC_DATABASE_URL=postgres://5432:5432@localhost:5432/strapi?synchronize=true
NEXTAUTH_URL=http://localhost:3000
GOOGLE_CLIENT_ID="your google client id"
GOOGLE_CLIENT_SECRET="your google client secret"
NEXTAUTH_SECRET=**This should be the JWT_SECRET from backend's .env file**

Use import { signIn } from "next-auth/react"; “instead of next-auth/client”
also i didnt use docker for the backend

4 Likes

I keep getting the error Error: This action with HTTP GET is not supported by NextAuth.js.

This is the code from ‘api/auth/[…nextauth].jsx’

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

const options = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  secret: process?.env?.NEXT_PUBLIC_SECRET,
  callbacks: {
    async session({ session, token }) {
      session.jwt = token.jwt;
      session.id = token.id;
      return session;
    },
    async jwt({ token, user, account }) {
      if (user) {
        const response = await fetch(
          `${process.env.NEXT_PUBLIC_API_URL}/auth/${account.provider}/callback?access_token=${account.access_token}`
        );
        const data = await response.json();
        token.jwt = data.jwt;
        token.id = data.user.id;
      }
      return token;
    },
  },
};

const Auth = (req, res) => NextAuth(req, res, options);

export default Auth;

I have set the redirect URIs in the google console to be :

https://frontend.com/api/auth/callback/google
https://backend.com/api/auth/callback/google

Also, I have set the redirect URI in strapi to be :

https://frontend.com/api/auth/callback/google

Thanks for taking the time to post this, it saved me a good few hours using strapi 4.3.4 and next auth 4.10.
All working as expected now.

The tutorial code in the article is written for next-auth 3 (no longer maintained) - the article might have been updated for strapi 4 but not for next auth 4) there are lots of differences captured in Sahil’s code.

You don’t need to specify the database connection (in fact this is a next-auth 3 specific way of configuration)

When testing locally, strapi (at least v4) will provision the (google authenticated) account if it doesn’t exist already (i.e. add a record to the users collection).

just in case, you have an X on the extension on api/auth/[…nextauth].jsX

I got this at first until I realized I’d created […nextauth.js] in the root api folder and not in /pages/api/auth

Here is the working example with latest versions.

Thanks for the comments above. Issues were related to response structure from strapi v4.

import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

const options = {
	providers: [
		GoogleProvider({
			clientId: process.env.GOOGLE_CLIENT_ID,
			clientSecret: process.env.GOOGLE_CLIENT_SECRET,
		}),
	],
	session: {
		jwt: true,
	},
	callbacks: {
		async session({ session, token, user }) {
			session.jwt = token.jwt;
			session.id = token.id;
			return session;
		},
		async jwt({ token, user, account }) {
			if (user) {
				const response = await fetch(
					`${process.env.NEXT_PUBLIC_API_URL}/api/auth/google/callback?access_token=${account.access_token}`
				);
				const data = await response.json();
				token.jwt = data.jwt;
				token.id = data.user.id;
			}
			return token;
		},
	},
};

const Auth = (req, res) => NextAuth(req, res, options);

export default Auth;

EDIT: Database URL not needed on frontend.

thank you so much for this, it worked for me

yes you’re right, we need updated guide

This one worked for me, thank you.

For others coming for solutions, here are a few bits that I found:

In pages/api[...nextauth].js:

The jwt callback must fetch
${process.env.NEXT_PUBLIC_API_URL}/api/auth/google/callback?access_token=${account.access_token}
to get the whole token details.

If you get an error saying: Grant: missing session or misconfigured provider:

I thought the v3 => v4 changed the URL to
${process.env.NEXT_PUBLIC_API_URL}/api/connect/google/callback?access_token=${account.access_token} which is wrong, that’s for the callback if Strapi initiated the OAuth flow and would receive a code, not an access token.

Duplicate accounts

Currently Strapi can’t handle several auth providers per user/email.

Even if a user with the same email is already registered and you set "One account per email address to TRUE (at /admin/settings/users-permissions/advanced-settings), Strapi will create a new user with the same email address, but a different ID when you sign in with a new Provider.

e.g.

  1. Settings are set to “One user per email address”
  2. user test@domain.com has signed up with local credentials - and gets assigned user ID 7
  3. user test@domain.com tries to sign in with their google account of the same email
  4. NextAuth calls /api/auth/google/callback?access_token=${account.access_token}
  5. Strapi creates new user with email test@domain.com and user ID 23
  6. User is logged in with User ID 23, any changes to user with ID 23 result in an error “Email already taken”

That’s a known issue and has been for a long time.

There is a “feature request” on the roadmap here:

And many Github issues like this one that just got closed with no comment or a “we might think about maybe doing something about it later”.

Just another sad moment developing with Strapi.

Make sure you think very thoroughly if you want to start a new Strapi project, changes are often random, lots of breaking changes and incomplete features. Many things get stuck at “works for us”.
In regards to auth providers, make sure you choose wisely which ones you offer, as for the foreseeable future we won’t be able to have several providers per user or change providers.

1 Like

Hi!
I have been trying to make this work, and have been semi-successful :smiley:
Frontend does login/out. However, strapi doesn’t notice it…

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

const options = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
  ],

  secret: process.env.NEXT_PUBLIC_SECRET,
  session: {
    jwt: true,
  },
  callbacks: {
    //the 'user' remains never read?!
    async session({ session, token, user }) {
      session.jwt = token.jwt;
      session.id = token.id;
      console.log(session);
      return session;
    },
    async jwt({ token, user, account }) {
      console.log(token, user, account);
      if (user) {
        //prettier-ignore
        const response = await fetch
(`${process.env.NEXTAUTH_URL}/api/auth/google/callback?access_token=${account?.access_token}`);
        const data = await response.text();
        console.log(data);
        token.jwt = data.jwt;

        // WARNING: if the followung line says 'token.id=data.user.id', it breaks!!!
        token.id = data.id;
      }
      // console.log(token.id);
      return token;
    },
  },
};

const Auth = (req, res) => NextAuth(req, res, options);

export default Auth;

I left the logs in, for anyone who might try :wink:

Thanks for sharing

I feel you

The github source code does not seem to have been updated

If anyone is still facing the mentioned problems, I hope this short gist will help:
User Authentication in Next.js with Auth.js and Strapi

3 Likes

Thank you for sharing.

Hey on above end point i am getting 400 bad request error and error message is like forbidden access, in my case for google authentication it’s working fine but when it come to linkedIn it will give me an 400 bad request error.
please let me know the solution of this