Player accounts (auth slice 1): register/login, JWT issuance, isolation

GUPI-issued local auth (path A): osint.users (handle + scrypt password_hash +
display_name + avatar_url; external_id reserved for a later glitch.university
key-exchange). POST /api/auth/register|login set the auth_token cookie via a
new signPlayerToken (sub=user id, role=player); /logout and /me added. Because
playthroughs already bind to resolveUserId, two players each "Begin" and stay
fully isolated. Verification stays issuer-agnostic for the external swap later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 21:42:49 +02:00
co-authored by Claude Opus 4.8
parent 03266b5f40
commit ce0e82f7fb
4 changed files with 112 additions and 1 deletions
+7
View File
@@ -49,6 +49,13 @@ export function requireAdmin(req: Request, res: Response, next: NextFunction) {
next()
}
// Mint a player token (path A: GUPI is the issuer for now). Verification is
// issuer-agnostic — a glitch.university token with the same sub verifies identically.
export function signPlayerToken(user: { id: string; displayName: string }) {
if (!process.env.JWT_SECRET) throw new Error('JWT_SECRET is required')
return jwt.sign({ sub: user.id, name: user.displayName, role: 'player' }, process.env.JWT_SECRET, { expiresIn: '30d' })
}
export function createDevelopmentAdminToken() {
if (process.env.NODE_ENV === 'production') throw new Error('Development sessions are disabled in production')
if (!process.env.JWT_SECRET) throw new Error('JWT_SECRET is required')