@nodii/auth-sdk
v0.5.0
Published
User-JWT verifier + REST issuer-client + Hono/Express middleware for services that consume nodii-auth. Verifies RS256 tokens against the JWKS published by nodii-auth.
Maintainers
Readme
@nodii/auth-sdk
User-JWT verifier + REST issuer-client + Hono/Express middleware for
services that consume nodii-auth. Verifies RS256 tokens against
the JWKS published by nodii-auth at
https://auth.<env>.nodii.co/.well-known/jwks.json.
bun add @nodii/auth-sdk
# or
npm install @nodii/auth-sdkWhat you get
| Sub-path | Purpose |
|---|---|
| @nodii/auth-sdk/verify | NodiiAuthVerifier — RS256-only JWT verifier |
| @nodii/auth-sdk/client | NodiiAuthClient — typed REST client for nodii-auth's HTTP API |
| @nodii/auth-sdk/hono | requireAuth() Hono middleware |
| @nodii/auth-sdk/express | requireAuth() Express 4/5 middleware (for legacy services) |
| @nodii/auth-sdk/types | NodiiClaims, LoginResult, AuthSdkError, error code unions |
Verifier — Hono
import { Hono } from "hono";
import { NodiiAuthVerifier } from "@nodii/auth-sdk/verify";
import { requireAuth, type NodiiAuthEnv } from "@nodii/auth-sdk/hono";
const verifier = new NodiiAuthVerifier({
issuer: "https://auth.prod.nodii.co",
jwksUri: "https://auth.prod.nodii.co/.well-known/jwks.json",
});
const app = new Hono<NodiiAuthEnv>();
app.use("/api/*", requireAuth({ verifier }));
app.get("/api/me", (c) => c.json({ claims: c.get("nodiiClaims") }));requireAuth reads from Authorization: Bearer <token> by default.
Pass source: "cookie" + cookieName: "nodii_at" to read from a
cookie instead, or source: "both" to fall back from header to
cookie. Pass requiredPermissions: ["billing.write"] to gate the
route — missing permissions return 403 missing_permission.
Verifier — Express 5 (legacy services)
import express from "express";
import { NodiiAuthVerifier } from "@nodii/auth-sdk/verify";
import { requireAuth, type NodiiAuthRequest } from "@nodii/auth-sdk/express";
const verifier = new NodiiAuthVerifier({
issuer: "https://auth.prod.nodii.co",
jwksUri: "https://auth.prod.nodii.co/.well-known/jwks.json",
});
const app = express();
app.use(requireAuth({ verifier }));
app.get("/api/me", (req: NodiiAuthRequest, res) => {
res.json({ claims: req.nodiiClaims });
});Both middlewares emit the same error envelope shape:
{ "error": true, "code": "missing_token" }Stable error codes:
missing_token— no Authorization header / cookie presentinvalid_token— malformed JWTexpired_token—expin the pastinvalid_algorithm— alg ≠RS256(oralg=none)invalid_signature— signature did not verify against the resolved keymissing_kid/no_matching_key— JWKS resolution failedinvalid_issuer—issdid not match the configured issuermissing_principal_kind— token lacks theprincipal_kindclaimmissing_permission— token'spermissionsdid not include all requirednetwork_error— JWKS fetch failed (timeout / connection refused)
All map to 401 except missing_permission, which is 403.
REST client
import { NodiiAuthClient, AuthSdkError } from "@nodii/auth-sdk/client";
const auth = new NodiiAuthClient({
baseUrl: "https://auth.prod.nodii.co",
});
try {
const result = await auth.login({
email: "[email protected]",
password: "...",
tenantId: "t-123",
});
if (result.kind === "challenge") {
// Cognito sent back a challenge (NEW_PASSWORD_REQUIRED, MFA, …)
// — pass result.challengeSession into the matching cognito.* call.
} else {
// result.token is the user JWT, valid for ~15min.
}
} catch (e) {
if (e instanceof AuthSdkError && e.code === "rest_error") {
// Server returned a non-2xx envelope { error: true, code, message? }.
// The issuer's envelope code is exposed structured for dispatch:
if (e.restEnvelopeCode === "invalid_credentials") {
// ...
}
}
}Methods:
login()/refresh()/logout()passwordReset.request()/passwordReset.confirm()cognito.respondNewPassword()/cognito.respondMfa()/cognito.confirmForgotPassword()
Security guarantees
- Algorithm allowlist is hard-pinned to
["RS256"].alg=none,alg=HS256, andalg=RS384are all rejected withinvalid_algorithm. kidis required on every incoming token. Tokens withoutkid, or with akidnot present in the JWKS, are rejected withmissing_kid/no_matching_key.- Issuer is compared exact-string against the configured
issuer. - The full adversarial test matrix lives in
tests/verifier.test.ts— includingalg=none, HS256-with-public-key (CVE-2018-7489 shape), RS384, tampered payloads, expired tokens, wrong issuer, and missingprincipal_kind.
Peer dependencies
hono (^4.0.0) and express (^5.0.0) are listed as optional
peer dependencies — only install the one your service uses. The
verifier and REST client have no framework dependency.
The package ships dual ESM + CJS builds, so legacy CommonJS services
can require("@nodii/auth-sdk/express") without ERR_REQUIRE_ESM.
Express 4 is not currently in scope (no CI test pass). If you need v4 support, open an issue with the consumer service's repro.
Development
This package lives in the nodii-libs monorepo. From the repo root:
bun install
bun run --cwd packages/auth-sdk test
bun run --cwd packages/auth-sdk typecheck
bun run --cwd packages/auth-sdk build