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

@auth9/node

v0.9.1

Published

Auth9 Node.js SDK - token verification, gRPC client, and framework middleware

Readme

@auth9/node

Node.js SDK for Auth9 - JWT verification, gRPC client, and framework middleware.

Installation

npm install @auth9/node @auth9/core
# or
pnpm add @auth9/node @auth9/core

Features

  • JWT Token Verification - JWKS-based verification with caching
  • gRPC Client - Token exchange, validation, introspection, user roles
  • M2M Authentication - Client credentials grant with auto-refresh
  • Express Middleware - Authentication + permission/role guards
  • Fastify Plugin - Token verification plugin
  • Next.js Middleware - Edge-compatible auth with header forwarding
  • Testing Utilities - Mock tokens and middleware for unit tests

Quick Start

import { Auth9 } from '@auth9/node';

const auth9 = new Auth9({
  domain: 'https://auth9.example.com',
  audience: 'my-service-client-id',
  clientId: 'service-client-id',
  clientSecret: 'service-secret', // pragma: allowlist secret
});

// Verify a JWT token
const claims = await auth9.verifyToken(token);
console.log(claims.sub, claims.email, claims.roles);

// Get M2M service token (auto-cached)
const serviceToken = await auth9.getServiceToken();

// Create gRPC client for token exchange
const grpc = auth9.grpc({ address: 'localhost:50051' });
const result = await grpc.exchangeToken({
  identityToken,
  tenantId: 'tenant-123',
  serviceId: 'my-app',
});

Token Verification

import { TokenVerifier } from '@auth9/node';

const verifier = new TokenVerifier({
  domain: 'https://auth9.example.com',
  audience: 'my-service',          // expected audience
  jwksCacheTtl: 3600,              // JWKS cache TTL in seconds (default: 3600)
  algorithms: ['RS256'],           // allowed algorithms (default: ["RS256"])
});

const { claims, tokenType } = await verifier.verify(token);
// tokenType: "identity" | "tenantAccess" | "serviceClient"

if (tokenType === 'tenantAccess') {
  console.log(claims.tenantId, claims.roles, claims.permissions);
}

Client Credentials (M2M)

import { ClientCredentials } from '@auth9/node';

const creds = new ClientCredentials({
  domain: 'https://auth9.example.com',
  clientId: 'my-service',
  clientSecret: 'my-secret', // pragma: allowlist secret
});

// Auto-cached, auto-refreshed (30s buffer before expiration)
const token = await creds.getToken();

// Force refresh
creds.clearCache();

gRPC Client

import { Auth9GrpcClient } from '@auth9/node';

const client = new Auth9GrpcClient({
  address: 'localhost:50051',
  tls: true,
  auth: {
    apiKey: 'your-api-key', // pragma: allowlist secret
    // or mTLS:
    // mtls: { cert: Buffer, key: Buffer, ca: Buffer },
  },
});

// Exchange identity token for tenant access token
const exchange = await client.exchangeToken({
  identityToken: 'id-token',
  tenantId: 'tenant-123',
  serviceId: 'my-app',
});
console.log(exchange.accessToken, exchange.expiresIn);

// Validate a token
const validation = await client.validateToken({
  accessToken: 'access-token',
  audience: 'my-service',
});
console.log(validation.valid, validation.userId);

// Get user roles and permissions
const roles = await client.getUserRoles({
  userId: 'user-123',
  tenantId: 'tenant-123',
  serviceId: 'my-app',
});
console.log(roles.roles, roles.permissions);

// Introspect token details
const introspection = await client.introspectToken({ token: 'access-token' });
console.log(introspection.active, introspection.roles);

// Clean up
client.close();

Express Middleware

import express from 'express';
import { auth9Middleware, requirePermission, requireRole } from '@auth9/node/middleware/express';

const app = express();

// Require authentication on all routes
app.use(auth9Middleware({
  domain: process.env.AUTH9_DOMAIN!,
  audience: process.env.AUTH9_AUDIENCE,
}));

// Access authenticated user info via req.auth
app.get('/api/profile', (req, res) => {
  res.json({
    userId: req.auth.userId,
    email: req.auth.email,
    roles: req.auth.roles,
  });
});

// Permission guard
app.post('/api/admin/users', requirePermission('user:write'), (req, res) => {
  res.json({ created: true });
});

// Role guard (require any)
app.get('/api/manage', requireRole(['admin', 'manager'], { mode: 'any' }), (req, res) => {
  res.json({ canManage: true });
});

// Inline permission checks
app.get('/api/dashboard', (req, res) => {
  if (req.auth.hasPermission('analytics:read')) {
    return res.json({ analytics: true });
  }
  res.status(403).json({ error: 'Forbidden' });
});

Optional Authentication

app.use(auth9Middleware({
  domain: process.env.AUTH9_DOMAIN!,
  optional: true,  // allows unauthenticated requests
}));

app.get('/api/public', (req, res) => {
  if (req.auth) {
    res.json({ greeting: `Hello ${req.auth.email}` });
  } else {
    res.json({ greeting: 'Hello guest' });
  }
});

req.auth API

req.auth.userId                          // string
req.auth.email                           // string
req.auth.tokenType                       // "identity" | "tenantAccess" | "serviceClient"
req.auth.tenantId                        // string | undefined
req.auth.roles                           // string[]
req.auth.permissions                     // string[]
req.auth.raw                             // Auth9Claims (full decoded claims)
req.auth.hasPermission('user:read')      // boolean
req.auth.hasRole('admin')                // boolean
req.auth.hasAnyPermission(['a', 'b'])    // boolean
req.auth.hasAllPermissions(['a', 'b'])   // boolean

Fastify Plugin

import fastify from 'fastify';
import { auth9Plugin } from '@auth9/node/middleware/fastify';

const app = fastify();

await app.register(auth9Plugin, {
  domain: process.env.AUTH9_DOMAIN!,
  audience: process.env.AUTH9_AUDIENCE,
});

app.get('/api/profile', async (request, reply) => {
  if (!request.auth9) {
    return reply.status(401).send({ error: 'Unauthorized' });
  }
  reply.send({
    userId: request.auth9.userId,
    isAdmin: request.auth9.hasRole('admin'),
  });
});

Next.js Middleware

// middleware.ts
import { auth9Middleware } from '@auth9/node/middleware/next';

export default auth9Middleware({
  domain: process.env.AUTH9_DOMAIN!,
  audience: process.env.AUTH9_AUDIENCE,
  publicPaths: ['/', '/login', '/api/health'],
});

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Auth info is forwarded via headers to route handlers:

// app/api/profile/route.ts
export async function GET(request: Request) {
  const userId = request.headers.get('x-auth9-user-id');
  const roles = JSON.parse(request.headers.get('x-auth9-roles') || '[]');

  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  return Response.json({ userId, roles });
}

Forwarded Headers: | Header | Content | |--------|---------| | x-auth9-user-id | User ID (sub claim) | | x-auth9-email | User email | | x-auth9-token-type | identity / tenantAccess / serviceClient | | x-auth9-tenant-id | Tenant ID (if present) | | x-auth9-roles | JSON array of roles | | x-auth9-permissions | JSON array of permissions |

Testing Utilities

import { createMockToken, createMockAuth9 } from '@auth9/node/testing';

// Create mock JWT (not cryptographically signed)
const token = createMockToken({
  sub: 'test-user',
  roles: ['admin'],
  permissions: ['user:read', 'user:write'],
});

// Create mock Auth9 instance
const mock = createMockAuth9({
  defaultUser: {
    sub: 'test-user',
    email: '[email protected]',
    roles: ['user'],
  },
});

// Use mock middleware in Express tests
const app = express();
app.use(mock.middleware());
app.get('/api/profile', (req, res) => res.json({ userId: req.auth.userId }));

// Verify mock tokens
const claims = mock.verifyToken(token);
expect(claims.sub).toBe('test-user');

Testing

pnpm test              # Run all tests
pnpm test:coverage     # With coverage

Related Packages

  • @auth9/core - Framework-agnostic HTTP client and TypeScript types

License

MIT

Links