├── .DS_Store ├── .gitignore ├── 10UserRecords ├── package-lock.json ├── package.json └── src │ ├── Encryption.txt │ ├── accounts │ └── register.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ └── user │ └── user.js ├── 11LoginUIFlow ├── package-lock.json ├── package.json └── src │ ├── Encryption.txt │ ├── accounts │ ├── authorize.js │ └── register.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ └── user │ └── user.js ├── 12AuthorizationWithCompare ├── package-lock.json ├── package.json └── src │ ├── Encryption.txt │ ├── accounts │ ├── authorize.js │ └── register.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ └── user │ └── user.js ├── 16SettingAndGettingHTTPOnlyCookies ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ └── register.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ └── user │ └── user.js ├── 17CreatingSessions ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── register.js │ └── session.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js ├── 18CreatingAndStoringOurJWTs ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── register.js │ ├── session.js │ └── tokens.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js ├── 19DecodingOurJWTAndUIAccess ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── register.js │ ├── session.js │ ├── tokens.js │ └── user.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js ├── 1ESMAndGettingStarted ├── package.json └── src │ ├── db.js │ └── index.js ├── 20RefreshTokens ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── register.js │ ├── session.js │ ├── tokens.js │ └── user.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js ├── 21Logout ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── logUserOut.js │ ├── register.js │ ├── session.js │ ├── tokens.js │ └── user.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js ├── 22PolishAndCleanup ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── logUserOut.js │ ├── register.js │ ├── session.js │ ├── tokens.js │ └── user.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js ├── 2FastifyUpandRunning ├── package-lock.json ├── package.json └── src │ ├── db.js │ ├── index.js │ └── public │ └── index.html ├── 3DotEnv ├── package-lock.json ├── package.json └── src │ ├── db.js │ ├── env.js │ ├── index.js │ └── public │ └── index.html ├── 4ConnectingToMongoDB ├── package-lock.json ├── package.json └── src │ ├── db.js │ ├── env.js │ ├── index.js │ └── public │ └── index.html ├── 6FormsWithVanillaJS ├── package-lock.json ├── package.json └── src │ ├── db.js │ ├── env.js │ ├── index.js │ └── public │ └── index.html ├── 7FetchPostRequests ├── node-auth │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── Encryption.txt │ │ ├── accounts │ │ └── register.js │ │ ├── db.js │ │ ├── env.js │ │ ├── index.js │ │ └── public │ │ └── index.html ├── package-lock.json ├── package.json └── src │ ├── db.js │ ├── env.js │ ├── index.js │ └── public │ └── index.html ├── 8WhatIsEncryptionSaltingAndHashing ├── package-lock.json ├── package.json └── src │ ├── db.js │ ├── env.js │ ├── index.js │ └── public │ └── index.html ├── 9SaltingAndHashing ├── package-lock.json ├── package.json └── src │ ├── Encryption.txt │ ├── accounts │ └── register.js │ ├── db.js │ ├── env.js │ ├── index.js │ └── public │ └── index.html ├── END_OF_SERIES ├── package-lock.json ├── package.json └── src │ ├── AccessVsRefresh.txt │ ├── Encryption.txt │ ├── HTTPOnlyCookies.txt │ ├── accounts │ ├── authorize.js │ ├── logUserIn.js │ ├── logUserOut.js │ ├── register.js │ ├── session.js │ ├── tokens.js │ └── user.js │ ├── db.js │ ├── env.js │ ├── index.js │ ├── public │ └── index.html │ ├── session │ └── session.js │ └── user │ └── user.js └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leveluptuts/node-fundamentals-authentication/fcb30eab4acf26cdea35327d18b6a36acc9fea24/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules -------------------------------------------------------------------------------- /10UserRecords/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-static": "^3.4.0", 17 | "mongodb": "^3.6.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /10UserRecords/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /10UserRecords/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /10UserRecords/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /10UserRecords/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /10UserRecords/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { registerUser } from "./accounts/register.js" 8 | 9 | // ESM specific features 10 | const __filename = fileURLToPath(import.meta.url) 11 | const __dirname = path.dirname(__filename) 12 | 13 | const app = fastify() 14 | 15 | console.log(process.env.MONGO_URL) 16 | 17 | async function startApp() { 18 | try { 19 | app.register(fastifyStatic, { 20 | root: path.join(__dirname, "public"), 21 | }) 22 | 23 | app.post("/api/register", {}, async (request, reply) => { 24 | try { 25 | const userId = await registerUser( 26 | request.body.email, 27 | request.body.password 28 | ) 29 | console.log("userId", userId) 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | }) 34 | 35 | // app.get("/", {}, (request, reply) => { 36 | // reply.send({ 37 | // data: "hello world", 38 | // }) 39 | // }) 40 | 41 | await app.listen(3000) 42 | console.log("🚀 Server Listening at port: 3000") 43 | } catch (e) { 44 | console.error(e) 45 | } 46 | } 47 | 48 | connectDb().then(() => { 49 | startApp() 50 | }) 51 | -------------------------------------------------------------------------------- /10UserRecords/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /10UserRecords/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /11LoginUIFlow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-static": "^3.4.0", 17 | "mongodb": "^3.6.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | export async function authorizeUser(email, password) {} 2 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { registerUser } from "./accounts/register.js" 8 | import { authorizeUser } from "./accounts/authorize.js" 9 | 10 | // ESM specific features 11 | const __filename = fileURLToPath(import.meta.url) 12 | const __dirname = path.dirname(__filename) 13 | 14 | const app = fastify() 15 | 16 | console.log(process.env.MONGO_URL) 17 | 18 | async function startApp() { 19 | try { 20 | app.register(fastifyStatic, { 21 | root: path.join(__dirname, "public"), 22 | }) 23 | 24 | app.post("/api/register", {}, async (request, reply) => { 25 | try { 26 | const userId = await registerUser( 27 | request.body.email, 28 | request.body.password 29 | ) 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | }) 34 | 35 | app.post("/api/authorize", {}, async (request, reply) => { 36 | try { 37 | console.log(request.body.email, request.body.password) 38 | const userId = await authorizeUser( 39 | request.body.email, 40 | request.body.password 41 | ) 42 | } catch (e) { 43 | console.error(e) 44 | } 45 | }) 46 | 47 | // app.get("/", {}, (request, reply) => { 48 | // reply.send({ 49 | // data: "hello world", 50 | // }) 51 | // }) 52 | 53 | await app.listen(3000) 54 | console.log("🚀 Server Listening at port: 3000") 55 | } catch (e) { 56 | console.error(e) 57 | } 58 | } 59 | 60 | connectDb().then(() => { 61 | startApp() 62 | }) 63 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /11LoginUIFlow/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-static": "^3.4.0", 17 | "mongodb": "^3.6.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return isAuthorized 17 | } 18 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { registerUser } from "./accounts/register.js" 8 | import { authorizeUser } from "./accounts/authorize.js" 9 | 10 | // ESM specific features 11 | const __filename = fileURLToPath(import.meta.url) 12 | const __dirname = path.dirname(__filename) 13 | 14 | const app = fastify() 15 | 16 | console.log(process.env.MONGO_URL) 17 | 18 | async function startApp() { 19 | try { 20 | app.register(fastifyStatic, { 21 | root: path.join(__dirname, "public"), 22 | }) 23 | 24 | app.post("/api/register", {}, async (request, reply) => { 25 | try { 26 | const userId = await registerUser( 27 | request.body.email, 28 | request.body.password 29 | ) 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | }) 34 | 35 | app.post("/api/authorize", {}, async (request, reply) => { 36 | try { 37 | console.log(request.body.email, request.body.password) 38 | const userId = await authorizeUser( 39 | request.body.email, 40 | request.body.password 41 | ) 42 | } catch (e) { 43 | console.error(e) 44 | } 45 | }) 46 | 47 | // app.get("/", {}, (request, reply) => { 48 | // reply.send({ 49 | // data: "hello world", 50 | // }) 51 | // }) 52 | 53 | await app.listen(3000) 54 | console.log("🚀 Server Listening at port: 3000") 55 | } catch (e) { 56 | console.error(e) 57 | } 58 | } 59 | 60 | connectDb().then(() => { 61 | startApp() 62 | }) 63 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /12AuthorizationWithCompare/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return isAuthorized 17 | } 18 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | 11 | // ESM specific features 12 | const __filename = fileURLToPath(import.meta.url) 13 | const __dirname = path.dirname(__filename) 14 | 15 | const app = fastify() 16 | 17 | console.log(process.env.MONGO_URL) 18 | 19 | async function startApp() { 20 | try { 21 | app.register(fastifyCookie, { 22 | secret: process.env.COOKIE_SIGNATURE, 23 | }) 24 | 25 | app.register(fastifyStatic, { 26 | root: path.join(__dirname, "public"), 27 | }) 28 | 29 | app.post("/api/register", {}, async (request, reply) => { 30 | try { 31 | const userId = await registerUser( 32 | request.body.email, 33 | request.body.password 34 | ) 35 | } catch (e) { 36 | console.error(e) 37 | } 38 | }) 39 | 40 | app.post("/api/authorize", {}, async (request, reply) => { 41 | try { 42 | console.log(request.body.email, request.body.password) 43 | const userId = await authorizeUser( 44 | request.body.email, 45 | request.body.password 46 | ) 47 | // Generate auth tokens 48 | // Set cookies 49 | reply 50 | .setCookie("testCookie", "the value is this", { 51 | path: "/", 52 | domain: "localhost", 53 | httpOnly: true, 54 | }) 55 | .send({ 56 | data: "just testing", 57 | }) 58 | } catch (e) { 59 | console.error(e) 60 | } 61 | }) 62 | 63 | app.get("/test", {}, (request, reply) => { 64 | console.log(request.cookies.testCookie) 65 | reply.send({ 66 | data: "hello world", 67 | }) 68 | }) 69 | 70 | await app.listen(3000) 71 | console.log("🚀 Server Listening at port: 3000") 72 | } catch (e) { 73 | console.error(e) 74 | } 75 | } 76 | 77 | connectDb().then(() => { 78 | startApp() 79 | }) 80 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /16SettingAndGettingHTTPOnlyCookies/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /17CreatingSessions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /17CreatingSessions/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /17CreatingSessions/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /17CreatingSessions/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /17CreatingSessions/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /17CreatingSessions/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | 3 | export async function logUserIn(userId, request, reply) { 4 | const connectionInformation = { 5 | ip: request.ip, 6 | userAgent: request.headers["user-agent"], 7 | } 8 | // Create Session 9 | const sessionToken = await createSession(userId, connectionInformation) 10 | console.log("sessionToken", sessionToken) 11 | // Create JWT 12 | // Set Cookie 13 | } 14 | -------------------------------------------------------------------------------- /17CreatingSessions/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /17CreatingSessions/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /17CreatingSessions/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /17CreatingSessions/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /17CreatingSessions/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | 12 | // ESM specific features 13 | const __filename = fileURLToPath(import.meta.url) 14 | const __dirname = path.dirname(__filename) 15 | 16 | const app = fastify() 17 | 18 | console.log(process.env.MONGO_URL) 19 | 20 | async function startApp() { 21 | try { 22 | app.register(fastifyCookie, { 23 | secret: process.env.COOKIE_SIGNATURE, 24 | }) 25 | 26 | app.register(fastifyStatic, { 27 | root: path.join(__dirname, "public"), 28 | }) 29 | 30 | app.post("/api/register", {}, async (request, reply) => { 31 | try { 32 | const userId = await registerUser( 33 | request.body.email, 34 | request.body.password 35 | ) 36 | } catch (e) { 37 | console.error(e) 38 | } 39 | }) 40 | 41 | app.post("/api/authorize", {}, async (request, reply) => { 42 | try { 43 | const { isAuthorized, userId } = await authorizeUser( 44 | request.body.email, 45 | request.body.password 46 | ) 47 | if (isAuthorized) { 48 | await logUserIn(userId, request, reply) 49 | } 50 | // Generate auth tokens 51 | // Set cookies 52 | reply 53 | .setCookie("testCookie", "the value is this", { 54 | path: "/", 55 | domain: "localhost", 56 | httpOnly: true, 57 | }) 58 | .send({ 59 | data: "just testing", 60 | }) 61 | } catch (e) { 62 | console.error(e) 63 | } 64 | }) 65 | 66 | app.get("/test", {}, (request, reply) => { 67 | console.log(request.headers["user-agent"]) 68 | reply.send({ 69 | data: "hello world", 70 | }) 71 | }) 72 | 73 | await app.listen(3000) 74 | console.log("🚀 Server Listening at port: 3000") 75 | } catch (e) { 76 | console.error(e) 77 | } 78 | } 79 | 80 | connectDb().then(() => { 81 | startApp() 82 | }) 83 | -------------------------------------------------------------------------------- /17CreatingSessions/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /17CreatingSessions/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | -------------------------------------------------------------------------------- /17CreatingSessions/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | import { createTokens } from "./tokens.js" 3 | 4 | export async function logUserIn(userId, request, reply) { 5 | const connectionInformation = { 6 | ip: request.ip, 7 | userAgent: request.headers["user-agent"], 8 | } 9 | // Create Session 10 | const sessionToken = await createSession(userId, connectionInformation) 11 | console.log("sessionToken", sessionToken) 12 | 13 | // Create JWT 14 | const { accessToken, refreshToken } = await createTokens(sessionToken, userId) 15 | // Set Cookie 16 | const now = new Date() 17 | const refreshExpires = now.setDate(now.getDate + 30) 18 | reply 19 | .setCookie("refreshToken", refreshToken, { 20 | path: "/", 21 | domain: "localhost", 22 | httpOnly: true, 23 | expires: refreshExpires, 24 | }) 25 | .setCookie("accessToken", accessToken, { 26 | path: "/", 27 | domain: "localhost", 28 | httpOnly: true, 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/accounts/tokens.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | 3 | const JWTSignature = process.env.JWT_SIGNATURE 4 | 5 | export async function createTokens(sessionToken, userId) { 6 | try { 7 | // Create Refresh Token 8 | // Session Id 9 | const refreshToken = jwt.sign( 10 | { 11 | sessionToken, 12 | }, 13 | JWTSignature 14 | ) 15 | // Create Access Token 16 | // Session Id, User Id 17 | const accessToken = jwt.sign( 18 | { 19 | sessionToken, 20 | userId, 21 | }, 22 | JWTSignature 23 | ) 24 | // Return Refresh Token & Access Token 25 | return { accessToken, refreshToken } 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | 12 | // ESM specific features 13 | const __filename = fileURLToPath(import.meta.url) 14 | const __dirname = path.dirname(__filename) 15 | 16 | const app = fastify() 17 | 18 | console.log(process.env.MONGO_URL) 19 | 20 | async function startApp() { 21 | try { 22 | app.register(fastifyCookie, { 23 | secret: process.env.COOKIE_SIGNATURE, 24 | }) 25 | 26 | app.register(fastifyStatic, { 27 | root: path.join(__dirname, "public"), 28 | }) 29 | 30 | app.post("/api/register", {}, async (request, reply) => { 31 | try { 32 | const userId = await registerUser( 33 | request.body.email, 34 | request.body.password 35 | ) 36 | } catch (e) { 37 | console.error(e) 38 | } 39 | }) 40 | 41 | app.post("/api/authorize", {}, async (request, reply) => { 42 | try { 43 | const { isAuthorized, userId } = await authorizeUser( 44 | request.body.email, 45 | request.body.password 46 | ) 47 | if (isAuthorized) { 48 | await logUserIn(userId, request, reply) 49 | reply.send({ 50 | data: "User Logged In", 51 | }) 52 | } 53 | reply.send({ 54 | data: "Auth Failed", 55 | }) 56 | } catch (e) { 57 | console.error(e) 58 | } 59 | }) 60 | 61 | app.get("/test", {}, (request, reply) => { 62 | console.log(request.headers["user-agent"]) 63 | reply.send({ 64 | data: "hello world", 65 | }) 66 | }) 67 | 68 | await app.listen(3000) 69 | console.log("🚀 Server Listening at port: 3000") 70 | } catch (e) { 71 | console.error(e) 72 | } 73 | } 74 | 75 | connectDb().then(() => { 76 | startApp() 77 | }) 78 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | -------------------------------------------------------------------------------- /18CreatingAndStoringOurJWTs/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | import { createTokens } from "./tokens.js" 3 | 4 | export async function logUserIn(userId, request, reply) { 5 | const connectionInformation = { 6 | ip: request.ip, 7 | userAgent: request.headers["user-agent"], 8 | } 9 | // Create Session 10 | const sessionToken = await createSession(userId, connectionInformation) 11 | console.log("sessionToken", sessionToken) 12 | 13 | // Create JWT 14 | const { accessToken, refreshToken } = await createTokens(sessionToken, userId) 15 | // Set Cookie 16 | const now = new Date() 17 | const refreshExpires = now.setDate(now.getDate + 30) 18 | reply 19 | .setCookie("refreshToken", refreshToken, { 20 | path: "/", 21 | domain: "localhost", 22 | httpOnly: true, 23 | expires: refreshExpires, 24 | }) 25 | .setCookie("accessToken", accessToken, { 26 | path: "/", 27 | domain: "localhost", 28 | httpOnly: true, 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/accounts/tokens.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | 3 | const JWTSignature = process.env.JWT_SIGNATURE 4 | 5 | export async function createTokens(sessionToken, userId) { 6 | try { 7 | // Create Refresh Token 8 | // Session Id 9 | const refreshToken = jwt.sign( 10 | { 11 | sessionToken, 12 | }, 13 | JWTSignature 14 | ) 15 | // Create Access Token 16 | // Session Id, User Id 17 | const accessToken = jwt.sign( 18 | { 19 | sessionToken, 20 | userId, 21 | }, 22 | JWTSignature 23 | ) 24 | // Return Refresh Token & Access Token 25 | return { accessToken, refreshToken } 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/accounts/user.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | import jwt from "jsonwebtoken" 3 | 4 | const { ObjectId } = mongo 5 | 6 | const JWTSignature = process.env.JWT_SIGNATURE 7 | 8 | export async function getUserFromCookies(request) { 9 | try { 10 | const { user } = await import("../user/user.js") 11 | // Check to make sure access token exists 12 | if (request?.cookies?.accessToken) { 13 | // If access token 14 | const { accessToken } = request.cookies 15 | // Decode decode access token 16 | const decodedAccessToken = jwt.verify(accessToken, JWTSignature) 17 | // Return user from record 18 | return user.findOne({ 19 | _id: ObjectId(decodedAccessToken?.userId), 20 | }) 21 | } 22 | // Decode refresh token 23 | // Look up session 24 | // Confirm session is valid 25 | // if session is valid, 26 | // Look up current user 27 | // refresh tokens 28 | // Return current user 29 | } catch (e) { 30 | console.error(e) 31 | } 32 | } 33 | 34 | export async function refreshTokens() { 35 | try { 36 | } catch (e) { 37 | console.error(e) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | import { getUserFromCookies } from "./accounts/user.js" 12 | 13 | // ESM specific features 14 | const __filename = fileURLToPath(import.meta.url) 15 | const __dirname = path.dirname(__filename) 16 | 17 | const app = fastify() 18 | 19 | console.log(process.env.MONGO_URL) 20 | 21 | async function startApp() { 22 | try { 23 | app.register(fastifyCookie, { 24 | secret: process.env.COOKIE_SIGNATURE, 25 | }) 26 | 27 | app.register(fastifyStatic, { 28 | root: path.join(__dirname, "public"), 29 | }) 30 | 31 | app.post("/api/register", {}, async (request, reply) => { 32 | try { 33 | const userId = await registerUser( 34 | request.body.email, 35 | request.body.password 36 | ) 37 | } catch (e) { 38 | console.error(e) 39 | } 40 | }) 41 | 42 | app.post("/api/authorize", {}, async (request, reply) => { 43 | try { 44 | const { isAuthorized, userId } = await authorizeUser( 45 | request.body.email, 46 | request.body.password 47 | ) 48 | if (isAuthorized) { 49 | await logUserIn(userId, request, reply) 50 | reply.send({ 51 | data: "User Logged In", 52 | }) 53 | } 54 | reply.send({ 55 | data: "Auth Failed", 56 | }) 57 | } catch (e) { 58 | console.error(e) 59 | } 60 | }) 61 | 62 | app.get("/test", {}, async (request, reply) => { 63 | try { 64 | // Verify user login 65 | const user = await getUserFromCookies(request) 66 | // Return user email, if it exists, otherwise return unauthorized 67 | if (user?._id) { 68 | reply.send({ 69 | data: user, 70 | }) 71 | } else { 72 | reply.send({ 73 | data: "User Lookup Failed", 74 | }) 75 | } 76 | } catch (e) { 77 | throw new Error(e) 78 | } 79 | }) 80 | 81 | await app.listen(3000) 82 | console.log("🚀 Server Listening at port: 3000") 83 | } catch (e) { 84 | console.error(e) 85 | } 86 | } 87 | 88 | connectDb().then(() => { 89 | startApp() 90 | }) 91 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | -------------------------------------------------------------------------------- /19DecodingOurJWTAndUIAccess/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /1ESMAndGettingStarted/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /1ESMAndGettingStarted/src/db.js: -------------------------------------------------------------------------------- 1 | console.log("hello from the db") 2 | -------------------------------------------------------------------------------- /1ESMAndGettingStarted/src/index.js: -------------------------------------------------------------------------------- 1 | import "./db.js" 2 | 3 | console.log("Hello World") 4 | -------------------------------------------------------------------------------- /20RefreshTokens/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /20RefreshTokens/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /20RefreshTokens/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /20RefreshTokens/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /20RefreshTokens/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /20RefreshTokens/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | import { createTokens } from "./tokens.js" 3 | 4 | export async function logUserIn(userId, request, reply) { 5 | const connectionInformation = { 6 | ip: request.ip, 7 | userAgent: request.headers["user-agent"], 8 | } 9 | // Create Session 10 | const sessionToken = await createSession(userId, connectionInformation) 11 | console.log("sessionToken", sessionToken) 12 | 13 | // Create JWT 14 | const { accessToken, refreshToken } = await createTokens(sessionToken, userId) 15 | // Set Cookie 16 | const now = new Date() 17 | // Get date, 30 days in the future 18 | const refreshExpires = now.setDate(now.getDate() + 30) 19 | reply 20 | .setCookie("refreshToken", refreshToken, { 21 | path: "/", 22 | domain: "localhost", 23 | httpOnly: true, 24 | expires: refreshExpires, 25 | }) 26 | .setCookie("accessToken", accessToken, { 27 | path: "/", 28 | domain: "localhost", 29 | httpOnly: true, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /20RefreshTokens/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /20RefreshTokens/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /20RefreshTokens/src/accounts/tokens.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | 3 | const JWTSignature = process.env.JWT_SIGNATURE 4 | 5 | export async function createTokens(sessionToken, userId) { 6 | try { 7 | // Create Refresh Token 8 | // Session Id 9 | const refreshToken = jwt.sign( 10 | { 11 | sessionToken, 12 | }, 13 | JWTSignature 14 | ) 15 | // Create Access Token 16 | // Session Id, User Id 17 | const accessToken = jwt.sign( 18 | { 19 | sessionToken, 20 | userId, 21 | }, 22 | JWTSignature 23 | ) 24 | // Return Refresh Token & Access Token 25 | return { accessToken, refreshToken } 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /20RefreshTokens/src/accounts/user.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | import jwt from "jsonwebtoken" 3 | import { createTokens } from "./tokens.js" 4 | 5 | const { ObjectId } = mongo 6 | 7 | const JWTSignature = process.env.JWT_SIGNATURE 8 | 9 | export async function getUserFromCookies(request, reply) { 10 | try { 11 | const { user } = await import("../user/user.js") 12 | const { session } = await import("../session/session.js") 13 | // Check to make sure access token exists 14 | if (request?.cookies?.accessToken) { 15 | // If access token 16 | const { accessToken } = request.cookies 17 | // Decode decode access token 18 | const decodedAccessToken = jwt.verify(accessToken, JWTSignature) 19 | // Return user from record 20 | return user.findOne({ 21 | _id: ObjectId(decodedAccessToken?.userId), 22 | }) 23 | } 24 | if (request?.cookies?.refreshToken) { 25 | const { refreshToken } = request.cookies 26 | // Decode refresh token 27 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 28 | // Look up session 29 | const currentSession = await session.findOne({ sessionToken }) 30 | // Confirm session is valid 31 | if (currentSession.valid) { 32 | // Look up current user 33 | const currentUser = await user.findOne({ 34 | _id: ObjectId(currentSession.userId), 35 | }) 36 | console.log("currentUser", currentUser) 37 | // refresh tokens 38 | await refreshTokens(sessionToken, currentUser._id, reply) 39 | // Return current user 40 | return currentUser 41 | } 42 | } 43 | } catch (e) { 44 | console.error(e) 45 | } 46 | } 47 | 48 | export async function refreshTokens(sessionToken, userId, reply) { 49 | try { 50 | // Create JWT 51 | const { accessToken, refreshToken } = await createTokens( 52 | sessionToken, 53 | userId 54 | ) 55 | // Set Cookie 56 | const now = new Date() 57 | // Get date, 30 days in the future 58 | const refreshExpires = now.setDate(now.getDate() + 30) 59 | reply 60 | .setCookie("refreshToken", refreshToken, { 61 | path: "/", 62 | domain: "localhost", 63 | httpOnly: true, 64 | expires: refreshExpires, 65 | }) 66 | .setCookie("accessToken", accessToken, { 67 | path: "/", 68 | domain: "localhost", 69 | httpOnly: true, 70 | }) 71 | } catch (e) { 72 | console.error(e) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /20RefreshTokens/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /20RefreshTokens/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /20RefreshTokens/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | import { getUserFromCookies } from "./accounts/user.js" 12 | 13 | // ESM specific features 14 | const __filename = fileURLToPath(import.meta.url) 15 | const __dirname = path.dirname(__filename) 16 | 17 | const app = fastify() 18 | 19 | console.log(process.env.MONGO_URL) 20 | 21 | async function startApp() { 22 | try { 23 | app.register(fastifyCookie, { 24 | secret: process.env.COOKIE_SIGNATURE, 25 | }) 26 | 27 | app.register(fastifyStatic, { 28 | root: path.join(__dirname, "public"), 29 | }) 30 | 31 | app.post("/api/register", {}, async (request, reply) => { 32 | try { 33 | const userId = await registerUser( 34 | request.body.email, 35 | request.body.password 36 | ) 37 | } catch (e) { 38 | console.error(e) 39 | } 40 | }) 41 | 42 | app.post("/api/authorize", {}, async (request, reply) => { 43 | try { 44 | const { isAuthorized, userId } = await authorizeUser( 45 | request.body.email, 46 | request.body.password 47 | ) 48 | if (isAuthorized) { 49 | await logUserIn(userId, request, reply) 50 | reply.send({ 51 | data: "User Logged In", 52 | }) 53 | } 54 | reply.send({ 55 | data: "Auth Failed", 56 | }) 57 | } catch (e) { 58 | console.error(e) 59 | } 60 | }) 61 | 62 | app.get("/test", {}, async (request, reply) => { 63 | try { 64 | // Verify user login 65 | const user = await getUserFromCookies(request, reply) 66 | // Return user email, if it exists, otherwise return unauthorized 67 | if (user?._id) { 68 | reply.send({ 69 | data: user, 70 | }) 71 | } else { 72 | reply.send({ 73 | data: "User Lookup Failed", 74 | }) 75 | } 76 | } catch (e) { 77 | throw new Error(e) 78 | } 79 | }) 80 | 81 | await app.listen(3000) 82 | console.log("🚀 Server Listening at port: 3000") 83 | } catch (e) { 84 | console.error(e) 85 | } 86 | } 87 | 88 | connectDb().then(() => { 89 | startApp() 90 | }) 91 | -------------------------------------------------------------------------------- /20RefreshTokens/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /20RefreshTokens/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | -------------------------------------------------------------------------------- /20RefreshTokens/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /21Logout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /21Logout/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /21Logout/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /21Logout/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /21Logout/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /21Logout/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | import { createTokens } from "./tokens.js" 3 | 4 | export async function logUserIn(userId, request, reply) { 5 | const connectionInformation = { 6 | ip: request.ip, 7 | userAgent: request.headers["user-agent"], 8 | } 9 | // Create Session 10 | const sessionToken = await createSession(userId, connectionInformation) 11 | console.log("sessionToken", sessionToken) 12 | 13 | // Create JWT 14 | const { accessToken, refreshToken } = await createTokens(sessionToken, userId) 15 | // Set Cookie 16 | const now = new Date() 17 | // Get date, 30 days in the future 18 | const refreshExpires = now.setDate(now.getDate() + 30) 19 | reply 20 | .setCookie("refreshToken", refreshToken, { 21 | path: "/", 22 | domain: "localhost", 23 | httpOnly: true, 24 | expires: refreshExpires, 25 | }) 26 | .setCookie("accessToken", accessToken, { 27 | path: "/", 28 | domain: "localhost", 29 | httpOnly: true, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /21Logout/src/accounts/logUserOut.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | const JWTSignature = process.env.JWT_SIGNATURE 3 | 4 | export async function logUserOut(request, reply) { 5 | try { 6 | const { session } = await import("../session/session.js") 7 | 8 | if (request?.cookies?.refreshToken) { 9 | const { refreshToken } = request.cookies 10 | // Decode refresh token 11 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 12 | // Delete database record for session 13 | await session.deleteOne({ sessionToken }) 14 | } 15 | // Remove Cookies 16 | reply.clearCookie("refreshToken").clearCookie("accessToken") 17 | } catch (e) { 18 | console.error(e) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /21Logout/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /21Logout/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /21Logout/src/accounts/tokens.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | 3 | const JWTSignature = process.env.JWT_SIGNATURE 4 | 5 | export async function createTokens(sessionToken, userId) { 6 | try { 7 | // Create Refresh Token 8 | // Session Id 9 | const refreshToken = jwt.sign( 10 | { 11 | sessionToken, 12 | }, 13 | JWTSignature 14 | ) 15 | // Create Access Token 16 | // Session Id, User Id 17 | const accessToken = jwt.sign( 18 | { 19 | sessionToken, 20 | userId, 21 | }, 22 | JWTSignature 23 | ) 24 | // Return Refresh Token & Access Token 25 | return { accessToken, refreshToken } 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /21Logout/src/accounts/user.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | import jwt from "jsonwebtoken" 3 | import { createTokens } from "./tokens.js" 4 | 5 | const { ObjectId } = mongo 6 | 7 | const JWTSignature = process.env.JWT_SIGNATURE 8 | 9 | export async function getUserFromCookies(request, reply) { 10 | try { 11 | const { user } = await import("../user/user.js") 12 | const { session } = await import("../session/session.js") 13 | // Check to make sure access token exists 14 | if (request?.cookies?.accessToken) { 15 | // If access token 16 | const { accessToken } = request.cookies 17 | // Decode decode access token 18 | const decodedAccessToken = jwt.verify(accessToken, JWTSignature) 19 | // Return user from record 20 | return user.findOne({ 21 | _id: ObjectId(decodedAccessToken?.userId), 22 | }) 23 | } 24 | if (request?.cookies?.refreshToken) { 25 | const { refreshToken } = request.cookies 26 | // Decode refresh token 27 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 28 | // Look up session 29 | const currentSession = await session.findOne({ sessionToken }) 30 | // Confirm session is valid 31 | if (currentSession.valid) { 32 | // Look up current user 33 | const currentUser = await user.findOne({ 34 | _id: ObjectId(currentSession.userId), 35 | }) 36 | // refresh tokens 37 | await refreshTokens(sessionToken, currentUser._id, reply) 38 | // Return current user 39 | return currentUser 40 | } 41 | } 42 | } catch (e) { 43 | console.error(e) 44 | } 45 | } 46 | 47 | export async function refreshTokens(sessionToken, userId, reply) { 48 | try { 49 | // Create JWT 50 | const { accessToken, refreshToken } = await createTokens( 51 | sessionToken, 52 | userId 53 | ) 54 | // Set Cookie 55 | const now = new Date() 56 | // Get date, 30 days in the future 57 | const refreshExpires = now.setDate(now.getDate() + 30) 58 | reply 59 | .setCookie("refreshToken", refreshToken, { 60 | path: "/", 61 | domain: "localhost", 62 | httpOnly: true, 63 | expires: refreshExpires, 64 | }) 65 | .setCookie("accessToken", accessToken, { 66 | path: "/", 67 | domain: "localhost", 68 | httpOnly: true, 69 | }) 70 | } catch (e) { 71 | console.error(e) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /21Logout/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /21Logout/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /21Logout/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | import { logUserOut } from "./accounts/logUserOut.js" 12 | import { getUserFromCookies } from "./accounts/user.js" 13 | 14 | // ESM specific features 15 | const __filename = fileURLToPath(import.meta.url) 16 | const __dirname = path.dirname(__filename) 17 | 18 | const app = fastify() 19 | 20 | console.log(process.env.MONGO_URL) 21 | 22 | async function startApp() { 23 | try { 24 | app.register(fastifyCookie, { 25 | secret: process.env.COOKIE_SIGNATURE, 26 | }) 27 | 28 | app.register(fastifyStatic, { 29 | root: path.join(__dirname, "public"), 30 | }) 31 | 32 | app.post("/api/register", {}, async (request, reply) => { 33 | try { 34 | const userId = await registerUser( 35 | request.body.email, 36 | request.body.password 37 | ) 38 | } catch (e) { 39 | console.error(e) 40 | } 41 | }) 42 | 43 | app.post("/api/authorize", {}, async (request, reply) => { 44 | try { 45 | const { isAuthorized, userId } = await authorizeUser( 46 | request.body.email, 47 | request.body.password 48 | ) 49 | if (isAuthorized) { 50 | await logUserIn(userId, request, reply) 51 | reply.send({ 52 | data: "User Logged In", 53 | }) 54 | } 55 | reply.send({ 56 | data: "Auth Failed", 57 | }) 58 | } catch (e) { 59 | console.error(e) 60 | } 61 | }) 62 | 63 | app.post("/api/logout", {}, async (request, reply) => { 64 | try { 65 | await logUserOut(request, reply) 66 | reply.send({ 67 | data: "User Logged Out", 68 | }) 69 | } catch (e) { 70 | console.error(e) 71 | } 72 | }) 73 | 74 | app.get("/test", {}, async (request, reply) => { 75 | try { 76 | // Verify user login 77 | const user = await getUserFromCookies(request, reply) 78 | // Return user email, if it exists, otherwise return unauthorized 79 | if (user?._id) { 80 | reply.send({ 81 | data: user, 82 | }) 83 | } else { 84 | reply.send({ 85 | data: "User Lookup Failed", 86 | }) 87 | } 88 | } catch (e) { 89 | throw new Error(e) 90 | } 91 | }) 92 | 93 | await app.listen(3000) 94 | console.log("🚀 Server Listening at port: 3000") 95 | } catch (e) { 96 | console.error(e) 97 | } 98 | } 99 | 100 | connectDb().then(() => { 101 | startApp() 102 | }) 103 | -------------------------------------------------------------------------------- /21Logout/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /21Logout/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | -------------------------------------------------------------------------------- /21Logout/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | -------------------------------------------------------------------------------- /22PolishAndCleanup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /22PolishAndCleanup/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | import { refreshTokens } from "./user.js" 3 | 4 | export async function logUserIn(userId, request, reply) { 5 | const connectionInformation = { 6 | ip: request.ip, 7 | userAgent: request.headers["user-agent"], 8 | } 9 | // Create Session 10 | const sessionToken = await createSession(userId, connectionInformation) 11 | 12 | // Create JWT 13 | // Set Cookie 14 | await refreshTokens(sessionToken, userId, reply) 15 | } 16 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/logUserOut.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | const JWTSignature = process.env.JWT_SIGNATURE 3 | 4 | export async function logUserOut(request, reply) { 5 | try { 6 | const { session } = await import("../session/session.js") 7 | 8 | if (request?.cookies?.refreshToken) { 9 | const { refreshToken } = request.cookies 10 | // Decode refresh token 11 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 12 | // Delete database record for session 13 | await session.deleteOne({ sessionToken }) 14 | } 15 | // Remove Cookies 16 | reply.clearCookie("refreshToken").clearCookie("accessToken") 17 | } catch (e) { 18 | console.error(e) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/tokens.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | 3 | const JWTSignature = process.env.JWT_SIGNATURE 4 | 5 | export async function createTokens(sessionToken, userId) { 6 | try { 7 | // Create Refresh Token 8 | // Session Id 9 | const refreshToken = jwt.sign( 10 | { 11 | sessionToken, 12 | }, 13 | JWTSignature 14 | ) 15 | // Create Access Token 16 | // Session Id, User Id 17 | const accessToken = jwt.sign( 18 | { 19 | sessionToken, 20 | userId, 21 | }, 22 | JWTSignature 23 | ) 24 | // Return Refresh Token & Access Token 25 | return { accessToken, refreshToken } 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/accounts/user.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | import jwt from "jsonwebtoken" 3 | import { createTokens } from "./tokens.js" 4 | 5 | const { ObjectId } = mongo 6 | 7 | const JWTSignature = process.env.JWT_SIGNATURE 8 | 9 | export async function getUserFromCookies(request, reply) { 10 | try { 11 | const { user } = await import("../user/user.js") 12 | const { session } = await import("../session/session.js") 13 | // Check to make sure access token exists 14 | if (request?.cookies?.accessToken) { 15 | // If access token 16 | const { accessToken } = request.cookies 17 | // Decode decode access token 18 | const decodedAccessToken = jwt.verify(accessToken, JWTSignature) 19 | // Return user from record 20 | return user.findOne({ 21 | _id: ObjectId(decodedAccessToken?.userId), 22 | }) 23 | } 24 | if (request?.cookies?.refreshToken) { 25 | const { refreshToken } = request.cookies 26 | // Decode refresh token 27 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 28 | // Look up session 29 | const currentSession = await session.findOne({ sessionToken }) 30 | // Confirm session is valid 31 | if (currentSession.valid) { 32 | // Look up current user 33 | const currentUser = await user.findOne({ 34 | _id: ObjectId(currentSession.userId), 35 | }) 36 | // refresh tokens 37 | await refreshTokens(sessionToken, currentUser._id, reply) 38 | // Return current user 39 | return currentUser 40 | } 41 | } 42 | } catch (e) { 43 | console.error(e) 44 | } 45 | } 46 | 47 | export async function refreshTokens(sessionToken, userId, reply) { 48 | try { 49 | // Create JWT 50 | const { accessToken, refreshToken } = await createTokens( 51 | sessionToken, 52 | userId 53 | ) 54 | // Set Cookie 55 | const now = new Date() 56 | // Get date, 30 days in the future 57 | const refreshExpires = now.setDate(now.getDate() + 30) 58 | reply 59 | .setCookie("refreshToken", refreshToken, { 60 | path: "/", 61 | domain: "localhost", 62 | httpOnly: true, 63 | expires: refreshExpires, 64 | }) 65 | .setCookie("accessToken", accessToken, { 66 | path: "/", 67 | domain: "localhost", 68 | httpOnly: true, 69 | }) 70 | } catch (e) { 71 | console.error(e) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | import { logUserOut } from "./accounts/logUserOut.js" 12 | import { getUserFromCookies } from "./accounts/user.js" 13 | 14 | // ESM specific features 15 | const __filename = fileURLToPath(import.meta.url) 16 | const __dirname = path.dirname(__filename) 17 | 18 | const app = fastify() 19 | 20 | async function startApp() { 21 | try { 22 | app.register(fastifyCookie, { 23 | secret: process.env.COOKIE_SIGNATURE, 24 | }) 25 | 26 | app.register(fastifyStatic, { 27 | root: path.join(__dirname, "public"), 28 | }) 29 | 30 | app.post("/api/register", {}, async (request, reply) => { 31 | try { 32 | const userId = await registerUser( 33 | request.body.email, 34 | request.body.password 35 | ) 36 | if (userId) { 37 | await logUserIn(userId, request, reply) 38 | reply.send({ 39 | data: { 40 | status: "SUCCESS", 41 | userId, 42 | }, 43 | }) 44 | } 45 | } catch (e) { 46 | console.error(e) 47 | reply.send({ 48 | data: { 49 | status: "FAILED", 50 | userId, 51 | }, 52 | }) 53 | } 54 | }) 55 | 56 | app.post("/api/authorize", {}, async (request, reply) => { 57 | try { 58 | const { isAuthorized, userId } = await authorizeUser( 59 | request.body.email, 60 | request.body.password 61 | ) 62 | if (isAuthorized) { 63 | await logUserIn(userId, request, reply) 64 | reply.send({ 65 | data: { 66 | status: "SUCCESS", 67 | userId, 68 | }, 69 | }) 70 | } 71 | } catch (e) { 72 | console.error(e) 73 | reply.send({ 74 | data: { 75 | status: "FAILED", 76 | userId, 77 | }, 78 | }) 79 | } 80 | }) 81 | 82 | app.post("/api/logout", {}, async (request, reply) => { 83 | try { 84 | await logUserOut(request, reply) 85 | reply.send({ 86 | data: { 87 | status: "SUCCESS", 88 | }, 89 | }) 90 | } catch (e) { 91 | console.error(e) 92 | reply.send({ 93 | data: { 94 | status: "FAILED", 95 | userId, 96 | }, 97 | }) 98 | } 99 | }) 100 | 101 | app.get("/test", {}, async (request, reply) => { 102 | try { 103 | // Verify user login 104 | const user = await getUserFromCookies(request, reply) 105 | // Return user email, if it exists, otherwise return unauthorized 106 | if (user?._id) { 107 | reply.send({ 108 | data: user, 109 | }) 110 | } else { 111 | reply.send({ 112 | data: "User Lookup Failed", 113 | }) 114 | } 115 | } catch (e) { 116 | throw new Error(e) 117 | } 118 | }) 119 | 120 | await app.listen(3000) 121 | console.log("🚀 Server Listening at port: 3000") 122 | } catch (e) { 123 | console.error(e) 124 | } 125 | } 126 | 127 | connectDb().then(() => { 128 | startApp() 129 | }) 130 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | 5 | session.createIndex({ sessionToken: 1 }) 6 | -------------------------------------------------------------------------------- /22PolishAndCleanup/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | 5 | user.createIndex({ "email.address": 1 }) 6 | -------------------------------------------------------------------------------- /2FastifyUpandRunning/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "fastify": "^3.11.0", 14 | "fastify-static": "^3.4.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /2FastifyUpandRunning/src/db.js: -------------------------------------------------------------------------------- 1 | console.log("hello from the db") 2 | -------------------------------------------------------------------------------- /2FastifyUpandRunning/src/index.js: -------------------------------------------------------------------------------- 1 | import { fastify } from "fastify" 2 | import fastifyStatic from "fastify-static" 3 | import path from "path" 4 | import { fileURLToPath } from "url" 5 | 6 | // ESM specific features 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = path.dirname(__filename) 9 | 10 | const app = fastify() 11 | 12 | async function startApp() { 13 | try { 14 | app.register(fastifyStatic, { 15 | root: path.join(__dirname, "public"), 16 | }) 17 | 18 | // app.get("/", {}, (request, reply) => { 19 | // reply.send({ 20 | // data: "hello world", 21 | // }) 22 | // }) 23 | 24 | await app.listen(3000) 25 | console.log("🚀 Server Listening at port: 3000") 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | 31 | startApp() 32 | -------------------------------------------------------------------------------- /2FastifyUpandRunning/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 | 13 | -------------------------------------------------------------------------------- /3DotEnv/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "fastify": "^3.11.0", 15 | "fastify-static": "^3.4.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /3DotEnv/src/db.js: -------------------------------------------------------------------------------- 1 | console.log("hello from the db") 2 | -------------------------------------------------------------------------------- /3DotEnv/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /3DotEnv/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | 7 | // ESM specific features 8 | const __filename = fileURLToPath(import.meta.url) 9 | const __dirname = path.dirname(__filename) 10 | 11 | const app = fastify() 12 | 13 | console.log(process.env.MONGO_URL) 14 | 15 | async function startApp() { 16 | try { 17 | app.register(fastifyStatic, { 18 | root: path.join(__dirname, "public"), 19 | }) 20 | 21 | // app.get("/", {}, (request, reply) => { 22 | // reply.send({ 23 | // data: "hello world", 24 | // }) 25 | // }) 26 | 27 | await app.listen(3000) 28 | console.log("🚀 Server Listening at port: 3000") 29 | } catch (e) { 30 | console.error(e) 31 | } 32 | } 33 | 34 | startApp() 35 | -------------------------------------------------------------------------------- /3DotEnv/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 | 13 | -------------------------------------------------------------------------------- /4ConnectingToMongoDB/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "fastify": "^3.11.0", 15 | "fastify-static": "^3.4.0", 16 | "mongodb": "^3.6.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /4ConnectingToMongoDB/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /4ConnectingToMongoDB/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /4ConnectingToMongoDB/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | 8 | // ESM specific features 9 | const __filename = fileURLToPath(import.meta.url) 10 | const __dirname = path.dirname(__filename) 11 | 12 | const app = fastify() 13 | 14 | console.log(process.env.MONGO_URL) 15 | 16 | async function startApp() { 17 | try { 18 | app.register(fastifyStatic, { 19 | root: path.join(__dirname, "public"), 20 | }) 21 | 22 | // app.get("/", {}, (request, reply) => { 23 | // reply.send({ 24 | // data: "hello world", 25 | // }) 26 | // }) 27 | 28 | await app.listen(3000) 29 | console.log("🚀 Server Listening at port: 3000") 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | } 34 | 35 | connectDb().then(() => { 36 | startApp() 37 | }) 38 | -------------------------------------------------------------------------------- /4ConnectingToMongoDB/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 | 13 | -------------------------------------------------------------------------------- /6FormsWithVanillaJS/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "fastify": "^3.11.0", 15 | "fastify-static": "^3.4.0", 16 | "mongodb": "^3.6.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /6FormsWithVanillaJS/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /6FormsWithVanillaJS/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /6FormsWithVanillaJS/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | 8 | // ESM specific features 9 | const __filename = fileURLToPath(import.meta.url) 10 | const __dirname = path.dirname(__filename) 11 | 12 | const app = fastify() 13 | 14 | console.log(process.env.MONGO_URL) 15 | 16 | async function startApp() { 17 | try { 18 | app.register(fastifyStatic, { 19 | root: path.join(__dirname, "public"), 20 | }) 21 | 22 | // app.get("/", {}, (request, reply) => { 23 | // reply.send({ 24 | // data: "hello world", 25 | // }) 26 | // }) 27 | 28 | await app.listen(3000) 29 | console.log("🚀 Server Listening at port: 3000") 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | } 34 | 35 | connectDb().then(() => { 36 | startApp() 37 | }) 38 | -------------------------------------------------------------------------------- /6FormsWithVanillaJS/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-static": "^3.4.0", 17 | "mongodb": "^3.6.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | // generate salt 7 | const salt = await genSalt(10) 8 | 9 | // hash with salt 10 | const hashedPassword = await hash(password, salt) 11 | 12 | // Store in database 13 | 14 | // Return user from database 15 | } 16 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { registerUser } from "./accounts/register.js" 8 | 9 | // ESM specific features 10 | const __filename = fileURLToPath(import.meta.url) 11 | const __dirname = path.dirname(__filename) 12 | 13 | const app = fastify() 14 | 15 | console.log(process.env.MONGO_URL) 16 | 17 | async function startApp() { 18 | try { 19 | app.register(fastifyStatic, { 20 | root: path.join(__dirname, "public"), 21 | }) 22 | 23 | app.post("/api/register", {}, async (request, reply) => { 24 | try { 25 | const userId = await registerUser( 26 | request.body.email, 27 | request.body.password 28 | ) 29 | console.log("userId", userId) 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | }) 34 | 35 | // app.get("/", {}, (request, reply) => { 36 | // reply.send({ 37 | // data: "hello world", 38 | // }) 39 | // }) 40 | 41 | await app.listen(3000) 42 | console.log("🚀 Server Listening at port: 3000") 43 | } catch (e) { 44 | console.error(e) 45 | } 46 | } 47 | 48 | connectDb().then(() => { 49 | startApp() 50 | }) 51 | -------------------------------------------------------------------------------- /7FetchPostRequests/node-auth/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /7FetchPostRequests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "fastify": "^3.11.0", 15 | "fastify-static": "^3.4.0", 16 | "mongodb": "^3.6.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /7FetchPostRequests/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /7FetchPostRequests/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /7FetchPostRequests/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { request } from "http" 8 | 9 | // ESM specific features 10 | const __filename = fileURLToPath(import.meta.url) 11 | const __dirname = path.dirname(__filename) 12 | 13 | const app = fastify() 14 | 15 | console.log(process.env.MONGO_URL) 16 | 17 | async function startApp() { 18 | try { 19 | app.register(fastifyStatic, { 20 | root: path.join(__dirname, "public"), 21 | }) 22 | 23 | app.post("/api/register", {}, (request, reply) => { 24 | console.log("request", request.body.email, request.body.password) 25 | }) 26 | 27 | // app.get("/", {}, (request, reply) => { 28 | // reply.send({ 29 | // data: "hello world", 30 | // }) 31 | // }) 32 | 33 | await app.listen(3000) 34 | console.log("🚀 Server Listening at port: 3000") 35 | } catch (e) { 36 | console.error(e) 37 | } 38 | } 39 | 40 | connectDb().then(() => { 41 | startApp() 42 | }) 43 | -------------------------------------------------------------------------------- /7FetchPostRequests/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /8WhatIsEncryptionSaltingAndHashing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "fastify": "^3.11.0", 15 | "fastify-static": "^3.4.0", 16 | "mongodb": "^3.6.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /8WhatIsEncryptionSaltingAndHashing/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /8WhatIsEncryptionSaltingAndHashing/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /8WhatIsEncryptionSaltingAndHashing/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { request } from "http" 8 | 9 | // ESM specific features 10 | const __filename = fileURLToPath(import.meta.url) 11 | const __dirname = path.dirname(__filename) 12 | 13 | const app = fastify() 14 | 15 | console.log(process.env.MONGO_URL) 16 | 17 | async function startApp() { 18 | try { 19 | app.register(fastifyStatic, { 20 | root: path.join(__dirname, "public"), 21 | }) 22 | 23 | app.post("/api/register", {}, (request, reply) => { 24 | console.log("request", request.body.email, request.body.password) 25 | }) 26 | 27 | // app.get("/", {}, (request, reply) => { 28 | // reply.send({ 29 | // data: "hello world", 30 | // }) 31 | // }) 32 | 33 | await app.listen(3000) 34 | console.log("🚀 Server Listening at port: 3000") 35 | } catch (e) { 36 | console.error(e) 37 | } 38 | } 39 | 40 | connectDb().then(() => { 41 | startApp() 42 | }) 43 | -------------------------------------------------------------------------------- /8WhatIsEncryptionSaltingAndHashing/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /9SaltingAndHashing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-static": "^3.4.0", 17 | "mongodb": "^3.6.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /9SaltingAndHashing/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /9SaltingAndHashing/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | // generate salt 7 | const salt = await genSalt(10) 8 | 9 | // hash with salt 10 | const hashedPassword = await hash(password, salt) 11 | 12 | // Store in database 13 | 14 | // Return user from database 15 | } 16 | -------------------------------------------------------------------------------- /9SaltingAndHashing/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /9SaltingAndHashing/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /9SaltingAndHashing/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import path from "path" 5 | import { fileURLToPath } from "url" 6 | import { connectDb } from "./db.js" 7 | import { registerUser } from "./accounts/register.js" 8 | 9 | // ESM specific features 10 | const __filename = fileURLToPath(import.meta.url) 11 | const __dirname = path.dirname(__filename) 12 | 13 | const app = fastify() 14 | 15 | console.log(process.env.MONGO_URL) 16 | 17 | async function startApp() { 18 | try { 19 | app.register(fastifyStatic, { 20 | root: path.join(__dirname, "public"), 21 | }) 22 | 23 | app.post("/api/register", {}, async (request, reply) => { 24 | try { 25 | const userId = await registerUser( 26 | request.body.email, 27 | request.body.password 28 | ) 29 | console.log("userId", userId) 30 | } catch (e) { 31 | console.error(e) 32 | } 33 | }) 34 | 35 | // app.get("/", {}, (request, reply) => { 36 | // reply.send({ 37 | // data: "hello world", 38 | // }) 39 | // }) 40 | 41 | await app.listen(3000) 42 | console.log("🚀 Server Listening at port: 3000") 43 | } catch (e) { 44 | console.error(e) 45 | } 46 | } 47 | 48 | connectDb().then(() => { 49 | startApp() 50 | }) 51 | -------------------------------------------------------------------------------- /9SaltingAndHashing/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /END_OF_SERIES/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Scott Tolinski", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "dotenv": "^8.2.0", 15 | "fastify": "^3.11.0", 16 | "fastify-cookie": "^5.1.0", 17 | "fastify-static": "^3.4.0", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongodb": "^3.6.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/AccessVsRefresh.txt: -------------------------------------------------------------------------------- 1 | Access Token 2 | * JWT 3 | * Contains all of the info someone needs to be logged 4 | * Says this user has access 5 | * Only available current session 6 | 7 | 8 | Refresh Token 9 | * JWT 10 | * Only contains session id 11 | * Be used to generate new access token 12 | * Used to refresh the access token -------------------------------------------------------------------------------- /END_OF_SERIES/src/Encryption.txt: -------------------------------------------------------------------------------- 1 | Encryption, 2 | Allows you to use a key to obfuscate and retrieve data 3 | 4 | password 5 | 6 | password X key 7 | 8 | qbttxpse 9 | 10 | qbttxpse / key 11 | 12 | password 13 | 14 | 15 | 16 | Hashing 17 | A one way trip. Not meant to be reversed 18 | 19 | password 20 | password x hash function 21 | hashed data 22 | 23 | md4, md5, sha 24 | Sha - security hashing algorithm 25 | sha-256 26 | 27 | 28 | Salt 29 | 30 | adds additional data (salt) 31 | 32 | passwordsalt 33 | passwordsalt x hash function 34 | creates a unique hash 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/HTTPOnlyCookies.txt: -------------------------------------------------------------------------------- 1 | HTTPOnly Cookies 2 | 3 | 4 | 5 | Local Storage and normal cookies are both 6 | * Stored in the browser 7 | * Accessible by the client 8 | * Able to be read / wrote by the browser 9 | * Able to be accessed by any browser extension on your browser 10 | 11 | 12 | HTTPOnly Cookies 13 | * Only accessible via the server 14 | * Only writeable via the server -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/authorize.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | const { compare } = bcrypt 3 | 4 | export async function authorizeUser(email, password) { 5 | // Import user collection 6 | const { user } = await import("../user/user.js") 7 | // Look up user 8 | const userData = await user.findOne({ 9 | "email.address": email, 10 | }) 11 | // Get user Password 12 | const savedPassword = userData.password 13 | // Compare password with one in database 14 | const isAuthorized = await compare(password, savedPassword) 15 | // Return boolean of if password is correct 16 | return { isAuthorized, userId: userData._id } 17 | } 18 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/logUserIn.js: -------------------------------------------------------------------------------- 1 | import { createSession } from "./session.js" 2 | import { refreshTokens } from "./user.js" 3 | 4 | export async function logUserIn(userId, request, reply) { 5 | const connectionInformation = { 6 | ip: request.ip, 7 | userAgent: request.headers["user-agent"], 8 | } 9 | // Create Session 10 | const sessionToken = await createSession(userId, connectionInformation) 11 | 12 | // Create JWT 13 | // Set Cookie 14 | await refreshTokens(sessionToken, userId, reply) 15 | } 16 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/logUserOut.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | const JWTSignature = process.env.JWT_SIGNATURE 3 | 4 | export async function logUserOut(request, reply) { 5 | try { 6 | const { session } = await import("../session/session.js") 7 | 8 | if (request?.cookies?.refreshToken) { 9 | const { refreshToken } = request.cookies 10 | // Decode refresh token 11 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 12 | // Delete database record for session 13 | await session.deleteOne({ sessionToken }) 14 | } 15 | // Remove Cookies 16 | reply.clearCookie("refreshToken").clearCookie("accessToken") 17 | } catch (e) { 18 | console.error(e) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/register.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs" 2 | 3 | const { genSalt, hash } = bcrypt 4 | 5 | export async function registerUser(email, password) { 6 | const { user } = await import("../user/user.js") 7 | 8 | // generate salt 9 | const salt = await genSalt(10) 10 | 11 | // hash with salt 12 | const hashedPassword = await hash(password, salt) 13 | 14 | // Store in database 15 | const result = await user.insertOne({ 16 | email: { 17 | address: email, 18 | verified: false, 19 | }, 20 | password: hashedPassword, 21 | }) 22 | // Return user from database 23 | return result.insertedId 24 | } 25 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/session.js: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto" 2 | 3 | export async function createSession(userId, connection) { 4 | try { 5 | // Generate a session token 6 | const sessionToken = randomBytes(43).toString("hex") 7 | // retrieve connection information 8 | const { ip, userAgent } = connection 9 | // database insert for session 10 | const { session } = await import("../session/session.js") 11 | await session.insertOne({ 12 | sessionToken, 13 | userId, 14 | valid: true, 15 | userAgent, 16 | ip, 17 | updatedAt: new Date(), 18 | createdAt: new Date(), 19 | }) 20 | // Return session token 21 | return sessionToken 22 | } catch (e) { 23 | throw new Error("Session Creation Failed") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/tokens.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken" 2 | 3 | const JWTSignature = process.env.JWT_SIGNATURE 4 | 5 | export async function createTokens(sessionToken, userId) { 6 | try { 7 | // Create Refresh Token 8 | // Session Id 9 | const refreshToken = jwt.sign( 10 | { 11 | sessionToken, 12 | }, 13 | JWTSignature 14 | ) 15 | // Create Access Token 16 | // Session Id, User Id 17 | const accessToken = jwt.sign( 18 | { 19 | sessionToken, 20 | userId, 21 | }, 22 | JWTSignature 23 | ) 24 | // Return Refresh Token & Access Token 25 | return { accessToken, refreshToken } 26 | } catch (e) { 27 | console.error(e) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/accounts/user.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | import jwt from "jsonwebtoken" 3 | import { createTokens } from "./tokens.js" 4 | 5 | const { ObjectId } = mongo 6 | 7 | const JWTSignature = process.env.JWT_SIGNATURE 8 | 9 | export async function getUserFromCookies(request, reply) { 10 | try { 11 | const { user } = await import("../user/user.js") 12 | const { session } = await import("../session/session.js") 13 | // Check to make sure access token exists 14 | if (request?.cookies?.accessToken) { 15 | // If access token 16 | const { accessToken } = request.cookies 17 | // Decode decode access token 18 | const decodedAccessToken = jwt.verify(accessToken, JWTSignature) 19 | // Return user from record 20 | return user.findOne({ 21 | _id: ObjectId(decodedAccessToken?.userId), 22 | }) 23 | } 24 | if (request?.cookies?.refreshToken) { 25 | const { refreshToken } = request.cookies 26 | // Decode refresh token 27 | const { sessionToken } = jwt.verify(refreshToken, JWTSignature) 28 | // Look up session 29 | const currentSession = await session.findOne({ sessionToken }) 30 | // Confirm session is valid 31 | if (currentSession.valid) { 32 | // Look up current user 33 | const currentUser = await user.findOne({ 34 | _id: ObjectId(currentSession.userId), 35 | }) 36 | // refresh tokens 37 | await refreshTokens(sessionToken, currentUser._id, reply) 38 | // Return current user 39 | return currentUser 40 | } 41 | } 42 | } catch (e) { 43 | console.error(e) 44 | } 45 | } 46 | 47 | export async function refreshTokens(sessionToken, userId, reply) { 48 | try { 49 | // Create JWT 50 | const { accessToken, refreshToken } = await createTokens( 51 | sessionToken, 52 | userId 53 | ) 54 | // Set Cookie 55 | const now = new Date() 56 | // Get date, 30 days in the future 57 | const refreshExpires = now.setDate(now.getDate() + 30) 58 | reply 59 | .setCookie("refreshToken", refreshToken, { 60 | path: "/", 61 | domain: "localhost", 62 | httpOnly: true, 63 | expires: refreshExpires, 64 | }) 65 | .setCookie("accessToken", accessToken, { 66 | path: "/", 67 | domain: "localhost", 68 | httpOnly: true, 69 | }) 70 | } catch (e) { 71 | console.error(e) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/db.js: -------------------------------------------------------------------------------- 1 | import mongo from "mongodb" 2 | 3 | const { MongoClient } = mongo 4 | 5 | const url = process.env.MONGO_URL 6 | 7 | export const client = new MongoClient(url, { useNewUrlParser: true }) 8 | 9 | export async function connectDb() { 10 | try { 11 | await client.connect() 12 | 13 | // Confirm connection 14 | await client.db("admin").command({ ping: 1 }) 15 | console.log("🗄️ Connected to DB Success") 16 | } catch (e) { 17 | console.error(e) 18 | // If there is a problem close connection to db 19 | await client.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/env.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | 3 | dotenv.config() 4 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/index.js: -------------------------------------------------------------------------------- 1 | import "./env.js" 2 | import { fastify } from "fastify" 3 | import fastifyStatic from "fastify-static" 4 | import fastifyCookie from "fastify-cookie" 5 | import path from "path" 6 | import { fileURLToPath } from "url" 7 | import { connectDb } from "./db.js" 8 | import { registerUser } from "./accounts/register.js" 9 | import { authorizeUser } from "./accounts/authorize.js" 10 | import { logUserIn } from "./accounts/logUserIn.js" 11 | import { logUserOut } from "./accounts/logUserOut.js" 12 | import { getUserFromCookies } from "./accounts/user.js" 13 | 14 | // ESM specific features 15 | const __filename = fileURLToPath(import.meta.url) 16 | const __dirname = path.dirname(__filename) 17 | 18 | const app = fastify() 19 | 20 | async function startApp() { 21 | try { 22 | app.register(fastifyCookie, { 23 | secret: process.env.COOKIE_SIGNATURE, 24 | }) 25 | 26 | app.register(fastifyStatic, { 27 | root: path.join(__dirname, "public"), 28 | }) 29 | 30 | app.post("/api/register", {}, async (request, reply) => { 31 | try { 32 | const userId = await registerUser( 33 | request.body.email, 34 | request.body.password 35 | ) 36 | if (userId) { 37 | await logUserIn(userId, request, reply) 38 | reply.send({ 39 | data: { 40 | status: "SUCCESS", 41 | userId, 42 | }, 43 | }) 44 | } 45 | } catch (e) { 46 | console.error(e) 47 | reply.send({ 48 | data: { 49 | status: "FAILED", 50 | userId, 51 | }, 52 | }) 53 | } 54 | }) 55 | 56 | app.post("/api/authorize", {}, async (request, reply) => { 57 | try { 58 | const { isAuthorized, userId } = await authorizeUser( 59 | request.body.email, 60 | request.body.password 61 | ) 62 | if (isAuthorized) { 63 | await logUserIn(userId, request, reply) 64 | reply.send({ 65 | data: { 66 | status: "SUCCESS", 67 | userId, 68 | }, 69 | }) 70 | } 71 | } catch (e) { 72 | console.error(e) 73 | reply.send({ 74 | data: { 75 | status: "FAILED", 76 | userId, 77 | }, 78 | }) 79 | } 80 | }) 81 | 82 | app.post("/api/logout", {}, async (request, reply) => { 83 | try { 84 | await logUserOut(request, reply) 85 | reply.send({ 86 | data: { 87 | status: "SUCCESS", 88 | }, 89 | }) 90 | } catch (e) { 91 | console.error(e) 92 | reply.send({ 93 | data: { 94 | status: "FAILED", 95 | userId, 96 | }, 97 | }) 98 | } 99 | }) 100 | 101 | app.get("/test", {}, async (request, reply) => { 102 | try { 103 | // Verify user login 104 | const user = await getUserFromCookies(request, reply) 105 | // Return user email, if it exists, otherwise return unauthorized 106 | if (user?._id) { 107 | reply.send({ 108 | data: user, 109 | }) 110 | } else { 111 | reply.send({ 112 | data: "User Lookup Failed", 113 | }) 114 | } 115 | } catch (e) { 116 | throw new Error(e) 117 | } 118 | }) 119 | 120 | await app.listen(3000) 121 | console.log("🚀 Server Listening at port: 3000") 122 | } catch (e) { 123 | console.error(e) 124 | } 125 | } 126 | 127 | connectDb().then(() => { 128 | startApp() 129 | }) 130 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello

11 | 12 |

Register Form

13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |

Login Form

24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/session/session.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const session = client.db("test").collection("session") 4 | 5 | session.createIndex({ sessionToken: 1 }) 6 | -------------------------------------------------------------------------------- /END_OF_SERIES/src/user/user.js: -------------------------------------------------------------------------------- 1 | import { client } from "../db.js" 2 | 3 | export const user = client.db("test").collection("user") 4 | 5 | user.createIndex({ "email.address": 1 }) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Level Up Tutorials 2 | 3 | ## Node Fundamentals Authentication 4 | 5 | ### https://www.leveluptutorials.com/tutorials/node-fundamentals-authentication 6 | --------------------------------------------------------------------------------