history: rokojori-auth built and deployed, Roject integration planned

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rokojori 2026-07-13 13:54:11 +02:00
parent 714996fcae
commit 605afa3e66
4 changed files with 220 additions and 8 deletions

View File

@ -38,6 +38,7 @@ var NAV_DATA = {
title: 'History', title: 'History',
path: 'history/index.html', path: 'history/index.html',
children: [ children: [
{ title: 'Sunday, 13 July 2026', path: 'history/2026/07-July/13-Sunday/index.html' },
{ title: 'Sunday, 12 July 2026', path: 'history/2026/07-July/12-Sunday/index.html' }, { title: 'Sunday, 12 July 2026', path: 'history/2026/07-July/12-Sunday/index.html' },
{ title: 'Saturday, 11 July 2026', path: 'history/2026/07-July/11-Saturday/index.html' }, { title: 'Saturday, 11 July 2026', path: 'history/2026/07-July/11-Saturday/index.html' },
{ title: 'Friday, 10 July 2026', path: 'history/2026/07-July/10-Friday/index.html' }, { title: 'Friday, 10 July 2026', path: 'history/2026/07-July/10-Friday/index.html' },

View File

@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sunday, 13 July 2026 — Roject</title>
<link rel="stylesheet" href="../../../../_assets_/styles.css">
<link rel="stylesheet" href="../../../../_assets_/nav.css">
</head>
<body>
<div class="page">
<header>
<p class="date">Sunday, 13 July 2026</p>
<h1>rokojori-auth — built and deployed</h1>
<p class="subtitle">
Full centralized auth service created from scratch, deployed to
<code>account.rokojori.com</code>, and documented.
</p>
</header>
<section>
<h2>What we built</h2>
<div class="card">
<h3>rokojori-auth — new standalone service</h3>
<p>
A complete auth service at <code>c:\rokojori\projects\web-projects\rokojori-auth</code>,
same stack as Roject (Node.js, Express, ts-node, JSON file storage). Built entirely
from scratch this session. Deployed to <code>account.rokojori.com</code> via nginx +
systemd + Let's Encrypt on Server A, port 3001.
</p>
<div class="tags">
<span class="tag">account.rokojori.com</span>
<span class="tag">nginx + systemd</span>
<span class="tag">port 3001</span>
</div>
</div>
<div class="card">
<h3>Auth endpoints</h3>
<p>
Register, login, logout, refresh (body), refresh-session (cookie + redirect),
forgot-password, reset-password, GET /me, PATCH /me/settings, POST /me/password,
DELETE /me. Access token is a 1-hour HS256 JWT containing userId, email, roles,
products, settings. Refresh token is a 30-day UUID stored server-side.
Both are set as <code>HttpOnly</code> cookies on <code>.rokojori.com</code>
and also returned in the response body for non-browser clients.
</p>
<div class="tags">
<span class="tag">jsonwebtoken</span>
<span class="tag">bcryptjs</span>
<span class="tag">cookie-parser</span>
<span class="tag">HttpOnly cookie</span>
<span class="tag">.rokojori.com domain</span>
</div>
</div>
<div class="card">
<h3>Roles: user, admin, superadmin</h3>
<p>
Three built-in roles. <code>superadmin</code> is bootstrapped via
<code>INITIAL_SUPERADMIN_EMAIL</code> in <code>.env</code> — fires once on
first registration if no superadmin exists. Superadmin can grant/revoke admin
on other users; cannot change their own roles. Admin and superadmin get an
enhanced profile page with a full user list. Role management controls are
superadmin-only.
</p>
</div>
<div class="card">
<h3>Rate limiting</h3>
<p>
In-memory per-IP rate limiters on all sensitive endpoints. Login: 10 attempts /
15 min, 3 s delay after attempt 5. Register: 5 / hour. Forgot-password: 20 / 20 min
with escalating delay (5 s → 15 s → 30 s). <code>trust proxy</code> enabled so
real client IPs are seen behind nginx.
</p>
</div>
<div class="card">
<h3>HTML pages</h3>
<p>
Five self-contained pages: login, register, profile, forgot-password, reset-password.
Plain HTML with inline CSS and JS. Login and register accept a <code>?redirect=</code>
query param. Reset-password fetches the email for the token via
<code>GET /api/auth/reset-token-email</code> and populates a hidden email field
so browsers offer to update the saved password. Profile page includes
change-password (with current password verification) and a two-step delete account
confirmation. Welcome email sent on registration (non-blocking).
</p>
</div>
<div class="card">
<h3>Admin routes</h3>
<p>
<code>GET /api/admin/users</code> (admin + superadmin) lists all users without
password hashes. <code>PATCH /api/admin/users/:id/roles</code> (superadmin only)
sets roles on another user. Guards: cannot change own roles; <code>user</code>
base role is always preserved.
</p>
</div>
<div class="card">
<h3>Workspace documentation</h3>
<p>
Created <code>workspace/</code> in the rokojori-auth repo with the same asset
system as the Roject workspace (styles, nav, breadcrumb). A single
<code>workspace/index.html</code> covers features, technical implementation,
data model, JWT payload, deployment, planned work, and a reference to the
Roject workspace for guides and actions.
</p>
</div>
</section>
<section>
<h2>Key decisions</h2>
<div class="decision">
<strong>Refresh token also set as HttpOnly cookie</strong>
<p>
Originally the refresh token was only returned in the response body. Added a
refresh token cookie so browser clients can silently refresh via
<code>GET /api/auth/refresh-session?redirect=...</code> without re-login.
Non-browser clients continue to use the body token and
<code>POST /api/auth/refresh</code>.
</p>
</div>
<div class="decision">
<strong>Superadmin cannot change their own roles</strong>
<p>
The PATCH /api/admin/users/:id/roles endpoint blocks changes to the calling
user's own record to prevent accidental self-demotion or lockout.
</p>
</div>
<div class="decision">
<strong>Welcome email is non-blocking</strong>
<p>
SMTP failures on the welcome email are caught and silently ignored so that a
misconfigured mail server cannot break user registration.
</p>
</div>
<div class="decision">
<strong>Forgot-password always returns OK</strong>
<p>
The response is always <code>{ ok: true }</code> regardless of whether the email
exists, preventing account enumeration via timing or response differences.
</p>
</div>
</section>
<section>
<h2>What's next</h2>
<div class="card">
<p>
Integrate Roject with rokojori-auth: remove the local user system from Roject,
add JWT verification middleware, redirect login/logout to
<code>account.rokojori.com</code>, and update all data references from local
user records to <code>userId</code> from the JWT payload.
</p>
</div>
</section>
<footer>
Roject &mdash; session history
</footer>
</div>
<script>var NAV_ROOT = '../../../../';</script>
<script src="../../../../_assets_/nav-data.js"></script>
<script src="../../../../_assets_/nav.js"></script>
</body>
</html>

View File

@ -19,6 +19,11 @@
<section> <section>
<h2>2026 — July</h2> <h2>2026 — July</h2>
<div class="card">
<h3><a href="2026/07-July/13-Sunday/index.html">Sunday, 13 July 2026</a></h3>
<p>rokojori-auth built and deployed — standalone auth service at account.rokojori.com with JWT tokens, refresh cookies, roles, rate limiting, admin panel, and workspace documentation.</p>
</div>
<div class="card"> <div class="card">
<h3><a href="2026/07-July/12-Sunday/index.html">Sunday, 12 July 2026</a></h3> <h3><a href="2026/07-July/12-Sunday/index.html">Sunday, 12 July 2026</a></h3>
<p>Full directory restructure and first production deployment — source/, build/, source/pages/; Roject live at roject.rokojori.com via nginx + systemd. EmailService SMTP layer added.</p> <p>Full directory restructure and first production deployment — source/, build/, source/pages/; Roject live at roject.rokojori.com via nginx + systemd. EmailService SMTP layer added.</p>

View File

@ -231,17 +231,17 @@
</div> </div>
<div class="card"> <div class="card">
<h3>In progress — Centralized auth: rokojori-auth</h3> <h3>Done — Centralized auth: rokojori-auth</h3>
<p> <p>
The user system currently embedded in Roject is being extracted into a The standalone auth service <strong>rokojori-auth</strong> is built and live at
standalone auth service, <strong>rokojori-auth</strong>, deployed at <code>account.rokojori.com</code>. It handles registration, login, JWT issuance,
<code>account.rokojori.com</code>. It will handle registration, login, refresh token rotation, password reset, roles (user / admin / superadmin),
JWT issuance, password reset, and user data (roles, products, settings) products, global settings, rate limiting, and account deletion.
for all rokojori projects. Roject will become a JWT-validating client Roject has not yet been integrated — that is the next step.
with no user storage of its own.
</p> </p>
<p style="margin-top:0.75rem"> <p style="margin-top:0.75rem">
See <a href="auth-restructure.html">auth-restructure</a> for the full plan. See <a href="auth-restructure.html">auth-restructure</a> for the full plan and
the rokojori-auth workspace for implementation details.
</p> </p>
<div class="tags"> <div class="tags">
<span class="tag">rokojori-auth</span> <span class="tag">rokojori-auth</span>
@ -251,6 +251,35 @@
</div> </div>
</div> </div>
<div class="card">
<h3>Next — Roject integration with rokojori-auth</h3>
<p>
Roject needs to be updated to become a JWT-validating client. The work involves
removing the existing session-based user system and replacing it with JWT
verification middleware that reads the shared <code>.rokojori.com</code> cookie.
Login and register links will point to <code>account.rokojori.com</code>.
Expired tokens are refreshed via
<code>account.rokojori.com/api/auth/refresh-session?redirect=...</code>.
All data models that currently reference a local user record will switch to
referencing <code>userId</code> from the JWT payload.
</p>
<ul style="line-height:1.9;margin-top:0.75rem;font-size:0.9rem">
<li>Remove <code>source/server/routes/auth.ts</code> and <code>middleware/auth.ts</code></li>
<li>Remove user storage from <code>source/server/db.ts</code></li>
<li>Remove <code>bcryptjs</code>, <code>express-session</code></li>
<li>Remove <code>source/pages/login.html</code>, <code>register.html</code></li>
<li>Remove <code>source/server/email/</code> (already lives in rokojori-auth)</li>
<li>Add JWT verification middleware</li>
<li>Point login/logout nav links at <code>account.rokojori.com</code></li>
</ul>
<div class="tags">
<span class="tag">JWT middleware</span>
<span class="tag">remove express-session</span>
<span class="tag">remove bcryptjs</span>
<span class="tag">account.rokojori.com login redirect</span>
</div>
</div>
<div class="card"> <div class="card">
<h3>Smaller open improvements</h3> <h3>Smaller open improvements</h3>
<ul style="line-height:1.9"> <ul style="line-height:1.9">