From b42c128456e35db2adf72152f79523cbd21af217 Mon Sep 17 00:00:00 2001 From: Nico Domino Date: Wed, 1 Dec 2021 16:57:00 +0100 Subject: [PATCH] feat: migrate to v4 (#40) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: bump nextjs to 11 * feat: migrate to v4.0.0-next.20 * fix: config comments * fix: add nodemailer as manual dependency * feat: add ndom91 as contributor :grin: * fix: provider import * feat: update next-auth * fix: add package-lock * feat: update next-auth to v4-beta.1 * fix: remove carrot * feat: upgrade to beta.2 * fix: filename typo * feat: add Next.js 12 Middleware example page * chore: simplify Middleware * chore: revert * chore(deps): ugprade `next-auth` * chore: change url check * chore: add logs, refactor middleware * fix: use new theme option * Image url value must be quoted within a string template (#46) * chore: remove lock file * chore: update next-auth@4.0.0-beta.7 * fix: exact version * Update _middleware.js * fix: comment out email adapter * feat: next-auth@4.0.1 * fix: cleanup options * fix: rm sqlite3 Co-authored-by: Balázs Orbán Co-authored-by: Shawn Goulet --- FUNDING.yml | 3 ++ components/access-denied.js | 2 +- components/header.js | 88 ++++++++++++++++++++++++--------- package.json | 11 +++-- pages/_app.js | 18 +++---- pages/_middleware.js | 13 +++++ pages/api/auth/[...nextauth].js | 55 +++++++++++---------- pages/api/examples/jwt.js | 4 +- pages/api/examples/protected.js | 4 +- pages/api/examples/session.js | 4 +- pages/middleware-protected.js | 9 ++++ pages/protected.js | 7 +-- pages/server.js | 5 +- 13 files changed, 147 insertions(+), 76 deletions(-) create mode 100644 FUNDING.yml create mode 100644 pages/_middleware.js create mode 100644 pages/middleware-protected.js diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 00000000..505f41dc --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,3 @@ +# https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository + +open_collective: nextauth diff --git a/components/access-denied.js b/components/access-denied.js index ed0ae491..e94a3a97 100644 --- a/components/access-denied.js +++ b/components/access-denied.js @@ -1,4 +1,4 @@ -import { signIn } from 'next-auth/client' +import { signIn } from 'next-auth/react' export default function AccessDenied () { return ( diff --git a/components/header.js b/components/header.js index 743516ba..c2ee241d 100644 --- a/components/header.js +++ b/components/header.js @@ -1,23 +1,31 @@ -import Link from 'next/link' -import { signIn, signOut, useSession } from 'next-auth/client' -import styles from './header.module.css' +import Link from "next/link" +import { signIn, signOut, useSession } from "next-auth/react" +import styles from "./header.module.css" // The approach used in this component shows how to build a sign in and sign out // component that works on pages which support both client and server side // rendering, and avoids any flash incorrect content on initial page load. -export default function Header () { - const [ session, loading ] = useSession() - +export default function Header() { + const { data: session, status } = useSession() + const loading = status === "loading" + return (
-

- {!session && <> - You are not signed in - + {!session && ( + <> + + You are not signed in + + { @@ -27,14 +35,22 @@ export default function Header () { > Sign in - } - {session && <> - {session.user.image && } - - Signed in as
- {session.user.email || session.user.name} + + )} + {session && ( + <> + {session.user.image && ( + + )} + + Signed in as +
+ {session.user.email || session.user.name}
- { @@ -44,16 +60,42 @@ export default function Header () { > Sign out - } + + )}

diff --git a/package.json b/package.json index fac655f5..5526319e 100644 --- a/package.json +++ b/package.json @@ -16,14 +16,15 @@ }, "author": "Iain Collins ", "contributors": [ - "Balázs Orbán " + "Balázs Orbán ", + "Nico Domino " ], "license": "ISC", "dependencies": { - "next": "^11.0.0", - "next-auth": "latest", + "next": "^12.0.1", + "next-auth": "^4.0.1", + "nodemailer": "^6.6.3", "react": "^17.0.2", - "react-dom": "^17.0.2", - "sqlite3": "^5.0.2" + "react-dom": "^17.0.2" } } diff --git a/pages/_app.js b/pages/_app.js index c0c64b79..46b23c5a 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,30 +1,30 @@ -import { Provider } from 'next-auth/client' +import { SessionProvider } from 'next-auth/react' import './styles.css' -// Use the to improve performance and allow components that call +// Use of the is now mandatory to allow components that call // `useSession()` anywhere in your application to access the `session` object. export default function App ({ Component, pageProps }) { return ( - - + ) -} \ No newline at end of file +} diff --git a/pages/_middleware.js b/pages/_middleware.js new file mode 100644 index 00000000..6e21aad5 --- /dev/null +++ b/pages/_middleware.js @@ -0,0 +1,13 @@ +import { getToken } from "next-auth/jwt" +import { NextResponse } from "next/server" + +/** @param {import("next/server").NextRequest} req */ +export async function middleware(req) { + if (req.nextUrl.pathname === "/middleware-protected") { + const session = await getToken({ req, secret: process.env.SECRET }) + // You could also check for any property on the session object, + // like role === "admin" or name === "John Doe", etc. + if (!session) return NextResponse.redirect("/api/auth/signin") + // If user is authenticated, continue. + } +} diff --git a/pages/api/auth/[...nextauth].js b/pages/api/auth/[...nextauth].js index c3a58402..27915122 100644 --- a/pages/api/auth/[...nextauth].js +++ b/pages/api/auth/[...nextauth].js @@ -1,18 +1,24 @@ import NextAuth from "next-auth" -import Providers from "next-auth/providers" +import GoogleProvider from "next-auth/providers/google" +import FacebookProvider from "next-auth/providers/facebook" +import GithubProvider from "next-auth/providers/github" +import TwitterProvider from "next-auth/providers/twitter" +import Auth0Provider from "next-auth/providers/auth0" +// import AppleProvider from "next-auth/providers/apple" +// import EmailProvider from "next-auth/providers/email" // For more information on each option (and a full list of options) go to // https://next-auth.js.org/configuration/options export default NextAuth({ // https://next-auth.js.org/configuration/providers providers: [ - Providers.Email({ - server: process.env.EMAIL_SERVER, - from: process.env.EMAIL_FROM, - }), + /* EmailProvider({ + server: process.env.EMAIL_SERVER, + from: process.env.EMAIL_FROM, + }), // Temporarily removing the Apple provider from the demo site as the // callback URL for it needs updating due to Vercel changing domains - /* + Providers.Apple({ clientId: process.env.APPLE_ID, clientSecret: { @@ -23,38 +29,30 @@ export default NextAuth({ }, }), */ - Providers.Facebook({ + FacebookProvider({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET, }), - Providers.GitHub({ + GithubProvider({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, // https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps - scope: "read:user" + scope: "read:user", }), - Providers.Google({ + GoogleProvider({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET, }), - Providers.Twitter({ + TwitterProvider({ clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET, }), - Providers.Auth0({ + Auth0Provider({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, domain: process.env.AUTH0_DOMAIN, }), ], - // Database optional. MySQL, Maria DB, Postgres and MongoDB are supported. - // https://next-auth.js.org/configuration/databases - // - // Notes: - // * You must install an appropriate node_module for your database - // * The Email provider requires a database (OAuth providers do not) - database: process.env.DATABASE_URL, - // The secret should be set to a reasonably long random string. // It is used to sign cookies and to sign and encrypt JSON Web Tokens, unless // a separate secret is defined explicitly for encrypting the JWT. @@ -63,8 +61,8 @@ export default NextAuth({ session: { // Use JSON Web Tokens for session instead of database sessions. // This option can be used with or without a database for users/accounts. - // Note: `jwt` is automatically set to `true` if no database is specified. - jwt: true, + // Note: `strategy` should be set to 'jwt' if no database is used. + strategy: 'jwt' // Seconds - How long until an idle session expires and is no longer valid. // maxAge: 30 * 24 * 60 * 60, // 30 days @@ -81,6 +79,7 @@ export default NextAuth({ jwt: { // A secret to use for key generation (you should set this explicitly) // secret: 'INp8IvdIyeMcoGAgFGoA61DdBglwwSqnXJZkgz8PSnw', + secret: process.env.SECRET, // Set to true to use encryption (default: false) // encryption: true, // You can define your own encode/decode functions for signing and encryption @@ -106,10 +105,10 @@ export default NextAuth({ // when an action is performed. // https://next-auth.js.org/configuration/callbacks callbacks: { - // async signIn(user, account, profile) { return true }, - // async redirect(url, baseUrl) { return baseUrl }, - // async session(session, user) { return session }, - // async jwt(token, user, account, profile, isNewUser) { return token } + // async signIn({ user, account, profile, email, credentials }) { return true }, + // async redirect({ url, baseUrl }) { return baseUrl }, + // async session({ session, token, user }) { return session }, + // async jwt({ token, user, account, profile, isNewUser }) { return token } }, // Events are useful for logging @@ -118,7 +117,9 @@ export default NextAuth({ // You can set the theme to 'light', 'dark' or use 'auto' to default to the // whatever prefers-color-scheme is set to in the browser. Default is 'auto' - theme: 'light', + theme: { + colorScheme: "light", + }, // Enable debug messages in the console if you are having problems debug: false, diff --git a/pages/api/examples/jwt.js b/pages/api/examples/jwt.js index 783ad7a6..bc708e7e 100644 --- a/pages/api/examples/jwt.js +++ b/pages/api/examples/jwt.js @@ -1,9 +1,9 @@ // This is an example of how to read a JSON Web Token from an API route -import jwt from 'next-auth/jwt' +import { getToken } from 'next-auth/jwt' const secret = process.env.SECRET export default async (req, res) => { - const token = await jwt.getToken({ req, secret }) + const token = await getToken({ req, secret }) res.send(JSON.stringify(token, null, 2)) } diff --git a/pages/api/examples/protected.js b/pages/api/examples/protected.js index 876c6e9b..87777465 100644 --- a/pages/api/examples/protected.js +++ b/pages/api/examples/protected.js @@ -1,5 +1,5 @@ // This is an example of to protect an API route -import { getSession } from 'next-auth/client' +import { getSession } from 'next-auth/react' export default async (req, res) => { const session = await getSession({ req }) @@ -9,4 +9,4 @@ export default async (req, res) => { } else { res.send({ error: 'You must be sign in to view the protected content on this page.' }) } -} \ No newline at end of file +} diff --git a/pages/api/examples/session.js b/pages/api/examples/session.js index 6b9c3bb2..40492589 100644 --- a/pages/api/examples/session.js +++ b/pages/api/examples/session.js @@ -1,7 +1,7 @@ // This is an example of how to access a session from an API route -import { getSession } from 'next-auth/client' +import { getSession } from 'next-auth/react' export default async (req, res) => { const session = await getSession({ req }) res.send(JSON.stringify(session, null, 2)) -} \ No newline at end of file +} diff --git a/pages/middleware-protected.js b/pages/middleware-protected.js new file mode 100644 index 00000000..cf4f645b --- /dev/null +++ b/pages/middleware-protected.js @@ -0,0 +1,9 @@ +import Layout from "../components/layout" + +export default function Page() { + return ( + +

Page protected by Middleware

+
+ ) +} diff --git a/pages/protected.js b/pages/protected.js index 9f205735..85be9b61 100644 --- a/pages/protected.js +++ b/pages/protected.js @@ -1,10 +1,11 @@ import { useState, useEffect } from 'react' -import { useSession } from 'next-auth/client' +import { useSession } from 'next-auth/react' import Layout from '../components/layout' import AccessDenied from '../components/access-denied' export default function Page () { - const [ session, loading ] = useSession() + const { data: session, status } = useSession() + const loading = status === 'loading' const [ content , setContent ] = useState() // Fetch content from protected route @@ -30,4 +31,4 @@ export default function Page () {

{content || "\u00a0"}

) -} \ No newline at end of file +} diff --git a/pages/server.js b/pages/server.js index f5adcee8..711a2481 100644 --- a/pages/server.js +++ b/pages/server.js @@ -1,4 +1,4 @@ -import { useSession, getSession } from 'next-auth/client' +import { useSession, getSession } from 'next-auth/react' import Layout from '../components/layout' export default function Page () { @@ -6,7 +6,8 @@ export default function Page () { // populated on render without needing to go through a loading stage. // This is possible because of the shared context configured in `_app.js` that // is used by `useSession()`. - const [ session, loading ] = useSession() + const { data: session, status } = useSession() + const loading = status === 'loading' return (