From 518abdc55e6f7efb5c714251bd6f30beb20c2283 Mon Sep 17 00:00:00 2001 From: Mike Smith <89040888+smiggiddy@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:38:36 -0500 Subject: [PATCH] auth: learning express auth --- auth-expressjs/migrations/init.sql | 14 ++ auth-expressjs/src/app.js | 189 ++++++++++++++++++++++ auth-expressjs/src/app.js.bak | 104 ++++++++++++ auth-expressjs/src/views/index.ejs | 29 ++++ auth-expressjs/src/views/log-in.ejs | 9 ++ auth-expressjs/src/views/sign-up-form.ejs | 18 +++ 6 files changed, 363 insertions(+) create mode 100644 auth-expressjs/migrations/init.sql create mode 100644 auth-expressjs/src/app.js create mode 100644 auth-expressjs/src/app.js.bak create mode 100644 auth-expressjs/src/views/index.ejs create mode 100644 auth-expressjs/src/views/log-in.ejs create mode 100644 auth-expressjs/src/views/sign-up-form.ejs diff --git a/auth-expressjs/migrations/init.sql b/auth-expressjs/migrations/init.sql new file mode 100644 index 0000000..3a5b5d8 --- /dev/null +++ b/auth-expressjs/migrations/init.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + username VARCHAR ( 255 ), + password VARCHAR (255) +); + +CREATE TABLE IF NOT EXISTS federated_credentials ( + user_id INTEGER NOT NULL, + provider TEXT NOT NULL, + subject TEXT NOT NULL, + PRIMARY KEY (provider, subject) +); + + diff --git a/auth-expressjs/src/app.js b/auth-expressjs/src/app.js new file mode 100644 index 0000000..330a6fb --- /dev/null +++ b/auth-expressjs/src/app.js @@ -0,0 +1,189 @@ +const path = require("node:path"); +const { Pool } = require("pg"); +const express = require("express"); +const session = require("express-session"); +const passport = require("passport"); +const LocalStrategy = require("passport-local"); +const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy; +const fs = require("node:fs"); +const bcryptjs = require("bcryptjs"); + +const pool = new Pool({ + connectionString: process.env.CONNECTION_STRING, +}); + +const initSQL = path.join(__dirname, "../migrations/init.sql"); + +fs.readFile(initSQL, "utf8", (err, data) => { + if (err) { + console.error(err); + return; + } + pool.query(data); +}); + +console.log(process.env.DEEZNUTS); +const app = express(); +app.set("views", path.join(__dirname, "views")); +app.set("view engine", "ejs"); + +//app.use(session({ secret: "cats", resave: false, saveUninitialized: false })); +app.use( + session({ + store: new (require("connect-pg-simple")(session))({ + pool: pool, + tableName: "user_sessions", + createTableIfMissing: true, + }), + resave: false, + cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 }, + saveUninitialized: false, + secret: "deezNuts", + }), +); +app.use(passport.session()); +app.use(express.urlencoded({ extended: false })); + +app.listen(3000, () => console.log("app running on port 3000")); + +passport.use( + new LocalStrategy(async (username, password, done) => { + try { + const { rows } = await pool.query( + "SELECT * FROM users WHERE username = $1", + [username], + ); + const user = rows[0]; + const match = await bcryptjs.compare(password, user.password); + + if (!user) { + return done(null, false, { message: "Incorrect username/password." }); + } + + if (!match) { + return done(null, false, { message: "incorrect username/password" }); + } + + return done(null, user); + } catch (err) { + return done(err); + } + }), +); + +passport.use( + new GoogleStrategy( + { + clientID: process.env["GOOGLE_CLIENT_ID"], + clientSecret: process.env["GOOGLE_CLIENT_SECRET"], + callbackURL: "/oauth2/redirect/google", + scope: ["profile"], + }, + async function verify(accessToken, refreshToken, profile, cb) { + try { + await pool.query("BEGIN"); + const { rows: federatedRows } = await pool.query( + "SELECT * FROM federated_credentials WHERE provider = $1 AND subject = $2", + ["https://accounts.google.com", profile.id], + ); + let id = federatedRows.length > 0 ? federatedRows[0].user_id : null; + if (federatedRows.length === 0) { + const { rows: newUserRows } = await pool.query( + "INSERT INTO users (username) VALUES ($1) RETURNING *", + [profile.displayName], + ); + id = newUserRows[0].id; + await pool.query( + "INSERT INTO federated_credentials (user_id, provider, subject) VALUES ($1, $2, $3)", + [id, "https://accounts.google.com", profile.id], + ); + const user = { + id: id, + name: profile.displayName, + }; + pool.query("COMMIT"); + cb(null, user); + } else { + const { rows: userRows } = await pool.query( + "SELECT * FROM users WHERE id = $1", + [id], + ); + cb(null, userRows[0]); + } + } catch (e) { + await pool.query("ROLLBACK"); + cb(e); + } + }, + ), +); + +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +passport.deserializeUser(async (id, done) => { + try { + const { rows } = await pool.query("SELECT * FROM users WHERE id= $1", [id]); + const user = rows[0]; + done(null, user); + } catch (e) { + done(e); + } +}); + +app.use((req, res, next) => { + console.log(req.user); + console.table(req.session.passport); + next(); +}); + +app.get("/", (req, res) => { + if (req.session.viewCount) { + req.session.viewCount++; + } else { + req.session.viewCount = 1; + req.session.deezNuts = "big AF dog"; + } + res.render("index", { user: req.user, sessions: req.session.viewCount }); +}); + +app.get("/sign-up", (req, res) => res.render("sign-up-form")); +app.post("/sign-up", async (req, res, next) => { + try { + const hashedPassword = await bcryptjs.hash(req.body.password, 10); + await pool.query("INSERT INTO users (username, password) VALUES ($1, $2)", [ + req.body.username, + hashedPassword, + ]); + res.redirect("/"); + } catch (err) { + next(err); + } +}); + +app.post( + "/log-in", + passport.authenticate("local", { + successRedirect: "/", + failureRedirect: "/", + }), +); + +app.get("/log-out", (req, res, next) => { + req.logout((err) => { + if (err) { + return next(err); + } + res.redirect("/"); + }); +}); + +app.get("/login/federated/google", passport.authenticate("google")); +app.get( + "/oauth2/redirect/google", + passport.authenticate("google", { + successRedirect: "/", + failureRedirect: "/login", + }), +); diff --git a/auth-expressjs/src/app.js.bak b/auth-expressjs/src/app.js.bak new file mode 100644 index 0000000..87c8228 --- /dev/null +++ b/auth-expressjs/src/app.js.bak @@ -0,0 +1,104 @@ +const path = require("node:path"); +const { Pool } = require("pg"); +const express = require("express"); +const session = require("express-session"); +const passport = require("passport"); +const LocalStrategy = require("passport-local"); +const fs = require("node:fs"); +const bcryptjs = require("bcryptjs"); + +const pool = new Pool({ + connectionString: "postgres://deez:postgres@smig-ca01:31450/deez", +}); + +const initSQL = path.join(__dirname, "../migrations/init.sql"); + +fs.readFile(initSQL, "utf8", (err, data) => { + if (err) { + console.error(err); + return; + } + pool.query(data); +}); + +const app = express(); +app.set("views", path.join(__dirname, "views")); +app.set("view engine", "ejs"); + +app.use(session({ secret: "cats", resave: false, saveUninitialized: false })); +app.use(passport.session()); +app.use(express.urlencoded({ extended: false })); + +app.get("/", (req, res) => res.render("index", { user: req.user })); + +app.get("/sign-up", (req, res) => res.render("sign-up-form")); +app.post("/sign-up", async (req, res, next) => { + try { + const hashedPassword = await bcryptjs.hash(req.body.password, 10); + await pool.query("INSERT INTO users (username, password) VALUES ($1, $2)", [ + req.body.username, + hashedPassword, + ]); + res.redirect("/"); + } catch (err) { + next(err); + } +}); + +app.listen(3000, () => console.log("app running on port 3000")); + +passport.use( + new LocalStrategy(async (username, password, done) => { + try { + const { rows } = await pool.query( + "SELECT * FROM users WHERE username = $1", + [username], + ); + const user = rows[0]; + const match = await bcryptjs.compare(password, user.password); + + if (!user) { + return done(null, false, { message: "Incorrect username/password." }); + } + + if (!match) { + return done(null, false, { message: "incorrect username/password" }); + } + + return done(null, user); + } catch (err) { + return done(err); + } + }), +); + +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +passport.deserializeUser(async (id, done) => { + try { + const { rows } = await pool.query("SELECT * FROM users WHERE id= $1", [id]); + const user = rows[0]; + done(null, user); + } catch (e) { + done(e); + } +}); + +app.post( + "/log-in", + passport.authenticate("local", { + successRedirect: "/", + failureRedirect: "/", + }), +); + +app.get("/log-out", (req, res, next) => { + req.logout((err) => { + if (err) { + return next(err); + } + res.redirect("/"); + }); +}); diff --git a/auth-expressjs/src/views/index.ejs b/auth-expressjs/src/views/index.ejs new file mode 100644 index 0000000..5899e2f --- /dev/null +++ b/auth-expressjs/src/views/index.ejs @@ -0,0 +1,29 @@ + + +
+ +Views: <%= sessions %>
+ +<% } else { %> +you should log in man
please log in + + +<% } %> + + Sign in with Google + + + diff --git a/auth-expressjs/src/views/log-in.ejs b/auth-expressjs/src/views/log-in.ejs new file mode 100644 index 0000000..fbaa668 --- /dev/null +++ b/auth-expressjs/src/views/log-in.ejs @@ -0,0 +1,9 @@ +