-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Next.js 13 app
directory
#5647
Comments
Would this already work with the experimental |
I will try it today
|
import { use } from "react";
import { unstable_getServerSession } from "next-auth/next";
export default function Page(props) {
console.log({ props }); // { props: { params: {}, searchParams: {} } }
const session = use(unstable_getServerSession()); //TS2345: Argument of type '[]' is not assignable to parameter of type '[IncomingMessage & { cookies: Partial<{ [key: string]: string; }>; }, ServerResponse<IncomingMessage>, NextAuthOptions] | [...]'. Type '[]' is not assignable to type '[NextApiRequest, NextApiResponse<any>, NextAuthOptions]'. Source has 0 element(s) but target requires 3.
return (
<>
<h1>Hi</h1>
</>
);
} There is no access to req and response. Using getSession also not working, even with |
According to this: vercel/next.js#41745 (reply in thread)
I created "use client";
import { SessionProvider } from "next-auth/react";
export default function RootLayout({
children,
...props
}: {
children: React.ReactNode;
}) {
console.log("layout", { props }); // empty
return (
<html>
<head></head>
<body>
<SessionProvider session={props.session}>{children}</SessionProvider> // session not exists
</body>
</html>
);
} And some "ClientSide" component: "use client";
import { useState } from "react";
import { useSession } from "next-auth/react";
export default function Counter() {
const session = useSession();
const [count, setCount] = useState(0);
console.log({ session }); // empty
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
} Where |
Current output is:
|
The below approach worked for me app/dashboard/layout.tsx "use client";
import { SessionProvider } from "next-auth/react";
export interface DashboardLayoutProps {
children: React.ReactNode;
}
export default function DashboardLayout({ children }: DashboardLayoutProps) {
return (
<SessionProvider>
<ProfileMenu />
{children}
</SessionProvider>
);
} app/dashboard/ProfileMenu.tsx "use client";
import { useSession } from "next-auth/react";
export default function ProfileMenu() {
const { data } = useSession();
return (
<div>
<p>{data?.user?.name}</p>
<p>{data?.user?.email}</p>
</div>
);
} |
Correction of my previous comment; Instead of marking the layout as a client component, it's better to wrap the app/dashboard/AuthContext.tsx "use client";
import { SessionProvider } from "next-auth/react";
export interface AuthContextProps {
children: React.ReactNode;
}
export default function AuthContext({ children }: AuthContextProps) {
return <SessionProvider>{children}</SessionProvider>;
} app/dashboard/layout.tsx import AuthContext from "./AuthContext";
import ProfileMenu from "./ProfileMenu";
export interface AccountLayoutProps {
children: React.ReactNode;
}
export default function AccountLayout({ children }: AccountLayoutProps) {
return (
<AuthContext>
<ProfileMenu />
{children}
</AuthContext>
);
} |
@ehsanbigzad https://next-auth.js.org/tutorials/securing-pages-and-api-routes#nextjs-middleware
I did have it rendering using |
This worked for me as well. The only thing left is to get the request/response object to get the session on the layout server component and pass it to AuthContext. I can't find a way to do so on the docs tho. |
As @ehsanbigzad has mentioned, the above solution works as an easy migration for now. 👀
As for the server components, you can try the following code: const session = await fetch("/api/auth/session").then (r => r.json() The API could definitely be improved, though, so it's open to suggestions! |
This not optimize I think |
Already tried that, but the session object comes empty, even after I log in. Not sure if I'm missing something. This is what I have: import { Session } from 'next-auth';
import { headers } from 'next/headers';
import Link from 'next/link';
import React, { use } from 'react';
import AuthContext from 'src/app/AuthContext';
import '../styles/globals.css';
interface RootLayoutProps {
children: React.ReactNode;
}
async function getSession(cookie: string): Promise<Session> {
const response = await fetch('http://localhost:3000/api/auth/session', {
headers: {
cookie,
},
});
const session = await response.json();
return Object.keys(session).length > 0 ? session : null;
}
export default async function RootLayout({ children }: RootLayoutProps) {
const session = await getSession(headers().get('cookie') ?? '');
return (
<div className="text-white">
<AuthContext session={session}>{children}</AuthContext>
</div>
);
} |
Tangentially related: server components do provide access to headers and cookies (which is basically what's needed for session management), but these are read-only for now. It seems client side (as seen above) or middleware really are the only options at the moment, until Vercel implements setting cookies (which the docs say they are working on). |
How did you handle the pages/api/auth/[...nextauth].js section on NEXT JS 13 as api routes are not available right now ? |
The pages folder still works alongside the app folder. |
Tried these approaches but always get
|
Nevermind actually, the underlying issue came from trpc / react-query to everyone who stumbles on this as well. |
You can get trpc working with next13's app directory? |
Didn't investigate further, I guess all packages will soon'ish get their respective patches and all hacks will be obsolete anyways :) Edit: This might help you: https://github.com/trpc/next-13 |
Hi all, I'm giving Next 13 a shot and have it working, however, when running my test it seems that the session isn't being wrapped. Here's what I have.
'use client';
import { SessionProvider } from 'next-auth/react';
export interface AuthContextProps {
children: React.ReactNode;
}
export default function AuthContext({ children }: AuthContextProps) {
return <SessionProvider>{children}</SessionProvider>;
}
import './globals.css';
import { ReactNode } from 'react';
import { headers } from 'next/headers';
import AuthContext from './AuthContext';
import { Session } from 'next-auth';
export interface RootLayoutProps {
children: ReactNode;
}
async function getSession(cookie: string): Promise<Session> {
const response = await fetch('http://localhost:3000/api/auth/session', {
headers: {
cookie
}
});
const session = await response.json();
return Object.keys(session).length > 0 ? session : null;
}
export default async function RootLayout({ children }: RootLayoutProps) {
const session = await getSession(headers().get('cookie') ?? '');
return (
<html lang="en">
<head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<AuthContext session={session}>{children}</AuthContext>
</body>
</html>
);
}
'use client';
import Counter from '@/components/UserTest';
import { signIn } from 'next-auth/react';
import Image from 'next/image';
import styles from './page.module.css';
export default function Home() {
return (
<div className={styles.container}>
<main className={styles.main}>
<a
href={`/api/auth/signin`}
onClick={(e) => {
e.preventDefault();
signIn();
}}
>
Sign in
</a>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js 13!</a>
</h1>
<Counter />
<p className={styles.description}>
Get started by editing{' '}
<code className={styles.code}>app/page.tsx</code>
</p>
<div className={styles.grid}>
<a href="https://beta.nextjs.org/docs" className={styles.card}>
<h2>Documentation →</h2>
<p>Find in-depth information about Next.js 13</p>
</a>
<a
href="https://github.com/vercel/next.js/tree/canary/examples"
className={styles.card}
>
<h2>Examples →</h2>
<p>Explore the Next.js 13 playground.</p>
</a>
<a
href="https://vercel.com/templates/next.js/app-directory?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
className={styles.card}
>
<h2>Deploy →</h2>
<p>Deploy your Next.js site to a public URL with Vercel.</p>
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
);
}
'use client';
import { useSession } from 'next-auth/react';
export default function UserTest() {
const { data } = useSession();
return (
<>
{console.log(data?.user)}
<h1>Hi {data?.user?.name}</h1>
</>
);
}
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
export const authOptions = {
providers: [
CredentialsProvider({
name: 'Sign in',
// generate a suitable form on the sign in page.
credentials: {
username: {
label: 'Email',
type: 'email',
placeholder: 'hello@example.com'
},
password: { label: 'Password', type: 'password' }
},
async authorize(credentials, req) {
// const res = await fetch('/your/endpoint', {
// method: 'POST',
// body: JSON.stringify(credentials),
// headers: { 'Content-Type': 'application/json' }
// });
// const user = await res.json();
// If no error and we have user data, return it
// if (res.ok && user) {
// return user;
// }
// return test user
return { id: 1, name: 'J Smith', email: 'jsmith@example.com' };
// Return null if user data could not be retrieved
// return null;
}
})
],
theme: {
colorScheme: 'dark'
}
};
export default NextAuth(authOptions); When running my tests I get the following:
Here is the test import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import Home from '@/app/page';
import '@testing-library/jest-dom';
describe('Index.js', () => {
test('that the title Welcome to Next.js! exists on the page', () => {
render(<Home />);
const title = screen.getByRole('heading', {
name: /welcome to next\.js 13!/i,
level: 1
});
expect(title).toBeInTheDocument();
}); So my question is how do I go about passing userSession to the test? |
Update all, here is how I'm mocking the session for tests. import React from 'react';
import { render, screen } from '@testing-library/react';
import Home from '@/app/page';
import '@testing-library/jest-dom';
import { useSession } from 'next-auth/react';
jest.mock('next-auth/react');
describe('Index.js', () => {
test('that the title Welcome to Next.js! exists on the page', () => {
async () => {
(useSession as jest.Mock).mockReturnValueOnce([false, false]);
render(<Home />);
const title = screen.getByRole('heading', {
name: /welcome to next\.js 13!/i,
level: 1
});
expect(title).toBeInTheDocument();
};
});
test('that the sign in button is clickable', () => {
async () => {
const mockSession = {
expires: new Date(Date.now() + 2 * 86400).toISOString(),
user: { name: 'Test User', email: 'testuser@example.dev' }
};
(useSession as jest.Mock).mockReturnValueOnce([
mockSession,
'authenticated'
]);
render(<Home />);
const signIn = screen.getByRole('link', {
name: 'Sign in'
});
const userName = screen.findByText('Hi J Smith');
expect(signIn).toBeInTheDocument();
expect(userName).toBeInTheDocument();
};
});
}); Could be moved into some helpers but this gets the job done for now |
If someone is in need of getting the session while server-side-rendering: import "server-only";
import { NextAuthHandler } from "next-auth/core";
import { Session } from "next-auth";
import { headers, cookies } from "next/headers";
import { authOptions } from "../pages/api/auth/[...nextauth]";
export const getSession = async (options = authOptions) => {
const session = await NextAuthHandler<Session | {} | string>({
options,
req: {
host: headers().get("x-forwarded-host") ?? "http://localhost:3000",
action: "session",
method: "GET",
cookies: Array.from(cookies().entries()).reduce(
(acc, [key]) => ({ ...acc, [key]: cookies().get(key) }),
{}
),
headers: headers(),
},
});
return session;
}; Probably contains a bug or two, but seemed to unblock me. |
I stumbled across a repo that implements Next Auth (along with many other stuff) in Next 13, might be used for reference: https://github.com/shadcn/taxonomy |
istill working for you? (I try use and keep getting undefined) |
I am trying this approach with the Next.js i18n routing example. I am calling |
@alessandrojcm You need to remove |
Thanks, missed that @atreya2011 |
I'm experiencing this without |
Please update here once a stable solution is available for app directory. |
is there any solution now for app directory? |
I can place the
Anybody else experiencing the same? |
Looking forward to the solution. |
The workaround I am using is :
|
I'm using NextAuth version 4.22.1 with Next.js version 13.4. I'm getting erro 'MISSING_NEXTAUTH_API_ROUTE_ERROR'. My auth provider is Auth0. |
Did you created the route handler inside |
Yeah, I've got the same error as @tusharshuklaa. And yes I'e got my route handler in |
Found the issue, it was my habit of camelCasingTheFileNames. I had this path |
It is still not working for me. Here is my route:
What could be the problem here? (still getting the same error message) |
@Gyurmatag what does your sign in page looks like? You are declaring a custom sign in |
Yeah, here you go:
What could be the problem? |
Can you please help me @dvrd or @balazsorban44? |
Ditto, using |
@MoltenCoffee Next.js has released server-actions, which allow us to gather cookies from server-side components. What does this mean for fetching session server-side in Next.js 13 using the |
Since AuthContext uses 'use client' and wraps the layout. Will the children in layout become part of the client bundle then? |
components/session-provider.tsx "use client";
import { SessionProvider } from "next-auth/react";
export { SessionProvider }; app/layout.tsx import { SessionProvider } from "./_components/session-provider";
import { getServerSession } from "next-auth";
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await getServerSession(authOptions);
return (
<html lang="en">
<body>
<SessionProvider session={session}>{children}</SessionProvider>
</body>
</html>
);
} |
In my case MISSING_NEXTAUTH_API_ROUTE_ERROR deploying on Vercel was the result of my initially committing incorrect casing of Debugging by viewing the Source in Vercel showed the issue, and I removed/readded the |
Description 📓
Add some hook to use like that:
How to reproduce ☕️
.
Contributing 🙌🏽
Yes, I am willing to help implement this feature in a PR
The text was updated successfully, but these errors were encountered: