mirror of
https://github.com/BradNut/node-auth
synced 2025-09-08 17:40:17 +00:00
Register, send email, password reset start.
This commit is contained in:
parent
98298d07d2
commit
3bc880559f
21 changed files with 1480 additions and 194 deletions
1
package-lock.json → api/package-lock.json
generated
1
package-lock.json → api/package-lock.json
generated
|
|
@ -5,6 +5,7 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "node-auth",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
50
api/src/accounts/verify.js
Normal file
50
api/src/accounts/verify.js
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import { logUserIn } from './accounts/logUserIn.js'
|
|||
import { logUserOut } from './accounts/logUserOut.js'
|
||||
import { getUserFromCookies } from './accounts/user.js'
|
||||
import { sendEmail, mailInit } from './mail/index.js'
|
||||
import { createVerifyEmailLink, validateVerifyEmail } from './accounts/verify.js'
|
||||
|
||||
// ESM specific "features"
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
|
|
@ -22,10 +23,6 @@ const app = fastify()
|
|||
async function startApp() {
|
||||
try {
|
||||
await mailInit()
|
||||
await sendEmail({
|
||||
subject: "New func",
|
||||
html: /*html*/ `<h2>New HTML who is?</h2>`,
|
||||
})
|
||||
|
||||
app.register(fastifyCors, {
|
||||
origin: [/\.nodeauth.dev/, 'https://nodeauth.dev'],
|
||||
|
|
@ -46,7 +43,14 @@ async function startApp() {
|
|||
request.body.email,
|
||||
request.body.password
|
||||
)
|
||||
// If account creation was successful
|
||||
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)
|
||||
reply.send({
|
||||
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) => {
|
||||
try {
|
||||
console.log(request.body.email, request.body.password)
|
||||
|
|
@ -4,6 +4,7 @@ let mail
|
|||
|
||||
export async function mailInit() {
|
||||
let testAccount = await nodemailer.createTestAccount();
|
||||
console.log(`Test Account: ${JSON.stringify(testAccount)}`)
|
||||
|
||||
mail = nodemailer.createTransport({
|
||||
host: "smtp.ethereal.email",
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
1322
ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
17
ui/package.json
Normal file
17
ui/package.json
Normal 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
65
ui/src/index.js
Normal 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();
|
||||
Loading…
Reference in a new issue