0.0.0 • Published 2d ago
@game-infra/shared-be
Licence
—
Version
0.0.0
Deps
0
Size
36 kB
Vulns
0
Weekly
0
@game-infra/shared-be
JWT signing/verification and password hashing built on the Web Crypto API, plus Hono auth middleware, for Cloudflare Workers services. Tokens are HS256; passwords are stored as salt:sha256hex (bcrypt-style algorithms are not practical inside Workers). The wire formats are shared with existing user records and issued tokens, so they are frozen: do not change claim names, the salt:hash layout, or any encoding.
Linking
This package is consumed as source from a sibling game-infra checkout, not from npm.
pnpm:
{
"dependencies": {
"@game-infra/shared-be": "link:../../../game-infra/packages/shared-be"
}
}
npm:
{
"dependencies": {
"@game-infra/shared-be": "file:../../../game-infra/packages/shared-be"
}
}
hono is a peer dependency (^4.12.0, mirrored in COMPAT.json); the consuming service provides it.
Usage
Sign and verify a JWT
import { generateToken, verifyToken } from "@game-infra/shared-be";
// Login endpoint: issue a token (expires in 30 days by default)
const token = await generateToken(
{ userId: user.id, username: user.username, claims: ["admin"] },
c.env.JWT_SECRET,
);
// Later: verify it. Returns null for invalid or expired tokens, never throws.
const payload = await verifyToken(token, c.env.JWT_SECRET);
if (!payload) {
return c.json({ error: "invalid token" }, 401);
}
console.log(payload.userId, payload.username, payload.claims);
Hash and check passwords
import { hashPassword, verifyPassword } from "@game-infra/shared-be";
// Registration: store the salt:hash string
const passwordHash = await hashPassword(body.password);
// Login: check the plaintext against the stored hash
const ok = await verifyPassword(body.password, user.passwordHash);
Wire the middleware into a Hono app
import {
createAdminAuthMiddleware,
createOptionalUserAuthMiddleware,
createUserAuthMiddleware,
type AuthEnv,
type UserContext,
} from "@game-infra/shared-be";
import { Hono } from "hono";
interface Env extends AuthEnv {
DB: D1Database;
}
const app = new Hono<{ Bindings: Env; Variables: UserContext }>();
app.use("/api/*", createUserAuthMiddleware<Env>());
app.use("/leaderboard", createOptionalUserAuthMiddleware<Env>());
app.use("/admin/*", createAdminAuthMiddleware<Env>());
app.get("/api/profile", (c) => c.json({ userId: c.get("userId") }));
app.get("/leaderboard", (c) => c.json({ me: c.get("userId") ?? null }));
app.delete("/admin/users/:id", (c) => c.json({ deletedBy: c.get("username") }));
export default app;
The middleware reads the secret from the JWT_SECRET binding and falls back to a dev default. Always bind JWT_SECRET in deployed environments.
Extension points
- Extend
AuthEnvwith your service's own bindings and pass the combined type as the factory's type parameter. - Add capability strings to
JWTPayload.claimsat token-issue time;createAdminAuthMiddlewarechecks for the literal'admin'claim, and handlers can readc.get('claims')to enforce their own. - Pass a custom
defaultSecretto the middleware factories for local dev setups that do not bindJWT_SECRET. expiresInSecondsongenerateTokencontrols token lifetime per call site.
Dependencies
- Internal: none.
- External:
hono(peer). The crypto primitives come from the Workers runtime (crypto.subtle,btoa/atob).
Consumers
core-service(its localauth.tscopy is deduped against this package in a later phase; formats must stay byte-compatible)progress-tracker-api(Phase 4)