Email verification
parent
59bca0bfc1
commit
be0776b9ea
@ -1,12 +1,16 @@
|
||||
# DB Config
|
||||
DB_HOST=
|
||||
DB_USER=
|
||||
DB_NAME=
|
||||
DB_PASS=
|
||||
|
||||
# Application Config
|
||||
PW_SALTROUNDS=10
|
||||
COOKIE_SECRET=
|
||||
EASYPOST_API_KEY=
|
||||
EXTERNAL_URL=http://localhost:3000
|
||||
|
||||
# Api Keys
|
||||
EASYPOST_API_KEY=
|
||||
STRIPE_PUBLIC_KEY=
|
||||
STRIPE_PRIVATE_KEY=
|
||||
EXTERNAL_URL=http://localhost:3000
|
||||
SENDGRID_KEY=
|
||||
|
@ -0,0 +1,56 @@
|
||||
const router = module.exports = require('express-promise-router')()
|
||||
const ensureUser = require('./middleware/ensureUser')
|
||||
const db = require('../db')
|
||||
|
||||
const sendgrid = require('@sendgrid/mail')
|
||||
sendgrid.setApiKey(process.env.SENDGRID_KEY)
|
||||
|
||||
router.get('/links', ensureUser, async (req, res) => {
|
||||
const links = await db.user.getOpenEmailLinks(req.user.uuid)
|
||||
|
||||
res.json(links.map(stripLink))
|
||||
})
|
||||
|
||||
router.post('/', ensureUser, async (req, res) => {
|
||||
if(req.user.time_email_confirmed)
|
||||
return res.status(400).json({errors: [{
|
||||
param: 'email',
|
||||
msg: 'Email address already verified'
|
||||
}]})
|
||||
|
||||
const confirmUrl = await db.user.createLoginLink(req.user.uuid)
|
||||
|
||||
const msg = {
|
||||
to: req.user.email,
|
||||
from: {email: 'registration@email.societyofsocks.us', name: 'Society of Socks'},
|
||||
templateId: 'd-33407f1dd1b14b7b84dd779511039c95',
|
||||
dynamic_template_data: {
|
||||
confirmUrl: confirmUrl
|
||||
}
|
||||
};
|
||||
|
||||
await sendgrid.send(msg);
|
||||
|
||||
res.json({sent: true})
|
||||
})
|
||||
|
||||
router.get('/confirm/:uuid', ensureUser, async (req, res) => {
|
||||
if(!req.query || !req.query.key)
|
||||
return res.redirect('/account/email/invalid')
|
||||
|
||||
const validLink = await db.user.verifyLoginLink(req.params.uuid, req.query.key)
|
||||
if(!validLink)
|
||||
return res.redirect('/account/email/invalid')
|
||||
|
||||
await db.user.markLoginLinkUsed(validLink.uuid)
|
||||
await db.user.markEmailVerified(validLink.user_uuid)
|
||||
|
||||
return res.redirect('/account')
|
||||
})
|
||||
|
||||
function stripLink(link) {
|
||||
return {
|
||||
...link,
|
||||
login_hash: undefined
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
module.exports = async (req, res) => {
|
||||
if(!req.user) {
|
||||
const err = new Error('Unauthorized')
|
||||
err.status = 401
|
||||
throw err;
|
||||
}
|
||||
|
||||
return 'next'
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
import {DateTime} from 'luxon'
|
||||
import Link from 'next/link'
|
||||
import {Button, FormController} from '~/components/form'
|
||||
|
||||
import useUser from '~/hooks/useUser'
|
||||
import redirect from '~/utils/redirectGetInitialProps'
|
||||
|
||||
ConfirmEmail.getInitialProps = async ({ctx, user}) => {
|
||||
const {axios} = ctx
|
||||
const {data: links} = await axios.get('/api/email/links')
|
||||
|
||||
if(!user)
|
||||
return redirect(ctx, 302, '/login')
|
||||
|
||||
if(user.email_confirmed)
|
||||
return redirect(ctx, 302, '/account')
|
||||
|
||||
return {links}
|
||||
}
|
||||
|
||||
export default function ConfirmEmail({links}) {
|
||||
const user = useUser()
|
||||
const lastLink = getLastLinkTime(links)
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Confirm Email Address</h2>
|
||||
<p>
|
||||
In order to make use of account related features, we require you to
|
||||
confirm your email address.
|
||||
</p>
|
||||
|
||||
{lastLink ? (
|
||||
<>
|
||||
<p>
|
||||
We last sent an email to <strong>{user.email}</strong> at:
|
||||
</p>
|
||||
|
||||
<p style={{textAlign: 'center'}}>
|
||||
{lastLink.time_created.toFormat('LLLL dd, h:mm a')}
|
||||
<br/>
|
||||
<em>This email will be valid until {lastLink.time_created.plus(lastLink.timeout_length).toFormat('LLLL dd, h:mm a')}</em>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you haven't received it yet, please be patient. Emails can take several
|
||||
minutes to be delivered, and depending on your email provider may also
|
||||
be subject to additional scans or verification before it shows up in
|
||||
your inbox.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Also, be sure to check your spam or junk folders - registration email
|
||||
like the one we sent can occasionally be caught in those.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you've waited a few minutes, and you're sure it won't arrive, you can
|
||||
click the button below to send another one. If you still have issues,
|
||||
please feel free to <Link href="/contact"><a>contact us</a></Link>.
|
||||
</p>
|
||||
|
||||
<FormController url="/api/email" afterSubmit={() => window.location.reload()}>
|
||||
<Button type="submit">Resend Email</Button>
|
||||
</FormController>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p>
|
||||
Click the button below to send a confirmation email.
|
||||
</p>
|
||||
|
||||
<FormController url="/api/email" afterSubmit={() => window.location.reload()}>
|
||||
<Button type="submit">Resend Email</Button>
|
||||
</FormController>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function getLastLinkTime(links) {
|
||||
if(links.length < 1)
|
||||
return null;
|
||||
|
||||
let lastLink = links[0]
|
||||
lastLink.time_created = DateTime.fromISO(lastLink.time_created)
|
||||
|
||||
for(const current of links){
|
||||
current.time_created = DateTime.fromISO(current.time_created)
|
||||
if(current.time_created.diff(lastLink.time_created).as('seconds') > 0)
|
||||
lastLink = current;
|
||||
}
|
||||
|
||||
return lastLink
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import Link from 'next/link'
|
||||
import {Button, FormController} from '~/components/form'
|
||||
import Router from 'next/router'
|
||||
|
||||
import useUser from '~/hooks/useUser'
|
||||
|
||||
|
||||
export default function InvalidEmail() {
|
||||
const user = useUser()
|
||||
|
||||
if(user.email_confirmed)
|
||||
Router.push('/account')
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Invalid Confirmation Link</h2>
|
||||
<p>
|
||||
Sorry, but the email confirmation link you used was invalid or expired.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you've gotten this error before,
|
||||
please <Link href="/contact"><a>contact us</a></Link> so we can help
|
||||
resolve it. Otherwise, feel free to try sending the email again.
|
||||
</p>
|
||||
|
||||
<FormController url="/api/email" afterSubmit={() => {window.location.href = '/account/email/confirm'}}>
|
||||
<Button type="submit">Resend Email</Button>
|
||||
</FormController>
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import Router from 'next/router'
|
||||
|
||||
export default function (ctx, status, url) {
|
||||
const {res} = ctx
|
||||
|
||||
if(res) {
|
||||
res.writeHead(status, {Location: url})
|
||||
res.end();
|
||||
} else if (window) {
|
||||
Router.push(url)
|
||||
} else {
|
||||
console.error("Could not redirect for unknown reason")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue