Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions courses/backend/node/module-materials/examples/auth-jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import express from "express";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";

const JWT_SECRET = process.env.JWT_SECRET || "development-secret";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
// bcrypt.hashSync("password123", 10)
password_hash:
"$2b$10$uXQ26BC378vlfQz80XTlKecUnhlcWFzZdoygngzx5CQhPkZJRZDtO",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

const app = express();
app.use(express.json());

app.post("/login", async (req, res) => {
const { username, password } = req.body;

const user = getUserByUsername(username);
if (!user) {
return res.status(404).json({ error: "User not found" });
}

const isMatch = await bcrypt.compare(password, user.password_hash);
if (!isMatch) {
return res.status(401).json({ error: "Invalid credentials" });
}

const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: "1h" });
res.json({ token });
});

function requireJwtAuth(req, res, next) {
const authHeader = req.headers.authorization;
const token = authHeader?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "No token provided" });
}

try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = { id: decoded.userId };
next();
} catch (err) {
return res.status(401).json({ error: "Invalid or expired token" });
}
}

app.get("/protected", requireJwtAuth, (req, res) => {
res.json({ data: "Top secret snippets", userId: req.user.id });
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (JWT auth example)");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import express from "express";
import bcrypt from "bcrypt";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
// bcrypt.hashSync("password123", 10)
password_hash:
"$2b$10$uXQ26BC378vlfQz80XTlKecUnhlcWFzZdoygngzx5CQhPkZJRZDtO",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

const app = express();
app.use(express.json());

app.post("/login", async (req, res) => {
const { username, password } = req.body;

const user = getUserByUsername(username);
if (!user) {
return res.status(404).json({ error: "User not found" });
}

const isMatch = await bcrypt.compare(password, user.password_hash);
if (!isMatch) {
return res.status(401).json({ error: "Invalid credentials" });
}

res.json({ message: "Login successful", userId: user.id });
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (bcrypt login example)");
});
59 changes: 59 additions & 0 deletions courses/backend/node/module-materials/examples/auth-sessions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import express from "express";
import session from "express-session";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
password: "password123",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

const app = express();
app.use(express.json());

app.use(
session({
secret: process.env.SESSION_SECRET || "development-session-secret",
resave: false,
saveUninitialized: false,
}),
);

app.post("/login", (req, res) => {
const { username, password } = req.body;

const user = getUserByUsername(username);
if (!user || user.password !== password) {
return res.status(401).json({ error: "Invalid credentials" });
}

req.session.userId = user.id;
res.json({ message: "Logged in with session" });
});

function requireSessionAuth(req, res, next) {
if (req.session.userId) {
return next();
}
res.status(401).json({ error: "Not authenticated" });
}

app.get("/protected", requireSessionAuth, (req, res) => {
res.json({ data: "Session-protected snippets", userId: req.session.userId });
});

app.post("/logout", (req, res) => {
req.session.destroy(() => {
res.json({ message: "Logged out" });
});
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (session auth example)");
});
142 changes: 142 additions & 0 deletions courses/backend/node/module-materials/node.postman_collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"info": {
"_postman_id": "689497c5-04e2-4c87-8883-2a3d7bc4b1c9",
"name": "node",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "48182721",
"_collection_link": "https://magdazelena-ea3683e1-7432080.postman.co/workspace/Magdalena-Odrow%C4%85%C5%BC-%C5%BBelezik's-Wor~d3429c08-9437-4576-84c6-94f688cd37c5/collection/48182721-689497c5-04e2-4c87-8883-2a3d7bc4b1c9?action=share&source=collection_link&creator=48182721"
},
"item": [
{
"name": "login",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"username\": \"alice\",\r\n \"password\": \"password123\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseURL}}/login",
"host": ["{{baseURL}}"],
"path": ["login"]
}
},
"response": []
},
{
"name": "protected-jwt",
"request": {
"auth": {
"type": "jwt",
"jwt": [
{
"key": "secret",
"value": "development-secret",
"type": "string"
},
{
"key": "isSecretBase64Encoded",
"value": false,
"type": "boolean"
},
{
"key": "addTokenTo",
"value": "header",
"type": "string"
},
{
"key": "algorithm",
"value": "HS256",
"type": "string"
},
{
"key": "payload",
"value": "{}",
"type": "string"
},
{
"key": "headerPrefix",
"value": "Bearer",
"type": "string"
},
{
"key": "queryParamKey",
"value": "token",
"type": "string"
},
{
"key": "header",
"value": "{}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{baseURL}}/protected",
"host": ["{{baseURL}}"],
"path": ["protected"]
}
},
"response": []
},
{
"name": "protected-session",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseURL}}/protected",
"host": ["{{baseURL}}"],
"path": ["protected"]
}
},
"response": []
},
{
"name": "logout",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "{{baseURL}}/logout",
"host": ["{{baseURL}}"],
"path": ["logout"]
}
},
"response": []
}
],
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"packages": {},
"exec": [""]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"packages": {},
"exec": [""]
}
}
],
"variable": [
{
"key": "baseURL",
"value": "",
"type": "default"
}
]
}
Loading