npm.io
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 AuthEnv with your service's own bindings and pass the combined type as the factory's type parameter.
  • Add capability strings to JWTPayload.claims at token-issue time; createAdminAuthMiddleware checks for the literal 'admin' claim, and handlers can read c.get('claims') to enforce their own.
  • Pass a custom defaultSecret to the middleware factories for local dev setups that do not bind JWT_SECRET.
  • expiresInSeconds on generateToken controls 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 local auth.ts copy is deduped against this package in a later phase; formats must stay byte-compatible)
  • progress-tracker-api (Phase 4)