06_Login
Code-Dateien
| Dateiname | Aktion |
|---|---|
| CODECode_bank.zip | Download |
| CODECode_pizza.zip | Download |
| CODECode_student.zip | Download |
Videos
| Dateiname | Aktion |
|---|---|
| VIDEOVideo_Bank_D | Abspielen |
| VIDEOVideo_Pizza_E | Abspielen |
Lernmaterialien
Stateless
In the context of a website or web application, stateless means:
The server does not remember anything about the client between two HTTP requests.
Each request is handled independently, as if it were the first one.
What does this mean in practice?
Stateless
Client sends a request → server responds
Next request → server has no stored memory of the previous one
If the server needs to know who you are, the client must send the information with every request
- e.g. an authentication token (JWT) or a cookie with each request
Stateful
The server stores state (for example, a session) about the user
The client sends a session ID on each request
The server looks up the session to know who the user is
Harder to scale, because session data must be shared or centralized
Common examples
REST APIs are typically stateless
Web apps using server-side sessions are stateful
Simple definition (one sentence)
Stateless means the server does not keep user state between requests; every request must contain all required information.
Session
A session is a way to keep track of a user across multiple HTTP requests.
It allows the server to remember state (for example “logged in”) while the user navigates through a website.
How a session typically works
1) Login
The user sends a username and password to the server.
2) Server creates a session
The server generates a session ID (for example
abc123) and stores session data such as:
userId
loggedIn = true
user role
This data is stored on the server (memory, Redis, or a database).
3) Browser receives a cookie
The server sends a cookie to the browser:
Set-Cookie: sessionId=abc123; HttpOnly; Secure
4) Subsequent requests
For every later request, the browser automatically sends:
Cookie: sessionId=abc123
The server uses the session ID to look up the stored session data and recognize the user.
Session vs JWT (quick comparison)
| Session | JWT |
|---|---|
| Server stores session data | Server stores no session data |
| Stateful | Stateless |
| Cookie usually only contains session ID | Token contains user info |
| Easy to revoke | Harder to revoke |
Why sessions are useful
Users stay logged in across pages
Server can keep user-specific state (e.g. shopping cart, permissions)
One-sentence definition
A session is a server-side mechanism that keeps user state across multiple HTTP requests using a session ID, usually stored in a cookie.
Cookies
Cookies are small pieces of data that a website stores in your browser and sends back to the server on later requests.
A cookie helps a website “remember” something about you (for example: that you’re logged in, your language choice, or items in a cart).
How cookies work (simple flow)
- Server sends a cookie to your browser in the HTTP response header:
Set-Cookie: sessionId=abc123; HttpOnly; Secure
Browser stores it.
On the next request to the same website, the browser automatically includes it:
Cookie: sessionId=abc123
That’s how the server can recognize you across requests.
What cookies are used for
Login sessions (keep you logged in)
Preferences (language, theme)
Analytics (tracking usage)
Shopping carts
Types of cookies
1) Session cookies
Temporary
Deleted when you close the browser
Common for login sessions
2) Persistent cookies
Stored until an expiration date (e.g., days/months)
Used for “Remember me” and preferences
3) First-party vs third-party
First-party: set by the site you visit
Third-party: set by another domain (often ads/trackers), increasingly blocked by browsers
One-sentence definition
Cookies are browser-stored key/value data that are automatically sent to the same website with future requests to maintain state (like sessions).
Middleware
In web development (especially with Express.js), middleware is a function that runs between the incoming request and the final response.
Think of the request handling like a pipeline:
Request → middleware(s) → route handler → Response
What middleware can do
A middleware function can:
read/modify the request (
req) (e.g., parse JSON, check auth)read/modify the response (
res)end the request by sending a response
or pass control to the next middleware using
next()
Express middleware signature
function myMiddleware(req, res, next) {
// do something
next(); // pass control to the next middleware/route
}Implementation login
Accounts
accounts.json
[
{
"username": "thomas",
"password": "thomas"
},
{
"username": "susi",
"password": "susi"
},
{
"username": "test",
"password": "test"
},
{
"username": "admin",
"password": "admin"
},
{
"username": "demo",
"password": "demo"
},
{
"username": "guest",
"password": "guest"
}
]server.ts
import accounts from "./accounts.json" with { type: "json" };
import session from "express-session";shell
deno add npm:express-session
Login
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="style_login.css">
</head>
<body>
<header>
<h1>Students - Login</h1>
</header>
<main>
<div class="login-container">
<h2>Login</h2>
<form action="/login" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required />
<label for="password">Password:</label>
<input type="password" id="password" name="password" required />
<div class="button-group">
<button type="submit">Sign In</button>
<button type="reset">Clear</button>
</div>
</form>
<div class="back-link">
<a href="/index.html">← Back to Students</a>
</div>
</div>
</main>
</body>
</html>style_login.css
.login-container {
max-width: 350px;
margin: 50px auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
h2 {
text-align: center;
}
label {
display: block;
margin-top: 10px;
}
input, button {
width: 100%;
padding: 10px;
margin: 5px 0;
border: 1px solid #ccc;
border-radius: 8px;
box-sizing: border-box;
}
.button-group {
display: flex;
gap: 8px;
margin-top: 10px;
}
button {
border: none;
cursor: pointer;
background: #ddd;
}
button[type="submit"] {
background: #0078ff;
color: white;
}
.back-link {
text-align: center;
margin-top: 15px;
}Cookie
server.ts
import session from "express-session";
app.use(express.urlencoded({ extended: true }));
app.use(
session({
secret: process.env.SESSION_SECRET ||
"your-secret-key-change-in-production",
resave: false,
saveUninitialized: false,
name: "cookie_gt",
cookie: {
maxAge: 5 * 60 * 1000, // 5 minutes
secure: process.env.NODE_ENV === "production",
httpOnly: true,
},
}),
);import session from "express-session";
Lädt das Middleware-Paket express-session. Damit
kann Express pro Nutzer eine Session verwalten (z. B.
req.session.userId).
app.use(express.urlencoded({ extended: true }));
Middleware, die Formulardaten aus dem Request-Body liest, z. B. aus HTML-Forms:
Content-Type:
application/x-www-form-urlencodedErgebnis: Du kannst dann
req.body.name,req.body.passwordusw. verwenden.
extended: true erlaubt “reichere” Strukturen
(verschachtelte Felder) im Body.
app.use(session({...}))
Das ist die Session-Middleware. Sie macht:
Setzt/liest ein Cookie (hier heißt es
cookie_gt)Verknüpft dieses Cookie mit einer Session
Hängt die Session an
req.session
Wichtige Optionen:
secret: ...
Schlüssel zum Signieren des Session-Cookies (damit es nicht manipuliert werden kann).
- In Produktion unbedingt per ENV setzen (
SESSION_SECRET) und stark/zufällig wählen.
resave: false
Speichert die Session nicht bei jedem Request neu, wenn sich nichts geändert hat. (Gut für Performance.)
saveUninitialized: false
Speichert keine leeren/neuen Sessions, solange du
nichts in req.session ablegst.
- Gut für Datenschutz (kein Cookie für Besucher, die nie “Session-Daten” bekommen).
name: "cookie_gt"
Name des Cookies, das im Browser gesetzt wird.
- Standard wäre
connect.sid, du gibst hier einen eigenen Namen.
cookie: { ... }
Cookie-Eigenschaften:
maxAge: 5 * 60 * 1000
Session-Cookie läuft nach 5 Minuten ab (inaktiv/abgelaufen, je nach Store/Browser).secure: process.env.NODE_ENV === "production"
Cookie wird nur über HTTPS gesendet (in Produktion).httpOnly: true
Cookie ist nicht per JavaScript im Browser lesbar (document.cookie), schützt gegen XSS-Cookie-Diebstahl.
Middleware
auth.ts
import { NextFunction, Request, Response } from "express";
export function authMiddleware(
req: Request,
res: Response,
next: NextFunction,
) {
if (req.session && req.session.user) {
return next();
}
// For APIs, return 401. For MVC/Handlebars, use res.redirect('/login')
res.status(401).json({ error: "Unauthorized" });
}Der Code definiert eine Express-Middleware, die prüft, ob der Benutzer eingeloggt/autorisiert ist, bevor er eine Route benutzen darf.
Import
import { NextFunction, Request, Response } from "express";Das sind TypeScript-Typen für Express:
Request: das Request-Objekt (req)Response: das Response-Objekt (res)NextFunction: die Funktionnext(), mit der man zur nächsten Middleware/Route weitergeht
Middleware-Funktion
export function authMiddleware(req, res, next) {export heißt: Du kannst sie in anderen Dateien
importieren, z. B.:
import { authMiddleware } from "./authMiddleware";Auth-Check (Session-basiert)
if (req.session && req.session.user) {
return next();
}Der Code erwartet, dass du express-session verwendest.
Wenn
req.session.userexistiert, gilt der User als eingeloggt.next()bedeutet: “OK, weiter zur eigentlichen Route”.
Wenn nicht eingeloggt
res.status(401).json({ error: "Unauthorized" });Antwortet mit HTTP 401 Unauthorized
und einem JSON-Fehler.
Der Kommentar sagt: In einer klassischen Website (MVC/Handlebars) würdest du statt JSON oft umleiten:
res.redirect("/login");Kurz: Die Middleware lässt Requests nur durch, wenn in der
Session ein user gespeichert ist.
server.ts
import { authMiddleware } from "./auth.ts";routes
/login
server.ts
app.post("/login", (req: Request, res: Response) => {
const { username, password } = req.body;
const account = accounts.find(
(acc) => acc.username === username && acc.password === assword,
);
if (account) {
req.session.user = { username: account.username };
return res.json({ message: "Login successful" });
}
res.status(401).json({ error: "Invalid credentials" });
});Anmerkungen:
Liest
usernameundpasswordaus dem Request-Body (z. B. aus einem Formular oder JSON).Sucht in
accounts(ein Array mit Benutzerkonten) nach einem passenden Eintrag.Wenn gefunden:
Speichert in der Session:
req.session.user = { username: ... }Damit ist der Benutzer “eingeloggt” (bei späteren Requests ist
req.session.uservorhanden).Antwortet mit
{ message: "Login successful" }
Wenn nicht gefunden:
- Antwortet mit 401 und
{ error: "Invalid credentials" }
- Antwortet mit 401 und
Wichtig: Das ist eine Lern-/Demo-Variante. In echt speichert man Passwörter nicht im Klartext, sondern gehasht.
/loginstatus
server.ts
app.get("/loginstatus", (req: Request, res: Response) => {
if (req.session.user) {
return res.json({ loggedIn: true, user: req.session.user });
}
res.json({ loggedIn: false });
});Anmerkungen:
Prüft, ob
req.session.userexistiert.Wenn ja: Benutzer ist eingeloggt → gibt
{ loggedIn: true, user: ... }zurück.Wenn nein:
{ loggedIn: false }.
Das ist praktisch für Frontend: “Bin ich noch eingeloggt?”
/safe
server.ts
app.get("/safe", authMiddleware, (req: Request, res: Response) => {
res.json({ message: "This is a safe route", user: req.session.user });
});Anmerkungen:
Diese Route ist durch
authMiddlewaregeschützt.Ablauf:
authMiddlewareprüft, obreq.session.usergesetzt ist.Wenn nicht: 401 “Unauthorized”
Wenn ja: Route läuft weiter und gibt die “safe” Antwort zurück.
index.html
</div>
<p>
<a href="/login.html">login</a> |
<a href="/loginstatus">loginstatus</a> |
<a href="/safe">safe route</a>
</p>
</main>routes
server.ts
app.delete("/students/:id", authMiddleware, async (req: Request, res: Response) => {
const id = parseInt(req.params.id);In dieser Zeile hat sich gegenüber deiner früheren Version
genau eine Sache geändert:
authMiddleware wurde eingefügt
app.delete("/students/:id",authMiddleware, ...
Das bedeutet: Bevor der DELETE-Handler ausgeführt
wird, läuft authMiddleware.
Wenn
req.session.userexistiert →next()→ der Delete läuft.Wenn nicht → sofort 401 Unauthorized und der Delete wird nicht ausgeführt.
!!! Die Middleware kann nun bei beliebigen routen eingefügt werden !!!
app.get("/students", ...); // public
app.get("/students/:id", ...); // public
app.post("/students", authMiddleware, ...);
app.put("/students/:id", authMiddleware, ...);
app.patch("/students/:id", authMiddleware, ...);
app.delete("/students/:id", authMiddleware, ...);