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

@ldauth/node

v0.10.1

Published

Node.js client library for ldauth permission checking

Readme

@ldauth/node

Node.js client library for ldauth permission checking. This library provides a simple way to integrate permission checking into your Node.js applications using the ldauth authentication and authorization service.

Features

  • 🔐 Simple permission checking against ldauth API
  • ⚡ Built-in caching for improved performance
  • 🎯 Framework integrations for Fastify, Express, and tRPC
  • 🚀 Automatic batching for tRPC to minimize API calls
  • 🔍 Optional local JWT validation
  • 💪 TypeScript support with full type definitions
  • 🛡️ Comprehensive error handling

Installation

npm install @ldauth/node
# or
yarn add @ldauth/node
# or
pnpm add @ldauth/node

Quick Start

Basic Usage

import { LDAuthClient } from '@ldauth/node';

const ldauth = new LDAuthClient({
  apiUrl: 'https://auth.example.com',
  defaultSchema: 'myapp',
  cacheTimeSeconds: 5 // Cache results for 5 seconds
});

// Check a permission
try {
  await ldauth.checkPermission(token, {
    resource: 'users',
    action: 'read'
  });
  console.log('Permission granted!');
} catch (error) {
  console.error('Permission denied');
}

// Get all user permissions
const permissions = await ldauth.getUserPermissions(token);
console.log(permissions);
// { permissions: { users: ['read', 'write'], posts: ['read'] } }

// Get user info
const userInfo = await ldauth.getUserInfo(token);
console.log(userInfo);
// { id: '123', email: '[email protected]', groups: ['admin'] }

Fastify Integration

import fastify from 'fastify';
import { ldauthFastify } from '@ldauth/node/fastify';

const app = fastify();

// Register the plugin
await app.register(ldauthFastify, {
  apiUrl: 'https://auth.example.com',
  defaultSchema: 'myapp',
  validateTokenLocally: true, // Validate JWT before calling API
  cacheTimeSeconds: 5
});

// Simple permission check
app.get('/users', {
  preHandler: app.ldauth.require('users', 'read')
}, async (request, reply) => {
  return { users: [] };
});

// With user info populated
app.get('/profile', {
  preHandler: [
    app.ldauth.require('profile', 'view'),
    app.ldauth.populateUser()
  ]
}, async (request, reply) => {
  return { 
    email: request.user.email,
    groups: request.user.groups 
  };
});

// Check multiple permissions (AND)
app.post('/admin', {
  preHandler: app.ldauth.requireAll([
    ['admin', 'access'],
    ['settings', 'write']
  ])
}, async (request, reply) => {
  return { success: true };
});

// Check multiple permissions (OR)
app.put('/content', {
  preHandler: app.ldauth.requireAny([
    ['content', 'edit'],
    ['admin', 'access']
  ])
}, async (request, reply) => {
  return { success: true };
});

Express Integration

import express from 'express';
import { ldauthExpress } from '@ldauth/node/express';

const app = express();

// Create middleware factory
const ldauth = ldauthExpress({
  apiUrl: 'https://auth.example.com',
  defaultSchema: 'myapp',
  validateTokenLocally: true,
  cacheTimeSeconds: 5
});

// Simple permission check
app.get('/users', 
  ldauth.require('users', 'read'),
  (req, res) => {
    res.json({ users: [] });
  }
);

// With user info populated
app.get('/profile',
  ldauth.require('profile', 'view'),
  ldauth.populateUser(),
  (req, res) => {
    res.json({ 
      email: req.user.email,
      groups: req.user.groups 
    });
  }
);

// Check multiple permissions (AND)
app.post('/admin',
  ldauth.requireAll([
    ['admin', 'access'],
    ['settings', 'write']
  ]),
  (req, res) => {
    res.json({ success: true });
  }
);

// Check multiple permissions (OR)
app.put('/content',
  ldauth.requireAny([
    ['content', 'edit'],
    ['admin', 'access']
  ]),
  (req, res) => {
    res.json({ success: true });
  }
);

// Global middleware for all /api routes
app.use('/api', ldauth.populateUser());

tRPC Integration

import { initTRPC } from '@trpc/server';
import { createLDAuthMiddleware, PermissionBatcher } from '@ldauth/node/trpc';
import { LDAuthClient } from '@ldauth/node';

// Initialize tRPC
const t = initTRPC.context().create();

// Create LDAuth client and batcher for automatic batching
const ldauthClient = new LDAuthClient({
  apiUrl: 'https://auth.example.com',
  audience: 'https://api.example.com',
  defaultSchemaName: 'myapp',
  cacheTimeSeconds: 5
});

const batcher = new PermissionBatcher(ldauthClient);

// Create the middleware
const authMiddleware = createLDAuthMiddleware({
  client: batcher,
  defaultSchemaName: 'myapp'
});

// Create a protected procedure
const protectedProcedure = t.procedure.use(authMiddleware);

// Define your router
export const appRouter = t.router({
  // Simple permission check
  getUsers: protectedProcedure
    .meta({
      permissions: {
        resource: 'users',
        action: 'read'
      }
    })
    .query(async ({ ctx }) => {
      // Permission automatically checked
      // Token available in ctx.token
      return { users: [] };
    }),

  // Require all permissions (AND logic)
  adminAction: protectedProcedure
    .meta({
      requireAll: [
        { resource: 'admin', action: 'access' },
        { resource: 'settings', action: 'write' }
      ]
    })
    .mutation(async ({ ctx, input }) => {
      return { success: true };
    }),

  // Require any permission (OR logic)
  editContent: protectedProcedure
    .meta({
      requireAny: [
        { resource: 'content', action: 'edit' },
        { resource: 'admin', action: 'access' }
      ]
    })
    .mutation(async ({ ctx, input }) => {
      return { success: true };
    }),

  // Object-level permission
  editPost: protectedProcedure
    .meta({
      permissions: {
        resource: 'posts',
        action: 'edit',
        instanceId: 'post-123' // Check for specific post
      }
    })
    .mutation(async ({ ctx, input }) => {
      return { success: true };
    }),

  // No permissions required (public endpoint)
  getPublicData: t.procedure
    .query(async () => {
      return { data: 'public' };
    })
});

export type AppRouter = typeof appRouter;

Note: When tRPC batches multiple procedures in one HTTP request, all permission checks are automatically batched into one or more API calls (grouped by token), significantly improving performance.

Configuration

Client Options

interface LDAuthClientConfig {
  // Base URL of your ldauth API gateway
  apiUrl?: string; // Can use LDAUTH_API_URL env var
  
  // Default schema for permission checks
  defaultSchema?: string;
  
  // Default profile (defaults to '__default')
  defaultProfile?: string;
  
  // Validate JWT locally before API call
  validateTokenLocally?: boolean; // Default: false

  // Cache results for this many seconds
  cacheTimeSeconds?: number; // Default: 0 (no cache)
  
  // API request timeout in milliseconds
  timeout?: number; // Default: 10000
  
  // Custom headers for API requests
  headers?: Record<string, string>;
}

Environment Variables

  • LDAUTH_API_URL - Set the API URL via environment variable instead of config

Error Handling

The library provides specific error classes for different scenarios:

import { 
  LDAuthPermissionError,
  LDAuthTokenError,
  LDAuthAPIError,
  LDAuthConfigError 
} from '@ldauth/node';

try {
  await ldauth.checkPermission(token, {
    resource: 'users',
    action: 'delete'
  });
} catch (error) {
  if (error instanceof LDAuthPermissionError) {
    // Permission was denied
    console.log('Required permission:', error.requiredPermission);
  } else if (error instanceof LDAuthTokenError) {
    // Token is invalid or expired
    console.log('Token error:', error.message);
  } else if (error instanceof LDAuthAPIError) {
    // API communication failed
    console.log('API error:', error.statusCode, error.message);
  }
}

Express Error Handler

app.use((err, req, res, next) => {
  if (err.name === 'LDAuthPermissionError') {
    res.status(403).json({ 
      error: 'Permission denied',
      required: err.requiredPermission 
    });
  } else if (err.name === 'LDAuthTokenError') {
    res.status(401).json({ 
      error: 'Invalid token' 
    });
  } else {
    next(err);
  }
});

Advanced Usage

Custom Token Extraction

// Fastify
await app.register(ldauthFastify, {
  apiUrl: 'https://auth.example.com',
  extractToken: (request) => {
    // Get token from custom header or cookie
    return request.headers['x-auth-token'] || 
           request.cookies?.authToken;
  }
});

// Express
const ldauth = ldauthExpress({
  apiUrl: 'https://auth.example.com',
  extractToken: (req) => {
    // Get token from custom header or cookie
    return req.headers['x-auth-token'] || 
           req.cookies?.authToken;
  }
});

Object-Level Permissions

// Check permission for a specific resource instance
await ldauth.checkPermission(token, {
  resource: 'posts',
  action: 'edit',
  instanceId: 'post-123' // Check for this specific post
});

Custom Schema and Profile

// Override default schema and profile
await ldauth.checkPermission(token, {
  resource: 'admin',
  action: 'access',
  schema: 'admin-portal',
  profile: 'elevated'
});

API Reference

LDAuthClient

checkPermission(token, options)

Check if the user has a specific permission.

getUserPermissions(token, options?)

Get all permissions for the user.

getUserInfo(token)

Get user information from the token.

getClientIdFromToken(token)

Extract client ID from token without validation.

clearCache()

Clear all cached results.

Framework Integrations

Fastify and Express integrations provide:

  • require(resource, action, options?) - Require a single permission
  • requireAll(permissions) - Require all specified permissions
  • requireAny(permissions) - Require at least one permission
  • populateUser() - Add user info to request
  • populatePermissions(options?) - Add all permissions to request

tRPC integration provides:

  • Permission checking via procedure .meta() configuration
  • permissions - Single permission requirement
  • requireAll - Require all specified permissions (AND logic)
  • requireAny - Require any of the specified permissions (OR logic)
  • Automatic batching of permission checks across procedures
  • Token automatically added to context after successful validation

License

MIT