16
loading...
This website collects cookies to deliver better user experience
yarn create next-app app && cd app
mkdir components && cd pages && touch login.js && cd api && mkdir auth
npm i next-auth axios
_app.js
Session Provider will allow us to supply session data to our Components.
useSession client-side react hook which will let us determine if the user is authenticated and pull user data.
import { SessionProvider, useSession } from 'next-auth/react'
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps: pageProps }) {
return (
<SessionProvider session={pageProps.session}>
{Component.auth ? (
<Auth>
<Component {...pageProps} />
</Auth>
) : (
<Component {...pageProps} />
)}
</SessionProvider>
)
}
function Auth({ children }) {
const router = useRouter()
const { data: session, status, token } = useSession()
const isUser = !!session?.user
useEffect(() => {
if (status === 'loading') return // Do nothing while loading
if (!isUser) router.push('/login') //Redirect to login
}, [isUser, status])
if (isUser) {
return children
}
// Session is being fetched, or no user.
// If no user, useEffect() will redirect.
return <div>Loading...</div>
}
/api/auth
as NextAuth by default will need access to these routes. The credential provider lets us implement the logic for user authorization, here we need our database or API to verify that user credentials are valid. Throwing an error will return a message to our login form. In this example for simplicity, I used a hardcoded user. We will use "jwt" an encrypted JWT (JWE) in the session cookie.pages/api/auth
byimport NextAuth from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'
//Api route function that is returned from next auth
export default NextAuth({
providers: [
CredentialsProvider({
async authorize(credentials) {
// credentials will to passed from our login form
// Your own logic here either check agains database or api endpoint
// e.g. verify password if valid return user object.
const user = {
id: 1,
name: 'john',
email: '[email protected]',
password: '12345',
}
if (
credentials.email === user.email &&
credentials.password === user.password
)
return user
throw new Error('Incorrect Credentials') // This will be error message displayed in login form
},
}),
],
callbacks: {
// called after sucessful signin
jwt: async ({ token, user }) => {
if (user) token.id = user.id
return token
}, // called whenever session is checked
session: async ({ session, token }) => {
if (token) session.id = token.id
return session
},
},
secret: 'SECRET_HERE',
session: {
strategy: 'jwt',
maxAge: 1 * 24 * 60 * 60, // 1d
},
jwt: {
secret: 'SECRET_HERE',
encryption: true,
},
})
login.js
import { signIn, useSession } from 'next-auth/react'
import { useRouter } from 'next/router'
import { useState, useRef } from 'react'
const Login = () => {
const { status, loading } = useSession()
const router = useRouter()
const [error, setError] = useState(false)
const emailRef = useRef()
const passwordRef = useRef()
if (status === 'authenticated') {
router.push('/')
}
const loginHandler = async (e) => {
e.preventDefault()
const { error } = await signIn('credentials', {
redirect: false,
email: emailRef.current.value,
password: passwordRef.current.value,
callbackUrl: '/',
})
if (error) setError(error)
}
return (
<>
{status === 'unauthenticated' && (
<>
<p>{status}</p>
<h3>{error}</h3>
<h3>Log in</h3>
<form onSubmit={(e) => loginHandler(e)}>
<input placeholder='Email' name='email' ref={emailRef} />
<input placeholder='Pasword' name='password' ref={passwordRef} />
<input type='submit' />
</form>
</>
)}
</>
)
}
export default Login
index.js
In our index.js we set Dashboard.auth = true
to mark this route as protected. So only authenticated users can access it.import Navbar from '../components/Navbar'
export default function Dashboard() {
return (
<>
<Navbar />
<h1>secret dashboard</h1>
</>
)
}
Dashboard.auth = true
Navbar.js
import { signOut, useSession } from 'next-auth/react'
const Navbar = () => {
const { data: session } = useSession()
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
width: '100%',
backgroundColor: '#b91c1c',
}}
>
<a>{session.user.name}</a>
<button
onClick={() => {
signOut({ redirect: false })
}}
>
Signout
</button>
</div>
)
}
export default Navbar