nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.85k stars 3.51k forks source link

Preact will cause the mongodb package to not be found? #2622

Closed Patryks1 closed 3 years ago

Patryks1 commented 3 years ago

Question πŸ’¬

When using nextjs with Preact the database part of auth will fail with -

https://next-auth.js.org/errors#oauth_callback_handler_error Error: no optional dependency [mongodb] defined in peerOptionalDependencies in any package.json
    at find_package_json_with_name (C:\Users\node_modules\require_optional\index.js:56:11)
    at require_optional (C:\Users\node_modules\require_optional\index.js:69:13)
    at Object.getAdapter (C:\Users\node_modules\@next-auth\typeorm-legacy-adapter\dist\index.js:104:55)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async Object.callback (C:\Users\.next\server\pages\api\auth\[...nextauth].js:5308:42)

I have tried to add the OptionalDependenciesand peerOptionalDependencies but both lead to the same error.

Is this a bug / Indented behaviour ? If so is there a way around it while still using preact? Removing pReact will fix this issue.

EDIT: Looking at previous PR's seems like this is more of a bug, Here is extra details if required -

System:
    OS: Windows 10 10.0.19043
    CPU: (24) x64 AMD Ryzen 9 5900X 12-Core Processor
    Memory: 17.34 GB / 31.92 GB
  Binaries:
    Node: 14.17.2 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 7.21.1 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.19041.1023.0), Chromium (92.0.902.84)
    Internet Explorer: 11.0.19041.906
  npmPackages:
    @preact/compat:  17.0.2
    next: ^11.1.0 => 11.1.0
    next-auth: ^3.29.0 => 3.29.0

How to reproduce β˜•οΈ

Sandbox - https://codesandbox.io/s/elated-rumple-yu39t

Contributing πŸ™ŒπŸ½

Yes, I am willing to help answer this question in a PR

balazsorban44 commented 3 years ago

Are you actually using mongodb, or you tried to add it to fix the problem?

We are nearing a major release that will make this problem go away by explicitly requiring users to import the adapters they are using, and in case if you don't, the problem you are reporting just won't exist.

Any chance you want to give it a try?

Check out next-auth@4.0.0-next.24

https://docs-nextauthjs.vercel.app/getting-started/upgrade-v4

API is pretty much stable now, only expected changes are bug fixes/ minor tweaks.

Patryks1 commented 3 years ago

Are you actually using mongodb, or you tried to add it to fix the problem?

We are nearing a major release that will make this problem go away by explicitly requiring users to import the adapters they are using, and in case if you don't, the problem you are reporting just won't exist.

Any chance you want to give it a try?

Check out next-auth@4.0.0-next.24

https://docs-nextauthjs.vercel.app/getting-started/upgrade-v4

API is pretty much stable now, only expected changes are bug fixes/ minor tweaks.

Yeah the actual project i am using next-auth in is using mongodb/Mongoose with next-auth. The sandbox just replicates the issue with same preact setup my main project has.

I will give V4 a go to see if it fixes the issue.

balazsorban44 commented 3 years ago

Here is a pure mongodb adapter for you as well, then!:

https://www.npmjs.com/package/@next-auth/mongodb-adapter

It's a PR, but I hope to merge it soon: https://github.com/nextauthjs/adapters/pull/193

Patryks1 commented 3 years ago

Here is a pure mongodb adapter for you as well, then!:

https://www.npmjs.com/package/@next-auth/mongodb-adapter

It's a PR, but I hope to merge it soon: nextauthjs/adapters#193

Unfortunately does not seem to work with pReact, Seems like the sessionContext wont accept the session being undefined when the user is logged out.

This is the error for more detail

Server Error
TypeError: Cannot read property 'SessionProvider' of undefined

This error happened while generating the page. Any console logs will be displayed in the terminal window.
  39 | <ReduxProvider store={store}>
  40 |   <PersistGate loading={null} persistor={persistor}>
> 41 |     <SessionProvider session={pageProps.session}>
     |     ^
  42 |       <Layout>
  43 |         <DefaultSeo
  44 |           openGraph={{
TypeError: Cannot read property 'SessionProvider' of undefined
    at Object.App (C:\Users\\.next\server\pages\_app.js:6399:137)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2523)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2667)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2667)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2667)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2667)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2667)
    at m (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:2667)
    at y (C:\Users\node_modules\preact-render-to-string\dist\commonjs.js:1:1278)
    at Object.renderPage (C:\Users\node_modules\next\dist\server\render.js:626:31)
    at Function.getInitialProps (C:\Users\.next\server\pages\_document.js:614:19)
    at Function.getInitialProps (C:\Users\P.next\server\pages\_document.js:1158:85)
    at Object.loadGetInitialProps (C:\Users\\node_modules\next\dist\shared\lib\utils.js:68:29)
    at Object.renderToHTML (C:\Users\node_modules\next\dist\server\render.js:643:40)
    at async doRender (C:\Users\node_modules\next\dist\server\next-server.js:1127:38)
    at async C:\Users\node_modules\next\dist\server\next-server.js:1221:28

It works fine when i disable pReact. The setup is identical to the one in the codesandbox

balazsorban44 commented 3 years ago

The error you are seeing is because SessionProvider is not imported correctly, it has nothing to do with the session value being undefined.

Make sure that you have exactly this in your package.json: next-auth: "4.0.0-next.24"

And you use the import next-auth/react in your _app file.

Patryks1 commented 3 years ago

The error you are seeing is because SessionProvider is not imported correctly, it has nothing to do with the session value being undefined.

Make sure that you have exactly this in your package.json: next-auth: "4.0.0-next.24"

And you use the import next-auth/react in your _app file.

Yeah i updated my code with the docs you sent over, Ive cleared all yarn, npm locks + cache as well as deleted node_modules and .next same issue so i dont think its related to my env. Ill update the code sandbox to replicate the issue.

EDIT: also like i mentioned if i disabled pReact it renders the page fine.

package.json

  "dependencies": {
    "@mongoosejs/double": "^0.2.0",
    "@next-auth/mongodb-adapter": "^0.0.2-pr.193-6071c497.276",
    "@reduxjs/toolkit": "^1.6.1",
    "@tailwindcss/forms": "^0.3.2",
    "@tailwindui/react": "^0.1.1",
    "bcryptjs": "^2.4.3",
    "coinbase-commerce-node": "^1.0.4",
    "cookies": "^0.8.0",
    "ioredis": "^4.27.5",
    "mongodb": "^4.1.1",
    "mongoose": "^5.12.7",
    "mongoose-unique-validator": "^2.0.3",
    "next": "11.1.0",
    "next-auth": "4.0.0-next.24",
    "next-images": "^1.7.0",
    "next-plugin-preact": "3.0.6",
    "next-seo": "^4.26.0",
    "next-swagger-doc": "^0.1.8",
    "preact": "10.5.14",
    "preact-render-to-string": "5.1.19",
    "preact-ssr-prepass": "1.2.0",
    "react": "npm:@preact/compat@0.0.4",
    "react-dom": "npm:@preact/compat@0.0.4",
    "react-icons": "^4.2.0",
    "react-multi-carousel": "^2.6.3",
    "react-redux": "^7.2.4",
    "redux-persist": "^6.0.0",
    "swr": "^0.5.6"
  },

_app.tsx


import Layout from '../components/Layout';
import { store } from '../redux/store';
import '../styles/globals.css';
import * as gtag from '../util/gtag';
import { NextPage } from 'next';

                        import { SessionProvider } from 'next-auth/react';

import { DefaultSeo } from 'next-seo';
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';

const App: NextPage<AppProps> = ({ Component, pageProps }) => {
  const router = useRouter();
  const persistor = persistStore(store);

  useEffect(() => {
    const handleRouteChange = (url: string): void => {
      gtag.pageview(url);
    };

    router.events.on('routeChangeComplete', handleRouteChange);

    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router.events]);

  return (
      <ReduxProvider store={store}>
        <PersistGate loading={null} persistor={persistor}>
            <SessionProvider session={pageProps.session}>
                  <Layout>
                    <Component {...pageProps} />
                  </Layout>
            </SessionProvider>
        </PersistGate>
      </ReduxProvider>
  );
};
balazsorban44 commented 3 years ago

Thank you, updating the CodeSandbox will be very helpful! :pray: I'll look into it once you have it.

Patryks1 commented 3 years ago

Thank you, updating the CodeSandbox will be very helpful! πŸ™ I'll look into it once you have it.

Here is the sandbox with the same issue maybe you can spot something - https://codesandbox.io/s/gracious-night-s346g?file=/next.config.js

balazsorban44 commented 3 years ago

I was able to verify that the problem is with next-plugin-preact. Simply removing it in next.config.js resolves the issue. I will try to dig into this and see if we have to make some adjustments to our bundling or is it a next-plugin-preact bug.

balazsorban44 commented 3 years ago

So I did test out our PR #2552 that rewrites the core to TypeScript (and also changes the build process quite a bit), and actually, it resolves the problem without more changes!

If you want to give it a try: next-auth@0.0.0-pr.2552.f73a37a5

Keep in mind, this is still a work in progress. When merged though, your issue here should be resolved.

If you don't like to "live on the edge", I suggest you wait a few days/weeks for us to get the new release over the finish line. :slightly_smiling_face: Hope that helps!

Patryks1 commented 3 years ago

So I did test out our PR #2552 that rewrites the core to TypeScript (and also changes the build process quite a bit), and actually, it resolves the problem without more changes!

If you want to give it a try: next-auth@0.0.0-pr.2552.f73a37a5

Keep in mind, this is still a work in progress. When merged though, your issue here should be resolved.

If you don't like to "live on the edge", I suggest you wait a few days/weeks for us to get the new release over the finish line. πŸ™‚ Hope that helps!

Im down to live on the edge for a while :), That PR does seem to fix the SessionProvider issue however it seems like the google provider does not work.

[...nextauth].js

import { MongoDBAdapter } from '@next-auth/mongodb-adapter';
import { MongoClient, ObjectId } from 'mongodb';
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

const client = new MongoClient('mongodb://127.0.0.1:27017');

export default NextAuth({
 providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
    //TODO: Add Discord and Facebook
  ],
   session: {
     jwt: true,
     maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  debug: process.env.DEBUG_NEXTAUTH === 'true',
  secret: process.env.NEXT_SECRET,

  // TODO: TS interface in types.d.ts is incorrect for JWT options needs to be corrected. 
  jwt: {
    // openssl rand -base64 64
    secret: process.env.JWT_SECRET,
    // jose newkey -s 512 -t oct -a HS512
    signingKey: {
      kty: 'oct',
      kid: '',
      alg: 'HS512',
      k: '',
    },
    verificationOptions: {
      algorithms: ['HS256'],
    },
    encryption: true,
    // You can generate an encryption key by using `npx node-jose-tools newkey -s 256 -t oct -a A256GCM -u enc`
    encryptionKey: process.env.JWT_ENCRYPTION_KEY,
    decryptionKey: process.env.JWT_ENCRYPTION_KEY,
    decryptionOptions: {
      algorithms: ['A256GCM'],
    },
    adapter: MongoDBAdapter({
      db: () => client.db('db'),
      ObjectId,
    }),
});

Error

https://next-auth.js.org/errors#signin_oauth_error Unexpected token o in JSON at position 1 {
  error: {
    message: 'Unexpected token o in JSON at position 1',
    stack: 'SyntaxError: Unexpected token o in JSON at position 1\n' +
      '    at JSON.parse (<anonymous>)\n' +
      '    at Object.encode (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\.next\\server\\pages\\api\\auth\\[...nextauth].js:163:65)\n' +
      '    at createPKCE (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\.next\\server\\pages\\api\\auth\\[...nextauth].js:1977:43)\n' +
      '    at getAuthorizationUrl (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\.next\\server\\pages\\api\\auth\\[...nextauth].js:1623:52)\n' +
      '    at runMicrotasks (<anonymous>)\n' +
      '    at processTicksAndRejections (internal/process/task_queues.js:95:5)\n' +
      '    at async Object.signin (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\.next\\server\\pages\\api\\auth\\[...nextauth].js:3155:32)\n' +
      '    at async NextAuthHandler (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\.next\\server\\pages\\api\\auth\\[...nextauth].js:868:18)\n' +
      '    at async C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\.next\\server\\pages\\api\\auth\\[...nextauth].js:917:32\n' +
      '    at async Object.apiResolver (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\node_modules\\next\\dist\\server\\api-utils.js:101:9)\n' +
      '    at async DevServer.handleApiRequest (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\node_modules\\next\\dist\\server\\next-server.js:760:9)\n' +
      '    at async Object.fn (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\node_modules\\next\\dist\\server\\next-server.js:651:37)\n' +
      '    at async Router.execute (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\node_modules\\next\\dist\\server\\router.js:205:32)\n' +
      '    at async DevServer.run (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\node_modules\\next\\dist\\server\\next-server.js:825:29)\n' +
      '    at async DevServer.handleRequest (C:\\Users\\Pat\\Documents\\GitHub\\Pcific-Nextjs\\node_modules\\next\\dist\\server\\next-server.js:292:20)',
    name: 'SyntaxError'
  },
  provider: {
    id: 'google',
    name: 'Google',
    type: 'oauth',
    wellKnown: 'https://accounts.google.com/.well-known/openid-configuration',
    authorization: { params: [Object] },
    idToken: true,
    checks: [ 'pkce', 'state' ],
    profile: [Function: profile],
    signinUrl: 'http://localhost:3000/api/auth/signin/google',
    callbackUrl: 'http://localhost:3000/api/auth/callback/google',
    clientId: '',
    clientSecret: ''
  },
  message: 'Unexpected token o in JSON at position 1'
}

Sign in function

onClick={() =>
              signIn('google', {
                callbackUrl: `${window.location.origin}/`,
              })
            }
balazsorban44 commented 3 years ago

signingKey is supposed to be a string, you provided it as an object. https://github.com/nextauthjs/next-auth/blob/08349c3a8b77befd63dfb99a3d9fca53ed53dc53/src/lib/jwt.js#L33-L35

You are right about the jwt types, there is an issue related to it #2597

Patryks1 commented 3 years ago

signingKey is supposed to be a string, you provided it as an object.

https://github.com/nextauthjs/next-auth/blob/08349c3a8b77befd63dfb99a3d9fca53ed53dc53/src/lib/jwt.js#L33-L35

You are right about the jwt types, there is an issue related to it #2597

I dont think the mongo adapter is working, the google call is successful now as the JWT is no longer failing. however when it calls getUserByAccount it absolutely spams my termal with it and exits the application.

The OAUTH_CALLBACK_RESPONSE seems correct with all the data.

one more seems like useSession is requesting options parameter can this be changed to optional, In index.d.ts its required atm.

same [...nextauth].js config as above, just without JWT bit.

[next-auth][debug][adapter_getUserByAccount] {
  args: [
    { providerAccountId: 'ACCID', provider: 'google' }
  ]
}
balazsorban44 commented 3 years ago

could you share what you mean by spamming your terminal? I tried myself locally and although didn't use the Google Provider specifically, it did seem to work.

Patryks1 commented 3 years ago

could you share what you mean by spamming your terminal? I tried myself locally and although didn't use the Google Provider specifically, it did seem to work.

All it does is spam the getuserbyaccount posted above in terminal. Before it just displays what Google pauth returns with all of the correct data. Seems like the dB and tables don't get created in mongodb

balazsorban44 commented 3 years ago

What do you mean by spamming specifically? Could you please paste anything printed to the terminal? It might be very helpful information there.

Patryks1 commented 3 years ago

What do you mean by spamming specifically? Could you please paste anything printed to the terminal? It might be very helpful information there.

I dont know if i explaied it wrong or not but it literally just spams the

[next-auth][debug][adapter_getUserByAccount] {
  args: [
    { providerAccountId: 'ACCID', provider: 'google' }
  ]
}

as if it was trying to call the function again if it fails causing the app to exit right after too many calls where made.

here is a pic -

First Second Third

balazsorban44 commented 3 years ago

That is strange, I don't have an explanation. There is no retry mechanism anywhere in the core or the adapter. I'll try to reproduce it locally.

balazsorban44 commented 3 years ago

@Patryks1 I reproduced it locally, will investigate now!

UPDATE: I identified the problem, it wasn't related to the new adapter, actually. It was in core. Should be fixed in next-auth@4.0.0-next.26. Could you give it a try?

Patryks1 commented 3 years ago

@Patryks1 I reproduced it locally, will investigate now!

UPDATE: I identified the problem, it wasn't related to the new adapter, actually. It was in core. Should be fixed in next-auth@4.0.0-next.26. Could you give it a try?

I think you released the wrong version (seems like its main branch not next), But weirdly enough it does work haha.

balazsorban44 commented 3 years ago

Not sure what you mean, here it is on npm https://www.npmjs.com/package/next-auth/v/4.0.0-next.26

it's on the next channel currently.

Patryks1 commented 3 years ago

Not sure what you mean, here it is on npm https://www.npmjs.com/package/next-auth/v/4.0.0-next.26

it's on the next channel currently.

Not sure if i am missing something but in the 4.0.0-next.24 i had to change the useSession hooks, Session provider, next-auth/react does not exist and use the adapter instead of the DB. With 4.0.0-next.26 it seems to use the old structure.

balazsorban44 commented 3 years ago

That would be really surprising. You can verify here it's not the case πŸ‘€ https://unpkg.com/browse/next-auth@4.0.0-next.26/

Patryks1 commented 3 years ago

That would be really surprising. You can verify here it's not the case πŸ‘€ https://unpkg.com/browse/next-auth@4.0.0-next.26/

Seems like it was an yarn issue, using NPM fetched the correct build. Weird how 3.29.0 gives me the mongo dependency issue but if i use 4.0.0-next.26 with yarn it fetches the wrong build but that works fine with preact.

any ways with the actual 4.0.0-next.26 and mongoDbAdapter does the client need to be connected? if so do you have any recommendations how to go about that as i am getting the following.

MongoError: MongoClient must be connected before calling MongoClient.prototype.db
balazsorban44 commented 3 years ago

Yes, hoping to get the adapter PR merged this weekend. until then, check this page:

https://github.com/nextauthjs/adapters/blob/3c949284ad458271a3d12e2eefcfa0cc2581f804/packages/mongodb/README.md

Patryks1 commented 3 years ago

Yes, hoping to get the adapter PR merged this weekend. until then, check this page:

https://github.com/nextauthjs/adapters/blob/3c949284ad458271a3d12e2eefcfa0cc2581f804/packages/mongodb/README.md

Brilliant it worked :). Is

export type RedirectableProvider = "email" | "credentials"

Intended? I use google, fb and discord, I can use them as a provider just fine but ofc typescript will complain. Could this be changes to the old one -

export type RedirectableProvider = "email" | "credentials"

export type SignInProvider = RedirectableProvider | string | undefined
balazsorban44 commented 3 years ago

Awesome! Happy to accept a PR that I can check out locally. let's open a different issue. Sounds like the original problem here is solved! 😊