Let’s now implement login!
It’s going to be very similar to the registration, except we call /api/auth/login
, and we don’t have to handle the password confirmation. And contrary to registration, we fail if the user is not there yet.
Let’s first install the cookies
package from npm:
npm install cookies
Now create a pages/api/auth/login.js
file.
In there, we first receive the email
and password
fields from the request, and we see if we can find a user with that email. If not, we return an error message:
import { User, sequelize } from '../../../model.js'
export default async (req, res) => {
if (req.method !== 'POST') {
res.status(405).end() //Method Not Allowed
return
}
const { email, password } = req.body
let user = await User.findOne({ where: { email } })
if (!user) {
res.end(JSON.stringify({ status: 'error', message: 'User does not exist' }))
return
}
}
If the user exists, we check if the password is valid:
const isPasswordValid = await user.isPasswordValid(password)
if (!isPasswordValid) {
res.end(JSON.stringify({ status: 'error', message: 'Password not valid' }))
return
}
If the password is valid, we check the session is not expired. If it’s expired, we generate a new session token and a new expiration date. If not, we just expand the expiration date of 30 days:
let sessionToken = null
const sessionExpiration = new Date()
sessionExpiration.setDate(sessionExpiration.getDate() + 30)
if (new Date(user.session_expiration) < new Date()) {
sessionToken = randomString(255)
User.update(
{
session_token: sessionToken,
session_expiration: sessionExpiration,
},
{ where: { email } }
)
} else {
sessionToken = user.session_token
User.update(
{
session_expiration: sessionExpiration,
},
{ where: { email } }
)
}
Finally we create a cookie and store the session token in there, and we terminate the network request:
const cookies = new Cookies(req, res)
cookies.set('nextbnb_session', sessionToken, {
httpOnly: true, // true by default
})
res.end(JSON.stringify({ status: 'success', message: 'Logged in' }))
Here’s the full source code of login.js
:
import { User, sequelize } from '../../../model.js'
import Cookies from 'cookies'
const randomString = (length) => {
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = ''
for (let i = length; i > 0; --i) {
result += chars[Math.floor(Math.random() * chars.length)]
}
return result
}
export default async (req, res) => {
if (req.method !== 'POST') {
res.status(405).end() //Method Not Allowed
return
}
const { email, password } = req.body
let user = await User.findOne({ where: { email } })
if (!user) {
res.end(JSON.stringify({ status: 'error', message: 'User does not exist' }))
return
}
const isPasswordValid = await user.isPasswordValid(password)
if (!isPasswordValid) {
res.end(JSON.stringify({ status: 'error', message: 'Password not valid' }))
return
}
let sessionToken = null
const sessionExpiration = new Date()
sessionExpiration.setDate(sessionExpiration.getDate() + 30)
if (new Date(user.session_expiration) < new Date()) {
sessionToken = randomString(255)
User.update(
{
session_token: sessionToken,
session_expiration: sessionExpiration,
},
{ where: { email } }
)
} else {
sessionToken = user.session_token
User.update(
{
session_expiration: sessionExpiration,
},
{ where: { email } }
)
}
const cookies = new Cookies(req, res)
cookies.set('nextbnb_session', sessionToken, {
httpOnly: true, // true by default
})
res.end(JSON.stringify({ status: 'success', message: 'Logged in' }))
}
In the components/LoginModal.js
file, we are going to to like we did for the registration form and use useState
to create 2 state variables for the email and the password, and we’re going to use Axios to perform the network request to /api/auth/login
when the form is submitted:
import { useState } from 'react'
import axios from 'axios'
export default function LoginModal(props) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submit = async () => {
const response = await axios.post('/api/auth/login', {
email,
password,
})
console.log(response)
if (response.data.status === 'error') {
alert(response.data.message)
}
}
return (
<>
<h2>Log in</h2>
<div>
<form
onSubmit={(event) => {
submit()
event.preventDefault()
}}
>
<input
id='email'
type='email'
placeholder='Email address'
onChange={(event) => setEmail(event.target.value)}
/>
<input
id='password'
type='password'
placeholder='Password'
onChange={(event) => setPassword(event.target.value)}
/>
<button>Log in</button>
</form>
</div>
<p>
Don't have an account yet?{' '}
<a href='javascript:;' onClick={() => props.showSignup()}>
Sign up
</a>
</p>
</>
)
}
The code for this lesson is available at https://github.com/flaviocopes/airbnb-clone-react-nextjs-2020/tree/5-4