diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml index a25f7b87..ce0832dd 100644 --- a/.github/pr-labeler.yml +++ b/.github/pr-labeler.yml @@ -13,10 +13,10 @@ fauna: ["packages/adapter-fauna/**/*"] firebase: ["packages/adapter-firebase/**/*"] hasura: ["packages/adapter-hasura/**/*"] frameworks: ["packages/frameworks-*/**/*"] -legacy: ["packages/next-auth/**/*"] mikro-orm: ["packages/adapter-mikro-orm/**/*"] mongodb: ["packages/adapter-mongodb/**/*"] neo4j: ["packages/adapter-neo4j/**/*"] +next-auth: ["packages/next-auth/**/*"] pg: ["packages/adapter-pg/**/*"] playgrounds: ["apps/playgrounds/**/*"] pouchdb: ["packages/adapter-pouchdb/**/*"] diff --git a/.gitignore b/.gitignore index a6fd7155..5800a764 100644 --- a/.gitignore +++ b/.gitignore @@ -30,13 +30,6 @@ dist .cache-loader packages/next-auth/providers packages/next-auth/src/providers/oauth-types.ts -packages/next-auth/client -packages/next-auth/css -packages/next-auth/utils -packages/next-auth/core -packages/next-auth/jwt -packages/next-auth/react -packages/next-auth/next packages/*/*.js packages/*/*.d.ts packages/*/*.d.ts.map @@ -90,7 +83,13 @@ packages/core/providers packages/core/src/lib/pages/styles.ts docs/docs/reference/core docs/docs/reference/sveltekit +docs/docs/reference/nextjs +# Next.js +packages/next-auth/lib +packages/next-auth/providers +# copied from @auth/core +packages/next-auth/src/providers # SvelteKit packages/frameworks-sveltekit/index.* diff --git a/.prettierignore b/.prettierignore index fa687a93..ffd87297 100644 --- a/.prettierignore +++ b/.prettierignore @@ -31,6 +31,7 @@ coverage dist packages/**/*.cjs packages/**/*.js +!packages/*/scripts/*.js # @auth/core packages/core/src/providers/oauth-types.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 4e6785bc..dd5a0507 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,7 @@ { "files.exclude": { "packages/core/{lib,providers,*.js,*.d.ts,*.d.ts.map}": true, - "packages/next-auth/{client,core,css,jwt,next,providers,react,utils,*.js,*.d.ts}": true }, "typescript.tsdk": "node_modules/typescript/lib", "openInGitHub.remote.branch": "main" -} +} \ No newline at end of file diff --git a/apps/dev/nextjs/app/api/protected/route.ts b/apps/dev/nextjs/app/api/protected/route.ts new file mode 100644 index 00000000..6f512019 --- /dev/null +++ b/apps/dev/nextjs/app/api/protected/route.ts @@ -0,0 +1,7 @@ +import { auth } from "auth" +import { NextResponse } from "next/server" + +export const GET = auth(function GET(req) { + if (req.auth) return NextResponse.json(req.auth) + return NextResponse.json({ message: "Not authenticated" }, { status: 401 }) +}) diff --git a/apps/dev/nextjs/app/auth/[...nextauth]/route.ts b/apps/dev/nextjs/app/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..0f99f6b1 --- /dev/null +++ b/apps/dev/nextjs/app/auth/[...nextauth]/route.ts @@ -0,0 +1,15 @@ +import { handlers } from "auth" +const { GET: AuthGET, POST } = handlers +export { POST } + +import type { NextRequest } from "next/server" + +// Showcasing advanced initialization in Route Handlers +export async function GET(request: NextRequest) { + // Do something with request + const response = await AuthGET(request) + // Do something with response + return response +} + +// export const runtime = "edge" diff --git a/apps/dev/nextjs/app/client.tsx b/apps/dev/nextjs/app/client.tsx new file mode 100644 index 00000000..39c5a41b --- /dev/null +++ b/apps/dev/nextjs/app/client.tsx @@ -0,0 +1,16 @@ +"use client" + +import { signIn, useSession } from "next-auth/react" + +export default function Client() { + const { data: session, update, status } = useSession() + return ( +
+ {status === "loading" ? "Loading..." : JSON.stringify(session, null, 2)}
+
+
+
+ + This is an example site to demonstrate how to use{" "} + NextAuth.js for authentication. +
+ > + ) +} diff --git a/apps/dev/nextjs/app/server-component/page.tsx b/apps/dev/nextjs/app/server-component/page.tsx deleted file mode 100644 index 0c1e90e6..00000000 --- a/apps/dev/nextjs/app/server-component/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { unstable_getServerSession } from "next-auth/next" - -export default async function Page() { - const session = await unstable_getServerSession() - return{JSON.stringify(session, null, 2)}
-}
diff --git a/apps/examples/nextjs/pages/styles.css b/apps/dev/nextjs/app/styles.css
similarity index 95%
rename from apps/examples/nextjs/pages/styles.css
rename to apps/dev/nextjs/app/styles.css
index 0b3f9ee3..7d0a6be3 100644
--- a/apps/examples/nextjs/pages/styles.css
+++ b/apps/dev/nextjs/app/styles.css
@@ -6,7 +6,7 @@ body {
max-width: 680px;
margin: 0 auto;
background: #fff;
- color: #333;
+ color: var(--color-text);
}
li,
diff --git a/apps/dev/nextjs/auth.config.ts b/apps/dev/nextjs/auth.config.ts
new file mode 100644
index 00000000..5520bd24
--- /dev/null
+++ b/apps/dev/nextjs/auth.config.ts
@@ -0,0 +1,52 @@
+import type { NextAuthConfig } from "next-auth"
+import Auth0 from "next-auth/providers/auth0"
+import Credentials from "next-auth/providers/credentials"
+import Facebook from "next-auth/providers/facebook"
+import GitHub from "next-auth/providers/github"
+import Google from "next-auth/providers/google"
+import Twitter from "next-auth/providers/twitter"
+
+declare module "next-auth" {
+ /**
+ * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
+ */
+ interface Session {
+ user: {
+ /** The user's postal address. */
+ address: string
+ } & User
+ }
+
+ interface User {
+ foo: string
+ }
+}
+
+export default {
+ debug: false,
+ providers: [
+ GitHub({ account() {} }),
+ Auth0,
+ Facebook,
+ Google,
+ Twitter,
+ Credentials({
+ credentials: { password: { label: "Password", type: "password" } },
+ authorize(c) {
+ if (c.password !== "password") return null
+ return {
+ id: "test",
+ foo: "bar",
+ name: "Test User",
+ email: "test@example.com",
+ }
+ },
+ }),
+ ],
+ callbacks: {
+ jwt({ token, trigger, session }) {
+ if (trigger === "update") token.name = session.user.name
+ return token
+ },
+ },
+} satisfies NextAuthConfig
diff --git a/apps/dev/nextjs/auth.ts b/apps/dev/nextjs/auth.ts
new file mode 100644
index 00000000..bce598bc
--- /dev/null
+++ b/apps/dev/nextjs/auth.ts
@@ -0,0 +1,19 @@
+import NextAuth from "next-auth"
+import Email from "next-auth/providers/email"
+import authConfig from "auth.config"
+import { PrismaClient } from "@prisma/client"
+import { PrismaAdapter } from "@auth/prisma-adapter"
+
+globalThis.prisma ??= new PrismaClient()
+
+// authConfig.providers.push(
+// // Start server with `pnpm email`
+// // @ts-expect-error
+// Email({ server: "smtp://127.0.0.1:1025?tls.rejectUnauthorized=false" })
+// )
+
+export const { handlers, auth, signIn, signOut, update } = NextAuth({
+ // adapter: PrismaAdapter(globalThis.prisma),
+ session: { strategy: "jwt" },
+ ...authConfig,
+})
diff --git a/apps/dev/nextjs/components/access-denied.js b/apps/dev/nextjs/components/access-denied.js
deleted file mode 100644
index 4c9c0d79..00000000
--- a/apps/dev/nextjs/components/access-denied.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { signIn } from "next-auth/react"
-
-export default function AccessDenied() {
- return (
- <>
- - { - e.preventDefault() - signIn() - }} - > - You must be signed in to view this page - -
- > - ) -} diff --git a/apps/examples/nextjs/components/access-denied.tsx b/apps/dev/nextjs/components/access-denied.tsx similarity index 100% rename from apps/examples/nextjs/components/access-denied.tsx rename to apps/dev/nextjs/components/access-denied.tsx diff --git a/apps/dev/nextjs/components/footer.js b/apps/dev/nextjs/components/footer.tsx similarity index 86% rename from apps/dev/nextjs/components/footer.js rename to apps/dev/nextjs/components/footer.tsx index de1a2f4a..2e8eb8ee 100644 --- a/apps/dev/nextjs/components/footer.js +++ b/apps/dev/nextjs/components/footer.tsx @@ -1,6 +1,6 @@ import Link from "next/link" import styles from "./footer.module.css" -import packageJSON from "package.json" +import packageJSON from "next-auth/package.json" export default function Footer() { return ( @@ -11,7 +11,7 @@ export default function Footer() { Documentation
- {!session && (
- <>
-
- You are not signed in
-
-
- Sign in
-
- >
- )}
- {session && (
- <>
- {session.user.image && (
-
- )}
-
- Signed in as
-
- {session.user.email}
- {session.user.name ? `(${session.user.name})` : null}
-
-
- Sign out
-
- >
- )}
-
The examples below show responses from the example API endpoints.
-- You must be signed in to see responses. -
-/api/examples/session
- -/api/examples/jwt
- -This page uses the useSession() React Hook in the{" "} @@ -22,6 +20,6 @@ export default function Page() { The disadvantage of useSession() is that it requires client side JavaScript.
-Response:
-
- {JSON.stringify(response, null, 2)}
-
- Response:
-
- {JSON.stringify(response, null, 2)}
-
- {response ? "Response:" : "Session:"}
+
+ {JSON.stringify(response ?? session, null, 2)}
+
+ >
+ )
+ }
+
+ return (
+ <>
+ Response:
+
+ {JSON.stringify(response, null, 2)}
+
+ >
+ )
+}
diff --git a/apps/dev/nextjs/pages/email.js b/apps/dev/nextjs/pages/email.tsx
similarity index 93%
rename from apps/dev/nextjs/pages/email.js
rename to apps/dev/nextjs/pages/email.tsx
index a83dc550..7bea7ec6 100644
--- a/apps/dev/nextjs/pages/email.js
+++ b/apps/dev/nextjs/pages/email.tsx
@@ -1,10 +1,10 @@
// eslint-disable-next-line no-use-before-define
import * as React from "react"
import { signIn, signOut, useSession } from "next-auth/react"
-import Layout from "components/layout"
export default function Page() {
- const [response, setResponse] = React.useState(null)
+ const [response, setResponse] =
+ React.useState
{JSON.stringify(response, null, 2)}
- - This is an example site to demonstrate how to use{" "} - NextAuth.js for authentication. -
-This is an example site to demonstrate how to use{" "} Auth.js for authentication. @@ -18,15 +16,11 @@ export default function Page() { SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- This site uses JSON Web Tokens and an in-memory database which resets - every ~2 hours. -
Data provided to this site is exclusively used to support signing in and is not passed to any third party services, other than via SMTP or OAuth for the purposes of authentication.
-- {content} -
-+ {content} +
+ > + ) +} + +export const getServerSideProps: GetServerSideProps = async (context) => { + const session = await auth(context) + if (session) { + // Note usually you don't need to fetch from an API route in getServerSideProps + // This is done here to demonstrate how you can fetch from a third-party API + // with a valid session. Likely you would also not pass cookies but an `Authorization` header + const hostname = + process.env.NEXTAUTH_URL ?? + (process.env.VERCEL + ? "https://next-auth-example-v5.vercel.app" + : "http://localhost:3000") + const res = await fetch(`${hostname}/api/examples/protected`, { + headers: { cookie: context.req.headers.cookie ?? "" }, + }) + return { props: { session, content: await res.json() } } + } + + return { props: {} } +} diff --git a/apps/dev/nextjs/pages/protected.js b/apps/dev/nextjs/pages/protected.tsx similarity index 77% rename from apps/dev/nextjs/pages/protected.js rename to apps/dev/nextjs/pages/protected.tsx index 88951afb..e56d0e73 100644 --- a/apps/dev/nextjs/pages/protected.js +++ b/apps/dev/nextjs/pages/protected.tsx @@ -1,11 +1,8 @@ import { useState, useEffect } from "react" import { useSession } from "next-auth/react" -import Layout from "../components/layout" export default function Page() { - const { status } = useSession({ - required: true, - }) + const { status } = useSession({ required: true }) const [content, setContent] = useState() // Fetch content from protected route @@ -21,15 +18,15 @@ export default function Page() { fetchData() }, [status]) - if (status === "loading") return{content}
-- This page uses the unstable_getServerSession() method - in getServerSideProps(). -
-- Using unstable_getServerSession() in{" "} - getServerSideProps() is currently the recommended - approach, although the API may still change, if you need to support - Server Side Rendering with authentication. -
-- Using getSession() is still recommended on the client. -
-- The advantage of Server Side Rendering is this page does not require - client side JavaScript. -
-- The disadvantage of Server Side Rendering is that this page is slower to - render. -
-{JSON.stringify(data, null, 2)}
- - You can also use Supabase in API routes. See the code in the - `/pages/api/examples/supabase-rls.js` file. -
-- You must be signed in to see responses. -
-/api/examples/supabase-rls
- -Data fetched during SSR from Supabase with RSL enabled:
-{JSON.stringify(data, null, 2)}
-
+ Open Source. Full Stack. Own Your Data.
## Overview -NextAuth.js is a complete open-source authentication solution. +NextAuth.js is a complete open source authentication solution. This is an example application that shows how `next-auth` is applied to a basic Next.js app. The deployed version can be found at [`next-auth-example.vercel.app`](https://next-auth-example.vercel.app) -Go to [next-auth.js.org](https://next-auth.js.org) for more information and documentation. +### About NextAuth.js + +NextAuth.js is an easy to implement, full-stack (client/server) open source authentication library originally designed for [Next.js](https://nextjs.org) and [Serverless](https://vercel.com). Our goal is to [support even more frameworks](https://github.com/nextauthjs/next-auth/issues/2294) in the future. + +Go to [next-auth.js.org](https://authjs.dev) for more information and documentation. + +> _NextAuth.js is not officially associated with Vercel or Next.js._ ## Getting Started @@ -66,19 +67,19 @@ You **can** skip configuring a database and come back to it later if you want. For more information about setting up a database, please check out the following links: -- Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview) +- Docs: [authjs.dev/reference/adapters](https://authjs.dev/reference/core/adapters) ### 3. Configure Authentication Providers -1. Review and update options in `pages/api/auth/[...nextauth].js` as needed. +1. Review and update options in `auth.ts` as needed. 2. When setting up OAuth, in the developer admin page for each of your OAuth services, you should configure the callback URL to use a callback path of `{server}/api/auth/callback/{provider}`. e.g. For Google OAuth you would use: `http://localhost:3000/api/auth/callback/google` -A list of configured providers and their callback URLs is available from the endpoint `/api/auth/providers`. You can find more information at https://next-auth.js.org/configuration/providers/oauth +A list of configured providers and their callback URLs is available from the endpoint `api/auth/providers`. You can find more information at https://authjs.dev/getting-started/oauth-tutorial -3. You can also choose to specify an SMTP server for passwordless sign in via email. +1. You can also choose to specify an SMTP server for passwordless sign in via email. ### 4. Start the application @@ -97,13 +98,15 @@ npm run start ### 5. Preparing for Production -Follow the [Deployment documentation](https://authjs.dev/guides/basics/deployment) or deploy the example instantly using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-auth-example) - -[](https://vercel.com/new/git/external?repository-url=https://github.com/nextauthjs/next-auth-example&project-name=next-auth-example&repository-name=next-auth-example) +Follow the [Deployment documentation](https://authjs.dev/guides/basics/deployment) ## AcknowledgementsThanks to Vercel sponsoring this project by allowing it to be deployed for free for the entire Auth.js Team
\ No newline at end of file +Thanks to Vercel sponsoring this project by allowing it to be deployed for free for the entire NextAuth.js Team
+ +## License + +ISC diff --git a/apps/examples/nextjs/app/api-example/page.tsx b/apps/examples/nextjs/app/api-example/page.tsx new file mode 100644 index 00000000..5f337c8a --- /dev/null +++ b/apps/examples/nextjs/app/api-example/page.tsx @@ -0,0 +1,34 @@ +"use client" +import CustomLink from "@/components/custom-link" +import { useEffect, useState } from "react" + +export default function Page() { + const [data, setData] = useState() + useEffect(() => { + ;(async () => { + const res = await fetch("/api/protected") + const json = await res.json() + setData(json) + })() + }, []) + return ( +
+ This page fetches data from an API{" "}
+ auth()
+
+ {JSON.stringify(data, null, 2)}
+
+
+ This page is protected by using the universal{" "}
+ auth()
+
+ This is an example site to demonstrate how to use{" "}
+
+ Current{" "}
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
++ This site uses JSON Web Tokens and an in-memory database which resets + every ~2 hours. +
++ Data provided to this site is exclusively used to support signing in + and is not passed to any third party services, other than via SMTP or + OAuth for the purposes of authentication. +
+
+ This page is server-rendered as a{" "}
+ auth()
+
+ This page fetches session data client side using the{" "}
+ useSession
+
+ It needs the{" "}
+ 'use client'
+ SessionProvider
+ client-example/page.tsx
+ {" "}
+ to provide the session data.
+