Register, send email, password reset start.

This commit is contained in:
Bradley Shellnut 2021-04-25 13:17:50 -07:00
parent 98298d07d2
commit 3bc880559f
21 changed files with 1480 additions and 194 deletions

View file

@ -5,6 +5,7 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "node-auth",
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {

View file

@ -0,0 +1,50 @@
import crypto from 'crypto'
const { ROOT_DOMAIN, JWT_SIGNATURE } = process.env
export async function createVerifyEmailToken(email) {
try {
// Auth String, JWT Signature, email
const authString = `${JWT_SIGNATURE}:${email}`
return crypto.createHash('sha256').update(authString).digest('hex');
} catch (e) {
console.log('e', e);
}
}
export async function createVerifyEmailLink(email) {
try {
// Create token
const emailToken = await createVerifyEmailToken(email);
// Encode url string
const URIencodedEmail = encodeURIComponent(email);
// Return link for verification
return `https://${ROOT_DOMAIN}/verify/${URIencodedEmail}/${emailToken}`
} catch (e) {
console.log('e', e);
}
}
export async function validateVerifyEmail(token, email) {
try {
// Create a hash aka token
const emailToken = await createVerifyEmailToken(email);
// Compare hash with token
const isValid = emailToken === token
// If successful
if (isValid) {
// update user, to make them verified
const { user } = await import ("../user/user.js")
await user.updateOne({
'email.address': email,
}, {
$set: { 'email.verified': true },
})
// Return success
return true
}
return false
} catch (e) {
console.log('e', e);
return false
}
}

View file

@ -12,6 +12,7 @@ import { logUserIn } from './accounts/logUserIn.js'
import { logUserOut } from './accounts/logUserOut.js' import { logUserOut } from './accounts/logUserOut.js'
import { getUserFromCookies } from './accounts/user.js' import { getUserFromCookies } from './accounts/user.js'
import { sendEmail, mailInit } from './mail/index.js' import { sendEmail, mailInit } from './mail/index.js'
import { createVerifyEmailLink, validateVerifyEmail } from './accounts/verify.js'
// ESM specific "features" // ESM specific "features"
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
@ -22,10 +23,6 @@ const app = fastify()
async function startApp() { async function startApp() {
try { try {
await mailInit() await mailInit()
await sendEmail({
subject: "New func",
html: /*html*/ `<h2>New HTML who is?</h2>`,
})
app.register(fastifyCors, { app.register(fastifyCors, {
origin: [/\.nodeauth.dev/, 'https://nodeauth.dev'], origin: [/\.nodeauth.dev/, 'https://nodeauth.dev'],
@ -46,7 +43,14 @@ async function startApp() {
request.body.email, request.body.email,
request.body.password request.body.password
) )
// If account creation was successful
if (userId) { if (userId) {
const emailLink = await createVerifyEmailLink(request.body.email)
await sendEmail({
to: request.body.email,
subject: "Verify your email",
html: /*html*/ `<a href="${emailLink}">verify</a>`,
})
await logUserIn(userId, request, reply) await logUserIn(userId, request, reply)
reply.send({ reply.send({
data: { data: {
@ -85,6 +89,22 @@ async function startApp() {
} }
}) })
app.post('/api/verify', {}, async (request, reply) => {
try {
const { token, email } = request.body
console.log('token, email', token, email);
const isValid = await validateVerifyEmail(token, email)
console.log(`Is Valid: ${isValid}`)
if (isValid) {
return reply.code(200).send()
}
return reply.code(401).send()
} catch (e) {
console.error('e', e);
return reply.code(401).send()
}
})
app.post('/api/authorize', {}, async (request, reply) => { app.post('/api/authorize', {}, async (request, reply) => {
try { try {
console.log(request.body.email, request.body.password) console.log(request.body.email, request.body.password)

View file

@ -4,6 +4,7 @@ let mail
export async function mailInit() { export async function mailInit() {
let testAccount = await nodemailer.createTestAccount(); let testAccount = await nodemailer.createTestAccount();
console.log(`Test Account: ${JSON.stringify(testAccount)}`)
mail = nodemailer.createTransport({ mail = nodemailer.createTransport({
host: "smtp.ethereal.email", host: "smtp.ethereal.email",

View file

@ -1,94 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello</h1>
<h3>Register Form</h3>
<form id="register-form">
<input type="email" name="email">
<input type="password" name="password">
<button type="submit">Register</button>
</form>
<br/>
<hr />
<br/>
<h3>Login Form</h3>
<form id="login-form">
<input type="email" name="email">
<input type="password" name="password">
<button type="submit">Login</button>
</form>
<br/>
<hr />
<br/>
<button onclick="logout()">Logout</button>
<script>
async function logout() {
try {
const res = await fetch('/api/logout', {
method: "POST",
})
} catch (e) {
console.error(e);
}
}
;(() => {
const registerForm = document.getElementById("register-form")
registerForm.addEventListener("submit", async (e) => {
e.preventDefault();
try {
const values = Object.values(registerForm).reduce((obj, field) => {
if (field.name) {
obj[field.name] = field.value
}
return obj
}, {})
const res = await fetch('/api/register', {
method: "POST",
body: JSON.stringify(values),
headers: { "Content-type": "application/json; charset=UTF-8"},
})
console.log("values", values)
} catch (e) {
console.error(e)
}
})
// Find form element
const loginForm = document.getElementById("login-form")
// Wait for event
loginForm.addEventListener("submit", async (e) => {
e.preventDefault();
try {
// Get form values
const values = Object.values(loginForm).reduce((obj, field) => {
if (field.name) {
obj[field.name] = field.value
}
return obj
}, {})
// Submit
const res = await fetch('/api/authorize', {
method: "POST",
body: JSON.stringify(values),
headers: { "Content-type": "application/json; charset=UTF-8"},
})
console.log("values", values)
} catch (e) {
console.error(e)
}
})
})()
</script>
</body>
</html>

View file

@ -1,96 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello</h1>
<h3>Register Form</h3>
<form id="register-form">
<input type="email" name="email" />
<input type="password" name="password" />
<button type="submit">Register</button>
</form>
<br />
<hr />
<br />
<h3>Login Form</h3>
<form id="login-form">
<input type="email" name="email" />
<input type="password" name="password" />
<button type="submit">Login</button>
</form>
<br />
<hr />
<br />
<button onclick="logout()">Logout</button>
<script>
async function logout() {
try {
const res = await fetch('https://api.nodeauth.dev/api/logout', {
method: 'POST',
});
} catch (e) {
console.error(e);
}
}
(() => {
const registerForm = document.getElementById('register-form');
registerForm.addEventListener('submit', async (e) => {
e.preventDefault();
try {
const values = Object.values(registerForm).reduce((obj, field) => {
if (field.name) {
obj[field.name] = field.value;
}
return obj;
}, {});
const res = await fetch('https://api.nodeauth.dev/api/register', {
method: 'POST',
body: JSON.stringify(values),
credentials: 'include',
headers: { 'Content-type': 'application/json; charset=UTF-8' },
});
console.log('values', values);
} catch (e) {
console.error(e);
}
});
// Find form element
const loginForm = document.getElementById('login-form');
// Wait for event
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
try {
// Get form values
const values = Object.values(loginForm).reduce((obj, field) => {
if (field.name) {
obj[field.name] = field.value;
}
return obj;
}, {});
// Submit
const res = await fetch('https://api.nodeauth.dev/api/authorize', {
method: 'POST',
body: JSON.stringify(values),
credentials: 'include',
headers: { 'Content-type': 'application/json; charset=UTF-8' },
});
console.log('values', values);
} catch (e) {
console.error(e);
}
});
})();
</script>
</body>
</html>

1322
ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

17
ui/package.json Normal file
View file

@ -0,0 +1,17 @@
{
"name": "ui",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "./src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"cross-fetch": "^3.1.4",
"fastify": "^3.15.0",
"fastify-static": "^4.0.1"
}
}

65
ui/src/index.js Normal file
View file

@ -0,0 +1,65 @@
import https from 'https'
import { fastify } from 'fastify'
import fastifyStatic from 'fastify-static'
import fetch from 'cross-fetch'
import path from 'path'
import { fileURLToPath } from 'url'
// ESM specific "features"
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const app = fastify()
async function startApp() {
try {
app.register(fastifyStatic, {
root: path.join(__dirname, "public"),
})
app.get('/verify/:email/:token', {}, async ( request, reply ) => {
try {
const { email, token } = request.params
console.log('request', request.params.email, request.params.token);
const values = {
email,
token,
}
// Fixes UNABLE_TO_GET_ISSUER_CERT_LOCALLY, ok to do so because we do on a route by route basis and it is on our servers
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
})
const res = await fetch('https://api.nodeauth.dev/api/verify', {
method: 'POST',
body: JSON.stringify(values),
credentials: 'include',
agent: httpsAgent,
headers: { 'Content-type': 'application/json; charset=UTF-8' },
});
if (res.status === 200) {
return reply.redirect('/')
}
console.log('res', res.status);
reply.code(401).send()
} catch (e) {
console.log('e', e);
reply.send({
data: {
status: "FAILED",
},
})
}
})
const PORT = 5000;
await app.listen(PORT);
console.log(`🚀 Server Listening at port: ${PORT}`);
} catch (e) {
console.log('e', e)
}
}
startApp();