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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ima-jin/auth-client

v0.8.18

Published

"Sign in with Imajin" SDK for federated apps — JWT session management and ready-made Next.js route handlers

Readme

@ima-jin/auth-client

"Sign in with Imajin" SDK for federated apps. Lightweight JWT session management + ready-made Next.js route handlers.

Install

npm install @ima-jin/auth-client

Quick Start

1. Configure

// src/lib/auth-config.ts
import type { ImajinAuthConfig } from '@ima-jin/auth-client';

export const authConfig: ImajinAuthConfig = {
  secret: process.env.SESSION_SECRET!,
  authUrl: process.env.IMAJIN_AUTH_URL!,
  appDid: process.env.IMAJIN_APP_DID,
  publicUrl: process.env.NEXT_PUBLIC_APP_URL,
  loginRedirect: '/dashboard',
};

2. Create route handlers

// app/api/auth/callback/route.ts
import { createCallbackHandler } from '@ima-jin/auth-client';
import { authConfig } from '@/lib/auth-config';
export const GET = createCallbackHandler(authConfig);

// app/api/auth/session/route.ts
import { createSessionHandler } from '@ima-jin/auth-client';
import { authConfig } from '@/lib/auth-config';
export const GET = createSessionHandler(authConfig);

// app/api/auth/logout/route.ts
import { createLogoutHandler } from '@ima-jin/auth-client';
import { authConfig } from '@/lib/auth-config';
export const POST = createLogoutHandler(authConfig);

3. Check session in server components

import { getSession } from '@ima-jin/auth-client';
import { authConfig } from '@/lib/auth-config';

const user = await getSession(authConfig);
if (!user) redirect('/');

4. Fetch this app's own signing key at boot (#2411)

An app provisioned via the kernel's apps.provision (see ima-jin/imajin-ai's docs/REGISTRATION.md) never holds a raw IMAJIN_APP_PRIVATE_KEY in any env file. Instead, .env.local carries IMAJIN_KERNEL_URL, IMAJIN_APP_DID, and — only until first boot — a one-time IMAJIN_APP_CLAIM_CODE, shown once on the kernel operator's /jin approval card:

// src/lib/signing-identity.ts
import { loadAppSigningKey } from '@ima-jin/auth-client';

let signingKey: Awaited<ReturnType<typeof loadAppSigningKey>> | null = null;

export async function bootstrapSigningIdentity(): Promise<void> {
  signingKey = await loadAppSigningKey();
}

export function getSigningIdentity() {
  if (!signingKey) throw new Error('signing identity not bootstrapped yet');
  return signingKey; // { appDid, privateKey, publicKey }
}

First boot vs. every later boot (Ryan's restart-authentication ruling, #2411): loadAppSigningKey() mints its own Ed25519 "bootstrap" keypair on first boot, exchanges the claim code + that keypair's public half for the real signing key, and persists ONLY the bootstrap keypair — never the signing key — in a local keystore file (IMAJIN_APP_KEYSTORE, default ./.imajin/keystore.json, mode 0600). Every later boot signs a fresh challenge with that persisted bootstrap key instead of spending another claim code, so no operator action is needed on an ordinary restart.

loadAppSigningKey() throws on any failure (missing config, an already-redeemed or expired claim code, an invalid bootstrap signature, a revoked grant) rather than degrading silently — a signing key is the app's own identity, so a misconfigured deploy should fail loudly at boot. If the local keystore is ever lost (disk wipe, redeploy to a fresh host), ask the kernel operator to re-approve apps.provision with reissueClaim: true for a fresh claim code — redeeming it also revokes the lost keystore's bootstrap key so it can never authenticate a fetch again.

| Env var | When needed | |---|---| | IMAJIN_KERNEL_URL | Always | | IMAJIN_APP_DID | Every later boot (once a keystore exists) | | IMAJIN_APP_CLAIM_CODE | First boot only (or after a reissueClaim: true rebind) | | IMAJIN_APP_KEYSTORE | Optional — defaults to ./.imajin/keystore.json |

5. Mint an app token from client components (/browser)

The . entry also imports fs, crypto and path (via loadAppSigningKey() and the keystore), so importing anything from it into a client component fails the Next.js build (Module not found: Can't resolve 'fs'). For code that runs in the browser, import from the browser-safe subpath instead:

'use client';
import { requestAppToken } from '@ima-jin/auth-client/browser';
import type { RequestAppTokenOptions, RequestAppTokenResult } from '@ima-jin/auth-client/browser';

const minted = await requestAppToken({
  authUrl: process.env.NEXT_PUBLIC_IMAJIN_KERNEL_URL!,
  aud: window.location.host,
  scopes: ['profile:read'],
});
// minted: { token, expiresIn, scopes } | null

requestAppToken calls POST {authUrl}/auth/api/tokens/app with credentials: 'include' (so the user's kernel session cookie is sent) and returns null on any failure.

When to use which entry

| Entry | Use it for | |---|---| | @ima-jin/auth-client/browser | Client components / any browser bundle — only requestAppToken and its types | | @ima-jin/auth-client | Server code — sessions, route handlers, loadAppSigningKey() (it also still exports requestAppToken, so existing imports keep working) | | @ima-jin/auth-client/handlers | Next.js route handler factories |

The browser entry's import graph is guaranteed free of node built-ins, next/headers, and keystore/signing-key code; tests/browser-entry.test.ts bundles it for a browser target to enforce that.

App-declared secrets (#2799)

After loadAppSigningKey() establishes the bootstrap keystore, server code can call loadFromVault({ keys: [{ key: 'SESSION_SECRET', onMissing: 'fail' }] }). It returns { values, degraded }. appDid, kernelUrl, and keystorePath use the same environment defaults as loadAppSigningKey(). Names must match the app manifest's secrets declarations; no grant IDs or generation recipes are supplied by the caller.

This auth-client helper is not the generic grant loader exported by @imajin/auth; that API and its deferred acknowledgement handles are unchanged. Here, the bound bootstrap keystore (not the registered app signing key) authenticates every fetch. Domain, app DID, requested names, recipient key, nonce and timestamp are signed. The kernel returns only a vault-core sealed envelope; the SDK checks the decrypted DID, nonce and requested names before returning values. Rotation is picked up on the next fetch.

Values stay in process memory: never persist, log, or copy them into environment variables. onMissing: 'fail' throws a value-free error; 'degrade' omits the unavailable name from values and lists it in degraded. Each key uses an independent fresh request so optional keys cannot hide required ones. The SDK requires HTTPS except for literal loopback hosts; redirects are refused. Production ingress must enforce public TLS. The handler permits internal HTTP URLs under a TLS-terminating reverse proxy without trusting forwarding headers. The endpoint refuses inactive apps, revoked/undeclared names, wrong keys, altered requests and nonce replay. Nonces are currently tracked in the kernel process, matching the existing single-instance bootstrap fetch mechanism. This helper does not issue acknowledgements or claim that a fetched value was used.

Part of Imajin

Imajin — sovereign technology infrastructure. Open source.