auth: learning express auth

This commit is contained in:
Mike 2025-02-04 12:38:36 -05:00
parent 0647b4af89
commit 518abdc55e
6 changed files with 363 additions and 0 deletions

View file

@ -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)
);

189
auth-expressjs/src/app.js Normal file
View file

@ -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",
}),
);

View file

@ -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("/");
});
});

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<% if (locals.user) {%>
MY PERSON <%= user.username %>
<a href="/log-out">LOG OUT</a>
<p> Views: <%= sessions %></p>
<% } else { %>
<p> you should log in man </P
<h1>please log in</h1>
<form action="/log-in" method="POST">
<label for="username">Username</label>
<input id="username" name="username" placeholder="username" type="text" />
<label for="password">Password</label>
<input id="password" name="password" type="password" />
<button>Log In</button>
</form>
<% } %>
<a type="button" href="/login/federated/google">Sign in with Google</a>
</body>
</html>

View file

@ -0,0 +1,9 @@
<h1>please log in</h1>
<form action="/log-in" method="POST">
<label for="username">Username</label>
<input id="username" name="username" placeholder="username" type="text" />
<label for="password">Password</label>
<input id="password" name="password" type="password" />
<button>Log In</button>
</form>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Sign Up</h1>
<form action="" method="POST">
<label for="username">Username</label>
<input id="username" name="username" placeholder="username" type="text" />
<label for="password">Password</label>
<input id="password" name="password" type="password" />
<button>Sign Up</button>
</form>
</body>
</html>