Skip to content

Commit

Permalink
feat: migrate to v4 (#40)
Browse files Browse the repository at this point in the history
* 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 😁

* 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 [email protected]

* fix: exact version

* Update _middleware.js

* fix: comment out email adapter

* feat: [email protected]

* fix: cleanup options

* fix: rm sqlite3

Co-authored-by: Balázs Orbán <[email protected]>
Co-authored-by: Shawn Goulet <[email protected]>
  • Loading branch information
3 people authored Dec 1, 2021
1 parent ddd9389 commit b42c128
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 76 deletions.
3 changes: 3 additions & 0 deletions FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository

open_collective: nextauth
2 changes: 1 addition & 1 deletion components/access-denied.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { signIn } from 'next-auth/client'
import { signIn } from 'next-auth/react'

export default function AccessDenied () {
return (
Expand Down
88 changes: 65 additions & 23 deletions components/header.js
Original file line number Diff line number Diff line change
@@ -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 (
<header>
<noscript>
<style>{`.nojs-show { opacity: 1; top: 0; }`}</style>
</noscript>
<div className={styles.signedInStatus}>
<p className={`nojs-show ${(!session && loading) ? styles.loading : styles.loaded}`}>
{!session && <>
<span className={styles.notSignedInText}>You are not signed in</span>
<a
<p
className={`nojs-show ${
!session && loading ? styles.loading : styles.loaded
}`}
>
{!session && (
<>
<span className={styles.notSignedInText}>
You are not signed in
</span>
<a
href={`/api/auth/signin`}
className={styles.buttonPrimary}
onClick={(e) => {
Expand All @@ -27,14 +35,22 @@ export default function Header () {
>
Sign in
</a>
</>}
{session && <>
{session.user.image && <span style={{backgroundImage: `url(${session.user.image})` }} className={styles.avatar}/>}
<span className={styles.signedInText}>
<small>Signed in as</small><br/>
<strong>{session.user.email || session.user.name}</strong>
</>
)}
{session && (
<>
{session.user.image && (
<span
style={{ backgroundImage: `url('${session.user.image}')` }}
className={styles.avatar}
/>
)}
<span className={styles.signedInText}>
<small>Signed in as</small>
<br />
<strong>{session.user.email || session.user.name}</strong>
</span>
<a
<a
href={`/api/auth/signout`}
className={styles.button}
onClick={(e) => {
Expand All @@ -44,16 +60,42 @@ export default function Header () {
>
Sign out
</a>
</>}
</>
)}
</p>
</div>
<nav>
<ul className={styles.navItems}>
<li className={styles.navItem}><Link href="/"><a>Home</a></Link></li>
<li className={styles.navItem}><Link href="/client"><a>Client</a></Link></li>
<li className={styles.navItem}><Link href="/server"><a>Server</a></Link></li>
<li className={styles.navItem}><Link href="/protected"><a>Protected</a></Link></li>
<li className={styles.navItem}><Link href="/api-example"><a>API</a></Link></li>
<li className={styles.navItem}>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href="/client">
<a>Client</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href="/server">
<a>Server</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href="/protected">
<a>Protected</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href="/api-example">
<a>API</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href="/middleware-protected">
<a>Middleware protected</a>
</Link>
</li>
</ul>
</nav>
</header>
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
},
"author": "Iain Collins <[email protected]>",
"contributors": [
"Balázs Orbán <[email protected]>"
"Balázs Orbán <[email protected]>",
"Nico Domino <[email protected]>"
],
"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"
}
}
18 changes: 9 additions & 9 deletions pages/_app.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import { Provider } from 'next-auth/client'
import { SessionProvider } from 'next-auth/react'
import './styles.css'

// Use the <Provider> to improve performance and allow components that call
// Use of the <SessionProvider> 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 (
<Provider
<SessionProvider
// Provider options are not required but can be useful in situations where
// you have a short session maxAge time. Shown here with default values.
options={{
// Client Max Age controls how often the useSession in the client should
// Stale Time controls how often the useSession in the client should
// contact the server to sync the session state. Value in seconds.
// e.g.
// * 0 - Disabled (always use cache value)
// * 60 - Sync session state with server if it's older than 60 seconds
clientMaxAge: 0,
// Keep Alive tells windows / tabs that are signed in to keep sending
staleTime: 0,
// Refetch Interval tells windows / tabs that are signed in to keep sending
// a keep alive request (which extends the current session expiry) to
// prevent sessions in open windows from expiring. Value in seconds.
//
// Note: If a session has expired when keep alive is triggered, all open
// windows / tabs will be updated to reflect the user is signed out.
keepAlive: 0
refetchInterval: 0
}}
session={pageProps.session} >
<Component {...pageProps} />
</Provider>
</SessionProvider>
)
}
}
13 changes: 13 additions & 0 deletions pages/_middleware.js
Original file line number Diff line number Diff line change
@@ -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.
}
}
55 changes: 28 additions & 27 deletions pages/api/auth/[...nextauth].js
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions pages/api/examples/jwt.js
Original file line number Diff line number Diff line change
@@ -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))
}
4 changes: 2 additions & 2 deletions pages/api/examples/protected.js
Original file line number Diff line number Diff line change
@@ -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 })
Expand All @@ -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.' })
}
}
}
4 changes: 2 additions & 2 deletions pages/api/examples/session.js
Original file line number Diff line number Diff line change
@@ -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))
}
}
9 changes: 9 additions & 0 deletions pages/middleware-protected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Layout from "../components/layout"

export default function Page() {
return (
<Layout>
<h1>Page protected by Middleware</h1>
</Layout>
)
}
7 changes: 4 additions & 3 deletions pages/protected.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -30,4 +31,4 @@ export default function Page () {
<p><strong>{content || "\u00a0"}</strong></p>
</Layout>
)
}
}
Loading

1 comment on commit b42c128

@vercel
Copy link

@vercel vercel bot commented on b42c128 Dec 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.