├── .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 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
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 |
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 |
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 |
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 |
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 |
18 |
19 |
20 |
21 |
22 |
23 | Login Form
24 |
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 |
--------------------------------------------------------------------------------