513 lines
21 KiB
HTML
513 lines
21 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>Auth Connector Rewrite — Roject Outline</title>
|
|||
|
|
<link rel="stylesheet" href="../_assets_/styles.css">
|
|||
|
|
<link rel="stylesheet" href="../_assets_/nav.css">
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<div class="page">
|
|||
|
|
|
|||
|
|
<header>
|
|||
|
|
<p class="date">Plan</p>
|
|||
|
|
<h1>Auth Connector Rewrite</h1>
|
|||
|
|
<p class="subtitle">
|
|||
|
|
Per-repo change list for adopting <code>rokojori-auth-connector</code> as a git
|
|||
|
|
submodule. Read in full before touching any file. Changes are listed in dependency
|
|||
|
|
order within each repo — do them top to bottom.
|
|||
|
|
</p>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<!-- ─── Overview ─────────────────────────────────────────────── -->
|
|||
|
|
<section>
|
|||
|
|
<h2>Overview</h2>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>What the connector replaces</h3>
|
|||
|
|
<p>
|
|||
|
|
Every service currently has its own copy of auth middleware. They have drifted in
|
|||
|
|
three ways that cause constant breakage:
|
|||
|
|
</p>
|
|||
|
|
<ul style="margin-top:0.75rem;line-height:1.9;font-size:0.9rem;color:var(--muted)">
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">No transparent refresh</strong> — tunnel and
|
|||
|
|
styles only have a bare <code>requireAuth</code>. An expired access token always
|
|||
|
|
returns 401 with no recovery attempt, even when a valid refresh token is present.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">Wrong property name</strong> — roject uses
|
|||
|
|
<code>req.user</code>; tunnel, styles, and rokojori-auth use <code>req.auth</code>.
|
|||
|
|
Routes cannot be safely copied between services.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">ACCESS_TOKEN_TTL set to 10 seconds</strong> —
|
|||
|
|
the single most likely cause of constant auth failures. Every session is broken
|
|||
|
|
10 seconds after login unless the transparent refresh is working perfectly.
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Repos in scope</h3>
|
|||
|
|
<pre><code>rokojori-auth — fix ACCESS_TOKEN_TTL only; does NOT get the submodule
|
|||
|
|
roject — add submodule; delete local auth.ts; rename req.user → req.auth (19 places)
|
|||
|
|
styles — add submodule; delete local requireAuth.ts + requireAccess.ts; add jwtMiddleware
|
|||
|
|
tunnel — add submodule; delete local requireAuth.ts; add jwtMiddleware for API routes</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Do this first, before any other change</h3>
|
|||
|
|
<p>
|
|||
|
|
Fix the TTL bug in rokojori-auth and redeploy. Every other fix depends on
|
|||
|
|
transparent refresh working, and transparent refresh being tested every 10 seconds
|
|||
|
|
instead of every hour makes everything harder to reason about.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ─── rokojori-auth ─────────────────────────────────────────── -->
|
|||
|
|
<section>
|
|||
|
|
<h2>rokojori-auth</h2>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Does NOT get the submodule</h3>
|
|||
|
|
<p>
|
|||
|
|
rokojori-auth is the auth service itself. It cannot call itself for token
|
|||
|
|
refresh, so <code>jwtMiddleware</code> makes no sense here. Its own
|
|||
|
|
<code>requireAuth.ts</code> is correct for guarding its own API routes
|
|||
|
|
(<code>/api/auth/me</code> etc.) and should not change.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 1 — Fix ACCESS_TOKEN_TTL</h3>
|
|||
|
|
<p>File: <code>source/server/routes/auth.ts</code>, line 13</p>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
const ACCESS_TOKEN_TTL = '10s';
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
const ACCESS_TOKEN_TTL = '1h';</code></pre>
|
|||
|
|
<p style="margin-top:0.75rem">
|
|||
|
|
Redeploy immediately after this change. Restart the service and confirm with
|
|||
|
|
<code>journalctl -u rokojori-auth -n 20</code> that it came up cleanly.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Nothing else changes</h3>
|
|||
|
|
<p>
|
|||
|
|
The page-level redirect middleware in <code>source/server/index.ts</code> is
|
|||
|
|
correct for a pure server-rendered auth service — leave it as-is.
|
|||
|
|
The <code>requireAuth.ts</code> middleware is correct for the auth service's own
|
|||
|
|
routes — leave it as-is.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ─── roject ───────────────────────────────────────────────── -->
|
|||
|
|
<section>
|
|||
|
|
<h2>roject</h2>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Current state</h3>
|
|||
|
|
<p>
|
|||
|
|
Roject is the most advanced of the three — it already has
|
|||
|
|
<code>jwtMiddleware</code> with transparent refresh. The problems are:
|
|||
|
|
</p>
|
|||
|
|
<ul style="margin-top:0.75rem;line-height:1.9;font-size:0.9rem;color:var(--muted)">
|
|||
|
|
<li>Uses <code>req.user</code> instead of <code>req.auth</code> (19 call sites across 6 files)</li>
|
|||
|
|
<li>Uses local type <code>JwtUser</code> instead of <code>AuthPayload</code></li>
|
|||
|
|
<li><code>jwtMiddleware</code> still redirects non-API requests to
|
|||
|
|
<code>AUTH_HOST/api/auth/refresh-session</code> — unnecessary because
|
|||
|
|
server-side transparent refresh already handles page requests silently</li>
|
|||
|
|
<li>Missing <code>app.set('trust proxy', 1)</code> in <code>index.ts</code></li>
|
|||
|
|
<li>Electron <code>main.ts</code> intercepts <code>will-redirect</code> to handle
|
|||
|
|
the <code>refresh-session</code> redirect — can be removed once the redirect
|
|||
|
|
branch is gone</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 1 — Add the submodule</h3>
|
|||
|
|
<pre><code>git submodule add git@community.rokojori.com:Rokojori/rokojori-auth-connector.git source/auth-connector
|
|||
|
|
git submodule update --init</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 2 — tsconfig path alias</h3>
|
|||
|
|
<p>Add to <code>tsconfig.json</code> (or the relevant tsconfig for server compilation):</p>
|
|||
|
|
<pre><code>"paths": {
|
|||
|
|
"auth-connector/*": ["./source/auth-connector/source/*"]
|
|||
|
|
}</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 3 — Delete source/server/middleware/auth.ts</h3>
|
|||
|
|
<p>
|
|||
|
|
The entire file is replaced by the connector. Delete it after the imports
|
|||
|
|
in all dependents are updated (changes 4–7 below).
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 4 — Update source/server/index.ts</h3>
|
|||
|
|
<ul style="margin-top:0.5rem;line-height:1.9;font-size:0.9rem;color:var(--muted)">
|
|||
|
|
<li>Add <code>app.set( 'trust proxy', 1 );</code> before <code>app.use( express.json() )</code></li>
|
|||
|
|
<li>Change import: <code>from 'auth-connector/server/auth'</code></li>
|
|||
|
|
<li>Line 35: <code>req.user</code> → <code>req.auth</code></li>
|
|||
|
|
</ul>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { jwtMiddleware, requireAuth } from './middleware/auth';
|
|||
|
|
...
|
|||
|
|
app.get( '/api/auth/me', requireAuth, ( req, res ) => res.json( req.user ) );
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import { jwtMiddleware, requireAuth } from 'auth-connector/server/auth';
|
|||
|
|
...
|
|||
|
|
app.set( 'trust proxy', 1 );
|
|||
|
|
...
|
|||
|
|
app.get( '/api/auth/me', requireAuth, ( req, res ) => res.json( req.auth ) );</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 5 — Update source/server/projectAccess.ts</h3>
|
|||
|
|
<p>
|
|||
|
|
Replace the local <code>JwtUser</code> import with <code>AuthPayload</code>
|
|||
|
|
from the connector. The type shape is identical — this is a rename only.
|
|||
|
|
</p>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { JwtUser } from './middleware/auth';
|
|||
|
|
export function isOwner( project: Project, user: JwtUser ): boolean { ... }
|
|||
|
|
// ... all function signatures use JwtUser
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import type { AuthPayload } from 'auth-connector/shared/types';
|
|||
|
|
export function isOwner( project: Project, user: AuthPayload ): boolean { ... }
|
|||
|
|
// ... replace JwtUser with AuthPayload in all 4 function signatures</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 6 — Rename req.user → req.auth in route files</h3>
|
|||
|
|
<p>19 occurrences across 5 files. All are mechanical replacements — the shape of
|
|||
|
|
the object does not change.</p>
|
|||
|
|
<ul style="margin-top:0.5rem;line-height:1.9;font-size:0.9rem;color:var(--muted)">
|
|||
|
|
<li><code>source/server/routes/files.ts</code> — 7 occurrences</li>
|
|||
|
|
<li><code>source/server/routes/projects.ts</code> — 7 occurrences</li>
|
|||
|
|
<li><code>source/server/routes/layout.ts</code> — 2 occurrences</li>
|
|||
|
|
<li><code>source/server/routes/rojos.ts</code> — 2 occurrences</li>
|
|||
|
|
<li><code>source/server/routes/userSettings.ts</code> — 2 occurrences</li>
|
|||
|
|
</ul>
|
|||
|
|
<p style="margin-top:0.75rem">
|
|||
|
|
The <code>checkAccess</code> calls pass <code>req.user!</code> as the second
|
|||
|
|
argument. After change 5, <code>projectAccess.ts</code> expects <code>AuthPayload</code>
|
|||
|
|
— the rename makes the types consistent. Change every <code>req.user</code> to
|
|||
|
|
<code>req.auth</code> and every <code>req.user!</code> to <code>req.auth!</code>.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 7 — Remove the non-API redirect branch from jwtMiddleware</h3>
|
|||
|
|
<p>
|
|||
|
|
In the old local <code>auth.ts</code> (now deleted), <code>jwtMiddleware</code>
|
|||
|
|
redirected non-API requests with an expired token to
|
|||
|
|
<code>AUTH_HOST/api/auth/refresh-session</code>.
|
|||
|
|
The connector's <code>jwtMiddleware</code> does not do this — it handles
|
|||
|
|
the refresh server-side and never redirects. This is correct behavior.
|
|||
|
|
No explicit action needed here once the old file is deleted.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 8 — Remove the will-redirect intercept from electron/main.ts</h3>
|
|||
|
|
<p>
|
|||
|
|
The <code>mainWindow.webContents.on('will-redirect', ...)</code> block
|
|||
|
|
(lines 136–158) exists solely to intercept the <code>/api/auth/refresh-session</code>
|
|||
|
|
redirect that the old <code>jwtMiddleware</code> emitted. Once the redirect is
|
|||
|
|
gone, this intercept is dead code and should be removed.
|
|||
|
|
</p>
|
|||
|
|
<pre><code>// Remove this entire block from electron/main.ts:
|
|||
|
|
mainWindow.webContents.on( 'will-redirect', async ( event, url ) =>
|
|||
|
|
{
|
|||
|
|
if ( url.includes( '/api/auth/refresh-session' ) )
|
|||
|
|
{
|
|||
|
|
// ... entire block
|
|||
|
|
}
|
|||
|
|
} );</code></pre>
|
|||
|
|
<p style="margin-top:0.75rem">
|
|||
|
|
The <code>refreshTokens()</code> helper function defined above it can also
|
|||
|
|
be deleted — Electron no longer needs to do its own refresh because the
|
|||
|
|
server-side <code>jwtMiddleware</code> handles it transparently.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Verify</h3>
|
|||
|
|
<pre><code>journalctl -u roject -f | grep '\[auth\]'</code></pre>
|
|||
|
|
<p style="margin-top:0.5rem">
|
|||
|
|
Log into Roject, wait 65 minutes (or temporarily set <code>ACCESS_TOKEN_TTL=65s</code>
|
|||
|
|
in the test environment), then make an API call. Expect to see the
|
|||
|
|
<code>[auth] tryRefresh → ...</code> sequence and a transparent recovery.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ─── styles ───────────────────────────────────────────────── -->
|
|||
|
|
<section>
|
|||
|
|
<h2>styles</h2>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Current state</h3>
|
|||
|
|
<p>
|
|||
|
|
styles has no <code>jwtMiddleware</code> at all. <code>requireAuth</code> is
|
|||
|
|
wired directly onto routes. An expired access token causes an immediate 401 with
|
|||
|
|
no recovery attempt, regardless of whether the refresh token is valid.
|
|||
|
|
<code>STYLES_RULES</code> is currently defined inside
|
|||
|
|
<code>middleware/requireAccess.ts</code> — it needs to move to <code>index.ts</code>.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 1 — Add the submodule</h3>
|
|||
|
|
<pre><code>git submodule add git@community.rokojori.com:Rokojori/rokojori-auth-connector.git source/auth-connector
|
|||
|
|
git submodule update --init</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 2 — tsconfig path alias</h3>
|
|||
|
|
<pre><code>"paths": {
|
|||
|
|
"auth-connector/*": ["./source/auth-connector/source/*"]
|
|||
|
|
}</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 3 — Delete both middleware files</h3>
|
|||
|
|
<pre><code>source/server/middleware/requireAuth.ts ← delete
|
|||
|
|
source/server/middleware/requireAccess.ts ← delete</code></pre>
|
|||
|
|
<p style="margin-top:0.5rem">
|
|||
|
|
Do this after updating <code>index.ts</code> so the service never references
|
|||
|
|
the deleted files.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 4 — Rewrite source/server/index.ts imports and wiring</h3>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { requireAuth } from './middleware/requireAuth';
|
|||
|
|
import { requireAccess, STYLES_RULES } from './middleware/requireAccess';
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import { jwtMiddleware, requireAuth, requireAccess } from 'auth-connector/server/auth';
|
|||
|
|
import type { AccessRule } from 'auth-connector/shared/types';
|
|||
|
|
|
|||
|
|
const STYLES_RULES: AccessRule[] = [
|
|||
|
|
{ role: 'admin' },
|
|||
|
|
{ role: 'user', product: 'styles' },
|
|||
|
|
{ role: 'user', product: 'premium' },
|
|||
|
|
];</code></pre>
|
|||
|
|
<p style="margin-top:0.75rem">
|
|||
|
|
Then add <code>jwtMiddleware</code> as global middleware — place it after
|
|||
|
|
<code>cookieParser()</code> and before the route registrations:
|
|||
|
|
</p>
|
|||
|
|
<pre><code>app.use( cookieParser() );
|
|||
|
|
app.use( jwtMiddleware ); // ← add this line
|
|||
|
|
|
|||
|
|
// rest of routes unchanged...</code></pre>
|
|||
|
|
<p style="margin-top:0.75rem">
|
|||
|
|
The route registrations themselves do not change — they already use
|
|||
|
|
<code>requireAuth</code> and <code>requireAccess( STYLES_RULES )</code>.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Verify</h3>
|
|||
|
|
<pre><code>journalctl -u styles-rokojori -f | grep '\[auth\]'</code></pre>
|
|||
|
|
<p style="margin-top:0.5rem">
|
|||
|
|
Hit <code>/api/fonts</code> with an expired token and a valid refresh cookie.
|
|||
|
|
Expect transparent recovery in the log.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ─── tunnel ───────────────────────────────────────────────── -->
|
|||
|
|
<section>
|
|||
|
|
<h2>tunnel</h2>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Current state</h3>
|
|||
|
|
<p>
|
|||
|
|
tunnel has no <code>jwtMiddleware</code>. Expired tokens on
|
|||
|
|
<code>/api/tunnels</code> routes always return 401 with no recovery.
|
|||
|
|
Two routes do their own inline JWT handling and must be treated carefully:
|
|||
|
|
</p>
|
|||
|
|
<ul style="margin-top:0.75rem;line-height:1.9;font-size:0.9rem;color:var(--muted)">
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">routes/agent.ts</strong> — handles WebSocket
|
|||
|
|
upgrades; cannot use Express middleware. Has its own <code>jwt.verify()</code>
|
|||
|
|
call. This is correct and does not change — WebSocket upgrades bypass Express
|
|||
|
|
middleware entirely.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">routes/proxy.ts</strong> — <code>softAuth()</code>
|
|||
|
|
does a non-blocking JWT check for the proxy route. Public tunnels pass even
|
|||
|
|
without a token. This intentional soft auth stays local to proxy.ts — do not
|
|||
|
|
replace it with <code>jwtMiddleware</code>.
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 1 — Add the submodule</h3>
|
|||
|
|
<pre><code>git submodule add git@community.rokojori.com:Rokojori/rokojori-auth-connector.git source/auth-connector
|
|||
|
|
git submodule update --init</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 2 — tsconfig path alias</h3>
|
|||
|
|
<pre><code>"paths": {
|
|||
|
|
"auth-connector/*": ["./source/auth-connector/source/*"]
|
|||
|
|
}</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 3 — Delete source/server/middleware/requireAuth.ts</h3>
|
|||
|
|
<p>Delete after updating all importers below.</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 4 — Update source/server/index.ts</h3>
|
|||
|
|
<p>
|
|||
|
|
Add <code>jwtMiddleware</code> scoped to the <code>/api/tunnels</code> routes.
|
|||
|
|
The proxy route (<code>/t</code>) must <strong style="color:var(--text)">not</strong>
|
|||
|
|
get <code>jwtMiddleware</code> — it does its own soft auth.
|
|||
|
|
</p>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { requireAuth } from './middleware/requireAuth'; // (was unused at index level)
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import { jwtMiddleware } from 'auth-connector/server/auth';
|
|||
|
|
|
|||
|
|
// Add jwtMiddleware only for the API routes section:
|
|||
|
|
app.use( '/api/tunnels', jwtMiddleware, express.json(), tunnelsRouter );
|
|||
|
|
|
|||
|
|
// The /t proxy route stays unchanged — no jwtMiddleware
|
|||
|
|
app.use( '/t', proxyRouter );</code></pre>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 5 — Update routes/tunnels.ts</h3>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { requireAuth } from '../middleware/requireAuth';
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import { requireAuth } from 'auth-connector/server/auth';</code></pre>
|
|||
|
|
<p style="margin-top:0.5rem">
|
|||
|
|
All uses of <code>req.auth</code> in this file are already correct — no other
|
|||
|
|
changes needed.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 6 — Update routes/proxy.ts</h3>
|
|||
|
|
<p>
|
|||
|
|
<code>proxy.ts</code> imports <code>AuthPayload</code> and
|
|||
|
|
<code>extractBearer</code> from the old local <code>requireAuth.ts</code>.
|
|||
|
|
<code>extractBearer</code> is internal to the connector and not exported.
|
|||
|
|
Inline it locally — it is three lines.
|
|||
|
|
</p>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { AuthPayload, extractBearer } from '../middleware/requireAuth';
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import type { AuthPayload } from 'auth-connector/shared/types';
|
|||
|
|
|
|||
|
|
function extractBearer( req: Request ): string | undefined
|
|||
|
|
{
|
|||
|
|
const h = req.headers.authorization;
|
|||
|
|
return h?.startsWith( 'Bearer ' ) ? h.slice( 7 ) : undefined;
|
|||
|
|
}</code></pre>
|
|||
|
|
<p style="margin-top:0.75rem">
|
|||
|
|
The <code>softAuth()</code> function and all other logic in proxy.ts stays
|
|||
|
|
unchanged.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Change 7 — Update routes/agent.ts</h3>
|
|||
|
|
<pre><code>// Before
|
|||
|
|
import { AuthPayload } from '../middleware/requireAuth';
|
|||
|
|
|
|||
|
|
// After
|
|||
|
|
import type { AuthPayload } from 'auth-connector/shared/types';</code></pre>
|
|||
|
|
<p style="margin-top:0.5rem">
|
|||
|
|
The inline <code>jwt.verify()</code> in the WebSocket upgrade handler is
|
|||
|
|
correct and stays — WebSocket upgrades bypass Express middleware.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>Verify</h3>
|
|||
|
|
<pre><code>journalctl -u tunnel-rokojori -f | grep '\[auth\]'</code></pre>
|
|||
|
|
<p style="margin-top:0.5rem">
|
|||
|
|
Make an authenticated API call to <code>/api/tunnels</code> with an expired
|
|||
|
|
access token and a valid refresh cookie. Expect transparent recovery in the log.
|
|||
|
|
Then make a request to a public tunnel (<code>/t/:id/...</code>) without any
|
|||
|
|
token — expect it to pass through without hitting the auth log at all.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- ─── Execution order ───────────────────────────────────────── -->
|
|||
|
|
<section>
|
|||
|
|
<h2>Execution order</h2>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<ol style="line-height:2.2;font-size:0.9rem;color:var(--muted)">
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">rokojori-auth</strong> — fix
|
|||
|
|
<code>ACCESS_TOKEN_TTL</code> to <code>'1h'</code>, redeploy, verify startup log.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">Create the Gitea repo</strong> for
|
|||
|
|
<code>rokojori-auth-connector</code> and push the local directory to it.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">roject</strong> — add submodule, make all
|
|||
|
|
changes, redeploy. This is the most complex change (19 renames + Electron
|
|||
|
|
cleanup). Test thoroughly before moving to the others.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">styles</strong> — add submodule, rewire
|
|||
|
|
index.ts, delete old middleware files, redeploy.
|
|||
|
|
</li>
|
|||
|
|
<li>
|
|||
|
|
<strong style="color:var(--text)">tunnel</strong> — add submodule, add
|
|||
|
|
jwtMiddleware to API routes, update imports in tunnels/proxy/agent,
|
|||
|
|
delete old requireAuth.ts, redeploy.
|
|||
|
|
</li>
|
|||
|
|
</ol>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card">
|
|||
|
|
<h3>After each repo</h3>
|
|||
|
|
<p>
|
|||
|
|
Before moving to the next repo: confirm auth still works end-to-end —
|
|||
|
|
login, wait for the access token to expire (check with
|
|||
|
|
<code>journalctl ... | grep '[auth]'</code>), and confirm the first API call
|
|||
|
|
after expiry succeeds transparently.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<footer>
|
|||
|
|
Roject — auth connector rewrite plan
|
|||
|
|
</footer>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
<script>var NAV_ROOT = '../';</script>
|
|||
|
|
<script src="../_assets_/nav-data.js"></script>
|
|||
|
|
<script src="../_assets_/nav.js"></script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|