mirror of
https://github.com/notrab/dumbo.git
synced 2025-01-29 03:37:38 +01:00
feat(examples): add cookie-jwt-auth
This commit is contained in:
parent
50e23ce53f
commit
6d39995257
26
examples/cookie-jwt-auth/README.md
Normal file
26
examples/cookie-jwt-auth/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Cookie JWT Auth Example
|
||||
|
||||
This example showcases how to uses a JWT to store information about the logged in user.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
```bash
|
||||
composer install
|
||||
```
|
||||
|
||||
2. Start the server:
|
||||
|
||||
```bash
|
||||
composer start
|
||||
```
|
||||
|
||||
3. Start the database server:
|
||||
|
||||
```bash
|
||||
brew install tursodatabase/tap/turso
|
||||
turso dev --port 8001
|
||||
```
|
||||
|
||||
4. Visit the URL [http://localhost:8000](http://localhost:8000) in your browser.
|
21
examples/cookie-jwt-auth/composer.json
Normal file
21
examples/cookie-jwt-auth/composer.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"require": {
|
||||
"notrab/dumbo": "@dev",
|
||||
"latte/latte": "^3.0",
|
||||
"darkterminal/turso-client-http": "^2.9",
|
||||
"firebase/php-jwt": "^6.10"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "path",
|
||||
"url": "../../"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"start": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"php -S localhost:8000 -t ."
|
||||
]
|
||||
},
|
||||
"prefer-stable": false
|
||||
}
|
177
examples/cookie-jwt-auth/index.php
Normal file
177
examples/cookie-jwt-auth/index.php
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . "/vendor/autoload.php";
|
||||
|
||||
use Dumbo\Dumbo;
|
||||
use Dumbo\Helpers\Cookie;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Latte\Engine as LatteEngine;
|
||||
use Darkterminal\TursoHttp\LibSQL;
|
||||
|
||||
$app = new Dumbo();
|
||||
$latte = new LatteEngine();
|
||||
|
||||
$dsn = "http://127.0.0.1:8001";
|
||||
$db = new LibSQL($dsn);
|
||||
|
||||
$latte->setAutoRefresh(true);
|
||||
$latte->setTempDirectory(null);
|
||||
|
||||
const JWT_SECRET = "your_jwt_secret_key";
|
||||
const JWT_EXPIRATION = 3600; // 1 hour
|
||||
const COOKIE_NAME = "jwt_session";
|
||||
|
||||
$db->execute("
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL
|
||||
)
|
||||
");
|
||||
|
||||
function render($latte, $view, $params = [])
|
||||
{
|
||||
return $latte->renderToString(__DIR__ . "/views/$view.latte", $params);
|
||||
}
|
||||
|
||||
function createJWT($userId, $username)
|
||||
{
|
||||
$issuedAt = time();
|
||||
$expirationTime = $issuedAt + JWT_EXPIRATION;
|
||||
|
||||
$payload = [
|
||||
"iat" => $issuedAt,
|
||||
"exp" => $expirationTime,
|
||||
"userId" => $userId,
|
||||
"username" => $username,
|
||||
];
|
||||
|
||||
return JWT::encode($payload, JWT_SECRET, "HS256");
|
||||
}
|
||||
|
||||
function verifyJWT($jwt)
|
||||
{
|
||||
try {
|
||||
$decoded = JWT::decode($jwt, new Key(JWT_SECRET, "HS256"));
|
||||
return (array) $decoded;
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$app->use(function ($c, $next) {
|
||||
$jwt = Cookie::getCookie($c, COOKIE_NAME);
|
||||
if ($jwt) {
|
||||
$payload = verifyJWT($jwt);
|
||||
if ($payload) {
|
||||
$c->set("user", $payload);
|
||||
|
||||
// Refresh JWT if it's close to expiration
|
||||
if (time() > $payload["exp"] - 300) {
|
||||
// Refresh if less than 5 minutes left
|
||||
$newJwt = createJWT($payload["userId"], $payload["username"]);
|
||||
Cookie::setCookie($c, COOKIE_NAME, $newJwt, [
|
||||
"httpOnly" => true,
|
||||
"secure" => true,
|
||||
"path" => "/",
|
||||
"maxAge" => JWT_EXPIRATION,
|
||||
"sameSite" => Cookie::SAME_SITE_LAX,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
Cookie::deleteCookie($c, COOKIE_NAME);
|
||||
}
|
||||
}
|
||||
return $next($c);
|
||||
});
|
||||
|
||||
$app->get("/", function ($c) use ($latte) {
|
||||
$user = $c->get("user");
|
||||
$html = render($latte, "home", [
|
||||
"user" => $user,
|
||||
]);
|
||||
return $c->html($html);
|
||||
});
|
||||
|
||||
$app->get("/register", function ($c) use ($latte) {
|
||||
$html = render($latte, "register");
|
||||
return $c->html($html);
|
||||
});
|
||||
|
||||
$app->post("/register", function ($c) use ($db, $latte) {
|
||||
$body = $c->req->body();
|
||||
$username = $body["username"] ?? "";
|
||||
$password = $body["password"] ?? "";
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
$html = render($latte, "register", [
|
||||
"error" => "Username and password are required",
|
||||
]);
|
||||
return $c->html($html);
|
||||
}
|
||||
|
||||
try {
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
$db->prepare(
|
||||
"INSERT INTO users (username, password) VALUES (?, ?)"
|
||||
)->execute([$username, $hashedPassword]);
|
||||
|
||||
return $c->redirect("/login");
|
||||
} catch (Exception $e) {
|
||||
$html = render($latte, "register", [
|
||||
"error" => "Username already exists",
|
||||
]);
|
||||
return $c->html($html);
|
||||
}
|
||||
});
|
||||
|
||||
$app->get("/login", function ($c) use ($latte) {
|
||||
$html = render($latte, "login");
|
||||
return $c->html($html);
|
||||
});
|
||||
|
||||
$app->post("/login", function ($c) use ($db, $latte) {
|
||||
$body = $c->req->body();
|
||||
$username = $body["username"] ?? "";
|
||||
$password = $body["password"] ?? "";
|
||||
|
||||
$result = $db
|
||||
->query("SELECT * FROM users WHERE username = ?", [$username])
|
||||
->fetchArray(LibSQL::LIBSQL_ASSOC);
|
||||
|
||||
if (!empty($result) && password_verify($password, $result[0]["password"])) {
|
||||
$jwt = createJWT($result[0]["id"], $result[0]["username"]);
|
||||
Cookie::setCookie($c, COOKIE_NAME, $jwt, [
|
||||
"httpOnly" => true,
|
||||
"secure" => true,
|
||||
"path" => "/",
|
||||
"maxAge" => JWT_EXPIRATION,
|
||||
"sameSite" => Cookie::SAME_SITE_LAX,
|
||||
]);
|
||||
return $c->redirect("/");
|
||||
} else {
|
||||
$html = render($latte, "login", [
|
||||
"error" => "Invalid username or password",
|
||||
]);
|
||||
return $c->html($html);
|
||||
}
|
||||
});
|
||||
|
||||
$app->get("/logout", function ($c) {
|
||||
Cookie::deleteCookie($c, COOKIE_NAME);
|
||||
return $c->redirect("/");
|
||||
});
|
||||
|
||||
$app->get("/protected", function ($c) use ($latte) {
|
||||
$user = $c->get("user");
|
||||
if (!$user) {
|
||||
return $c->redirect("/login");
|
||||
}
|
||||
$html = render($latte, "protected", [
|
||||
"user" => $user,
|
||||
]);
|
||||
return $c->html($html);
|
||||
});
|
||||
|
||||
$app->run();
|
13
examples/cookie-jwt-auth/views/home.latte
Normal file
13
examples/cookie-jwt-auth/views/home.latte
Normal file
@ -0,0 +1,13 @@
|
||||
{layout 'layout.latte'}
|
||||
|
||||
{block content}
|
||||
<h1>Welcome to JWT Auth Example</h1>
|
||||
{if $user}
|
||||
<p>Hello, {$user['username']}!</p>
|
||||
<a href="/protected">Protected Page</a> |
|
||||
<a href="/logout">Logout</a>
|
||||
{else}
|
||||
<p>You are not logged in.</p>
|
||||
<a href="/login">Login</a> | <a href="/register">Register</a>
|
||||
{/if}
|
||||
{/block}
|
14
examples/cookie-jwt-auth/views/layout.latte
Normal file
14
examples/cookie-jwt-auth/views/layout.latte
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Cookie Auth</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
{if isset($flash_message)}
|
||||
<p style="color: green;">{$flash_message}</p>
|
||||
{/if}
|
||||
{include content}
|
||||
</body>
|
||||
</html>
|
21
examples/cookie-jwt-auth/views/login.latte
Normal file
21
examples/cookie-jwt-auth/views/login.latte
Normal file
@ -0,0 +1,21 @@
|
||||
{layout 'layout.latte'}
|
||||
|
||||
{block content}
|
||||
<h1>Login</h1>
|
||||
{if isset($error)}
|
||||
<p style="color: red;">{$error}</p>
|
||||
{/if}
|
||||
<form action="/login" method="post">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<p>Don't have an account? <a href="/register">Register</a></p>
|
||||
<p><a href="/">Back to Home</a></p>
|
||||
{/block}
|
8
examples/cookie-jwt-auth/views/protected.latte
Normal file
8
examples/cookie-jwt-auth/views/protected.latte
Normal file
@ -0,0 +1,8 @@
|
||||
{layout 'layout.latte'}
|
||||
|
||||
{block content}
|
||||
<h1>Protected Page</h1>
|
||||
<p>Welcome, {$user['username']}! This is a protected page.</p>
|
||||
<p>Your user ID is: {$user['userId']}</p>
|
||||
<p><a href="/">Back to Home</a></p>
|
||||
{/block}
|
21
examples/cookie-jwt-auth/views/register.latte
Normal file
21
examples/cookie-jwt-auth/views/register.latte
Normal file
@ -0,0 +1,21 @@
|
||||
{layout 'layout.latte'}
|
||||
|
||||
{block content}
|
||||
<h1>Register</h1>
|
||||
{if isset($error)}
|
||||
<p style="color: red;">{$error}</p>
|
||||
{/if}
|
||||
<form action="/register" method="post">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
<p>Already have an account? <a href="/login">Login</a></p>
|
||||
<p><a href="/">Back to Home</a></p>
|
||||
{/block}
|
Loading…
x
Reference in New Issue
Block a user