npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fractalshq/auth-core

v0.1.12

Published

Pure, side-effect free helpers for SIWS and action intents.

Readme

@fractalshq/auth-core

Pure, side-effect free helpers for SIWS and action intents.

API

import { core } from "@fractalshq/auth-core";

const { issuedAt, expirationTime } = core.timeWindow(new Date(), 300);
const message = core.generateSiwsMessage({
  domain: "example.com",
  address: "<pubkey>",
  statement: "Sign in",
  uri: "https://example.com",
  nonce: core.randomNonce(),
  issuedAt,
  expirationTime,
});
const ok = core.verifySiwsSignature({ publicKey: "<pubkey>", signature: "<sig>", message });

// Ledger / transaction-based flow: verify against serialized message bytes
const okLedger = core.verifySiwsSignature({
  publicKey: "<pubkey>",
  signature: "<sig>",
  messageBytes: "BASE64_SERIALIZED_MESSAGE",
  messageEncoding: "base64",
});

// Ledger memo helper: build memo payload and validate memo transaction payloads
const parsed = core.verifyMemoAuthSignature({
  publicKey: "<pubkey>",
  signature: "<sig>",
  messageBytes: "BASE64_SERIALIZED_MESSAGE",
  messageEncoding: "base64",
  expectedMemo: core.generateMemoAuthPayload({
    nonce: "<nonce>",
    issuedAt,
    expirationTime,
    domain: "example.com",
  }),
});
console.log(parsed.memo, parsed.recentBlockhash);
console.log(parsed.payload?.nonce);

Constants and roles

import {
  API_PREFIX,
  AUTH_BASE,
  ORGS_BASE,
  ME_ENDPOINT,
  FRACTALS_PLATFORM_ID,
  DISTRIBUTION_SAAS_PLATFORM_ID,
  FRACTALS_ORGANIZATIONS_ID,
  DISTRIBUTION_SAAS_ORGANIZATIONS_ID,
  PLATFORM_ROLE_KEYS,
} from "@fractalshq/auth-core";

SIWS Driver

Create a driver that talks to your auth service. baseURL can be the origin or include /api/v1/auth; it is normalized internally.

import { createSiwsDriver } from "@fractalshq/auth-core";

const driver = createSiwsDriver({
  baseURL: process.env.AUTH_BASE_URL, // e.g. https://auth.example.com or https://auth.example.com/api/v1/auth
  getApiKey: () => process.env.AUTH_INTERNAL_API_KEY || null,
});

Client-side helpers

These throw on non-2xx and return parsed payloads:

// 1) Create a SIWS message
const { message } = await driver.createNonce("<pubkey>");

// 2) Sign message with wallet (client)
// ... get signature as base58 string ...

// 3) Complete sign-in
// For Ledger/tx flows, pass messageBytes (e.g., base64-encoded serialized message)
const { userId } = await driver.signInWithWallet({
  pubkey: "<pubkey>",
  signature: "<sig>",
  message,
  // messageBytes: base64Serialized,
  // messageEncoding: "base64",
});

Server/API route helpers (UpstreamResponse)

When proxying through your Next.js/express route and you need to forward status and Set-Cookie, use the UpstreamResponse (or HttpResponse) variants. They do not throw on non-2xx and include headers to forward.

// Create nonce and forward upstream response as-is
const { status, bodyText, contentType, setCookie } = await driver.createNonceUpstreamResponse("<pubkey>");
const res = new Response(bodyText, { status, headers: { "content-type": contentType || "application/json" } });
if (setCookie) (res.headers as any).set("set-cookie", setCookie as any);

// Sign-in and forward cookies
const r = await driver.signInWithWalletUpstreamResponse({ pubkey, signature, message, organizationId });

Aliases:

  • createNonceUpstreamResponse = createNonceHttpResponse = createNonceRaw
  • signInWithWalletUpstreamResponse = signInWithWalletHttpResponse = signInWithWalletRaw

One-file Next.js Route (framework-agnostic handler)

Use a single catch-all route that forwards cookies/headers to your auth service and supports safe redirects. Handlers use Web Request/Response, so they are portable.

  1. Create app/api/v1/auth/siws/[...siws]/route.ts in your app:
import { siwsRouteHandler } from "@fractalshq/auth-core";
import type { SiwsRouteOptions } from "@fractalshq/auth-core";

const options: SiwsRouteOptions = {
  // Optional: allow redirects via ?redirectTo=
  redirectParam: "redirectTo",
  // Comma-separated env of allowed cross-origin redirects (host[:port])
  allowedRedirectHosts: (process.env.ALLOWED_REDIRECT_HOSTS || "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean),
};

export async function POST(req: Request) {
  return siwsRouteHandler(req, options);
}
  1. Optionally re-export specific endpoints (e.g., create-nonce) to keep nice paths:
// app/api/v1/auth/siws/create-nonce/route.ts
export { POST } from "../[...siws]/route";
  1. me and sessions routes using SDK handlers:
// app/api/v1/me/route.ts
export { meRouteGET as GET } from "@fractalshq/auth-core";

// app/api/v1/sessions/route.ts
export { sessionsRouteGET as GET, sessionsRouteDELETE as DELETE } from "@fractalshq/auth-core";
  1. Required env variables:
# Auth service origin (or /api/v1/auth path, both work)
AUTH_BASE_URL=https://auth.example.com

# Optional: internal key forwarded as x-internal-key
AUTH_INTERNAL_API_KEY=your-internal-key

# Optional: allowlist hosts for absolute redirects
ALLOWED_REDIRECT_HOSTS=app.example.com,another.example.com:3000

Notes:

  • Redirects use 303 and forward upstream Set-Cookie when present.
  • You can still opt into Next-specific helpers via NextResponse.from(...) if needed.

All-in-one Router (optional)

If you prefer a single route that handles SIWS, sessions, and me:

// app/api/v1/[...fractals-auth]/route.ts
import { fractalsAuthHandler } from "@fractalshq/auth-core";

export const GET = (req: Request) => fractalsAuthHandler(req, {
  redirectParam: "redirectTo",
  allowedRedirectHosts: (process.env.ALLOWED_REDIRECT_HOSTS || "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean),
});

export const POST = (req: Request) => fractalsAuthHandler(req, {
  redirectParam: "redirectTo",
  allowedRedirectHosts: (process.env.ALLOWED_REDIRECT_HOSTS || "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean),
});

export const DELETE = (req: Request) => fractalsAuthHandler(req);

Supported paths:

  • /api/v1/auth/siws/create-nonce (POST)
  • /api/v1/auth/siws/sign-in (POST)
  • /api/v1/sessions (GET, DELETE)
  • /api/v1/me (GET)

Role helpers usage

Protect routes and check roles with server helpers.

// app/api/admin/route.ts
import { requirePlatformRole, FRACTALS_PLATFORM_ID } from "@fractalshq/auth-core";

export async function GET(req: Request) {
  const forbidden = await requirePlatformRole(req, FRACTALS_PLATFORM_ID, ["admin"]);
  if (forbidden) return forbidden;
  return Response.json({ ok: true });
}
// app/api/org/[id]/manage/route.ts
import { requireOrgRole } from "@fractalshq/auth-core";

export async function POST(req: Request, { params }: { params: { id: string } }) {
  const forbidden = await requireOrgRole(req, params.id, ["admin", "distributor"]);
  if (forbidden) return forbidden;
  return Response.json({ ok: true });
}
// app/api/me/route.ts
import { getMeFromRequest } from "@fractalshq/auth-core";

export async function GET(req: Request) {
  const me = await getMeFromRequest(req);
  if (!me) return new Response(null, { status: 204 });
  return Response.json(me);
}
// app/api/guarded/route.ts
import { withRequiredRole, requireOrgRole } from "@fractalshq/auth-core";

async function handler(_req: Request) {
  return Response.json({ ok: true });
}

export const GET = withRequiredRole(handler, (req) => requireOrgRole(req, "<org-id>", ["admin"]));