Skip to content

Passwords & JWT

import { hashPassword, verifyPassword } from "@mountsqli/auth";
const hash = hashPassword("s3cret");
// scrypt$15$<salt>$<hash>
const ok = verifyPassword("s3cret", hash); // true
  • scrypt cost N = 2^15 (above Node’s 2^14 default).
  • Cost stored in the hash format scrypt$<cost>$<salt>$<hash> so it can be raised later without breaking existing hashes.
  • Comparison is constant-time (timingSafeEqual).

Uses the jose library under the hood.

import { createToken, verifyToken, decodeToken } from "@mountsqli/auth";
const token = await createToken(
{ sub: "u1", email: "a@b.c" },
"my-secret",
{ maxAge: 3600 },
);
const payload = await verifyToken(token, "my-secret");
// { sub: "u1", email: "a@b.c", iat: ..., exp: ... } or null
const decoded = decodeToken(token); // no verification, debugging only
Function Purpose
hashPassword(pw) → scrypt hash string
verifyPassword(hash, pw) boolean (constant-time)
createToken(payload, secret, opts?) → JWT string (HS256)
verifyToken(token, secret) JWTPayload | null
decodeToken(token) → decoded payload (no verify)
  • Store the hash, never the password.
  • Use a 32+ byte random secret for HS256.
  • Let createToken set iat/exp — don’t hand-roll them.
  • Using a short or guessable JWT secret.
  • Storing password hashes in a column without .nullable() (OAuth-only users have no password).