Skip to content

Commit

Permalink
2fa logic on log in
Browse files Browse the repository at this point in the history
  • Loading branch information
tdjsnelling committed Feb 13, 2023
1 parent 977205f commit ecc49a1
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 2 deletions.
28 changes: 27 additions & 1 deletion api/src/controllers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,42 @@ export const register = async (req, res) => {
export const login = async (req, res) => {
if (req.body.username && req.body.password) {
try {
const user = await User.findOne({ username: req.body.username })
const user = await User.findOne({ username: req.body.username }).lean()

if (user) {
if (user.banned) {
res.status(403).send('User is banned')
return
}

if (user.totp.enabled && !req.body.totp) {
res.status(401).send('One-time code required')
return
}

const matches = await bcrypt.compare(req.body.password, user.password)

if (user.totp.enabled) {
const validToken = speakeasy.totp.verify({
secret: user.totp.secret,
encoding: 'base32',
token: req.body.totp,
window: 1,
})

if (!validToken) {
if (!user.totp.backup.includes(req.body.totp)) {
res.status(401).send('Invalid one-time code')
return
} else {
await User.findOneAndUpdate(
{ username: req.body.username },
{ $pull: { 'totp.backup': req.body.totp } }
)
}
}
}

if (matches) {
res.send({
token: jwt.sign(
Expand Down
9 changes: 8 additions & 1 deletion client/pages/login.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from 'react'
import React, { useContext, useState } from 'react'
import getConfig from 'next/config'
import { useRouter } from 'next/router'
import Link from 'next/link'
Expand All @@ -11,6 +11,8 @@ import { NotificationContext } from '../components/Notifications'
import LoadingContext from '../utils/LoadingContext'

const Login = () => {
const [totpRequired, setTotpRequired] = useState(false)

const [, setCookie] = useCookies()

const { addNotification } = useContext(NotificationContext)
Expand All @@ -36,11 +38,13 @@ const Login = () => {
body: JSON.stringify({
username: form.get('username'),
password: form.get('password'),
totp: form.get('totp'),
}),
})

if (res.status !== 200) {
const reason = await res.text()
if (reason === 'One-time code required') setTotpRequired(true)
throw new Error(reason)
}

Expand Down Expand Up @@ -78,6 +82,9 @@ const Login = () => {
mb={4}
required
/>
{totpRequired && (
<Input name="totp" label="One-time code" mb={4} required />
)}
<Button>Log in</Button>
</form>
<Link href="/reset-password/initiate" passHref>
Expand Down

0 comments on commit ecc49a1

Please sign in to comment.