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

@localpro/auth-access

v1.0.4

Published

Shared authentication & authorization middleware for LocalPro services

Readme

@localpro/auth-access

Shared authentication & authorization middleware for LocalPro services.

Full TypeScript support included - Works seamlessly with React.js, Next.js, and TypeScript projects.

Features

  • ✅ JWT token management with RS256 (asymmetric signing)
  • ✅ Role-Based Access Control (RBAC) with scopes
  • ✅ Express.js middleware
  • ✅ Fastify middleware
  • ✅ GraphQL/Apollo Server integration
  • ✅ Token refresh flows

Installation

npm install @localpro/auth-access

TypeScript Support

Full TypeScript definitions are included. No additional @types package needed!

import { initAuth, authMiddleware, TokenPayload } from '@localpro/auth-access';

The package works seamlessly with TypeScript, React.js, and Next.js projects. All exports are fully typed for better IDE support and type safety.

Quick Start

1. Initialize

const { initAuth } = require('@localpro/auth-access');

// Initialize once at app startup
initAuth({
  issuer: process.env.AUTH_ISSUER || 'localpro',
  privateKey: process.env.AUTH_PRIVATE_KEY, // For issuing tokens
  publicKey: process.env.AUTH_PUBLIC_KEY,   // For validating tokens
  defaultExpiresIn: '1h'
});

2. Express.js Usage

JavaScript:

const express = require('express');
const { initAuth, authMiddleware, requireRole } = require('@localpro/auth-access');

const app = express();

// Initialize and attach to app
const authAccess = initAuth({
  issuer: process.env.AUTH_ISSUER,
  publicKey: process.env.AUTH_PUBLIC_KEY
});

app.locals.authAccess = authAccess;

// Apply to all routes
app.use(authMiddleware());

// Or apply to specific routes with role requirements
app.get('/admin/data', requireRole('admin'), (req, res) => {
  res.json({ data: 'admin content', user: req.user });
});

// Access user info
app.get('/profile', authMiddleware(), (req, res) => {
  res.json({
    partnerId: req.partnerId,
    role: req.role,
    scopes: req.scopes
  });
});

TypeScript:

import express, { Response } from 'express';
import { 
  initAuth, 
  authMiddleware, 
  requireRole,
  ExpressRequest 
} from '@localpro/auth-access';

const app = express();

// Initialize with type safety
const authAccess = initAuth({
  issuer: process.env.AUTH_ISSUER,
  publicKey: process.env.AUTH_PUBLIC_KEY
});

app.locals.authAccess = authAccess;

// Type-safe middleware
app.use(authMiddleware());

// Type-safe route handlers - TypeScript knows about req.user, req.partnerId, etc.
app.get('/profile', authMiddleware(), (req: ExpressRequest, res: Response) => {
  res.json({
    partnerId: req.partnerId,  // TypeScript autocomplete works!
    role: req.role,
    scopes: req.scopes,
    user: req.user
  });
});

// Role-based routes with type safety
app.get('/admin/data', requireRole('admin'), (req: ExpressRequest, res: Response) => {
  res.json({ data: 'admin content', user: req.user });
});

3. Fastify Usage

const fastify = require('fastify');
const { initAuth, authPlugin } = require('@localpro/auth-access');

const app = fastify();

// Initialize
const authAccess = initAuth({
  issuer: process.env.AUTH_ISSUER,
  publicKey: process.env.AUTH_PUBLIC_KEY,
});

// Attach to Fastify instance
app.decorate('authAccess', authAccess);

// Register auth plugin
app.register(authPlugin());

// Routes automatically have access to request.user
app.get('/profile', async (request, reply) => {
  return {
    partnerId: request.partnerId,
    role: request.role,
    scopes: request.scopes
  };
});

4. GraphQL/Apollo Server Usage

JavaScript:

const { ApolloServer } = require('apollo-server-express');
const { initAuth, authGraphQL } = require('@localpro/auth-access');

// Initialize
initAuth({
  issuer: process.env.AUTH_ISSUER,
  publicKey: process.env.AUTH_PUBLIC_KEY
});

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: authGraphQL()
});

// In resolvers
const resolvers = {
  Query: {
    myData: (parent, args, context) => {
      if (!context || !context.user) {
        throw new Error('Authentication required');
      }
      
      return {
        partnerId: context.partnerId,
        data: 'your data here'
      };
    }
  }
};

TypeScript:

import { ApolloServer } from 'apollo-server-express';
import { initAuth, authGraphQL, GraphQLContext, graphqlCheckScopes } from '@localpro/auth-access';

// Initialize
initAuth({
  issuer: process.env.AUTH_ISSUER,
  publicKey: process.env.AUTH_PUBLIC_KEY
});

const server = new ApolloServer({
  typeDefs,
  resolvers: {
    Query: {
      myData: (parent: any, args: any, context: GraphQLContext) => {
        if (!context || !context.user) {
          throw new Error('Authentication required');
        }
        
        // Type-safe access to context properties
        return {
          partnerId: context.partnerId,
          role: context.role,
          scopes: context.scopes,
          data: 'your data here'
        };
      },
      analytics: (parent: any, args: any, context: GraphQLContext) => {
        // Type-safe scope checking
        graphqlCheckScopes(context, ['read:analytics']);
        return { views: 1000, conversions: 50 };
      }
    }
  },
  context: authGraphQL()
});

5. Next.js Usage

API Routes (Pages Router):

// pages/api/protected.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { validateToken, TokenPayload } from '@localpro/auth-access';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const authHeader = req.headers.authorization;
  
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  const token = authHeader.substring(7);
  
  try {
    const decoded: TokenPayload = await validateToken(token);
    return res.json({
      partnerId: decoded.partnerId,
      role: decoded.role,
      scopes: decoded.scopes
    });
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
}

App Router:

// app/api/protected/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { validateToken, TokenPayload } from '@localpro/auth-access';

export async function GET(request: NextRequest) {
  const authHeader = request.headers.get('authorization');
  
  if (!authHeader?.startsWith('Bearer ')) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const token = authHeader.substring(7);
  
  try {
    const decoded: TokenPayload = await validateToken(token);
    return NextResponse.json({
      partnerId: decoded.partnerId,
      role: decoded.role,
      scopes: decoded.scopes
    });
  } catch (error) {
    return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
  }
}

6. React.js Usage

// hooks/useAuth.ts
import { useState, useEffect } from 'react';
import { validateToken, TokenPayload } from '@localpro/auth-access';

export function useAuth() {
  const [user, setUser] = useState<TokenPayload | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const token = localStorage.getItem('authToken');
    
    if (token) {
      validateToken(token)
        .then((decoded: TokenPayload) => {
          setUser(decoded);
          setLoading(false);
        })
        .catch(() => {
          setUser(null);
          setLoading(false);
        });
    } else {
      setLoading(false);
    }
  }, []);

  return { user, loading };
}

API Reference

Initialization

initAuth(config)

Initialize the auth access system.

Parameters:

  • config.issuer (string, optional): Token issuer (default: 'localpro')
  • config.privateKey (string, optional): Private key for signing tokens
  • config.publicKey (string, optional): Public key for verifying tokens
  • config.algorithm (string, optional): JWT algorithm (default: 'RS256')
  • config.defaultExpiresIn (string, optional): Default token expiration (default: '1h')

Returns: Auth access instance

Token Management

issueToken(payload, options)

Issue a new JWT access token.

JavaScript:

const { issueToken } = require('@localpro/auth-access');

const token = await issueToken({
  partnerId: 'partner-123',
  role: 'partner:premium',
  scopes: ['read:analytics', 'write:services']
}, {
  expiresIn: '2h'
});

TypeScript:

import { issueToken, TokenPayload, TokenOptions } from '@localpro/auth-access';

const payload: TokenPayload = {
  partnerId: 'partner-123',
  role: 'partner:premium',
  scopes: ['read:analytics', 'write:services']
};

const options: TokenOptions = {
  expiresIn: '2h'
};

const token = await issueToken(payload, options);

Parameters:

  • payload.partnerId (string, required): Partner ID
  • payload.role (string, required): Role (client, partner:basic, partner:premium, admin)
  • payload.scopes (string[], optional): Additional scopes
  • options.expiresIn (string, optional): Expiration time

Returns: JWT token string

validateToken(token, options)

Validate and decode a JWT token.

JavaScript:

const { validateToken } = require('@localpro/auth-access');

try {
  const decoded = await validateToken(token);
  console.log(decoded.partnerId, decoded.role);
} catch (error) {
  console.error('Invalid token:', error.message);
}

TypeScript:

import { validateToken, TokenPayload } from '@localpro/auth-access';

try {
  const decoded: TokenPayload = await validateToken(token);
  console.log(decoded.partnerId, decoded.role, decoded.scopes);
} catch (error) {
  console.error('Invalid token:', error);
}

Returns: Decoded token payload

refreshToken(oldToken, options)

Refresh a token (issue new token with same payload but new expiration).

const { refreshToken } = require('@localpro/auth-access');

const newToken = await refreshToken(oldToken, {
  expiresIn: '1h'
});

Scope Management

checkScopes(tokenPayload, scopes)

Check if token has required scopes.

JavaScript:

const { validateToken, checkScopes } = require('@localpro/auth-access');

const decoded = await validateToken(token);
if (!checkScopes(decoded, ['read:analytics'])) {
  throw new Error('Insufficient permissions');
}

TypeScript:

import { validateToken, checkScopes, TokenPayload } from '@localpro/auth-access';

const decoded: TokenPayload = await validateToken(token);
if (!checkScopes(decoded, ['read:analytics'])) {
  throw new Error('Insufficient permissions');
}

Returns: boolean

Express Middleware

authMiddleware(options)

Create Express authentication middleware.

JavaScript:

const { authMiddleware } = require('@localpro/auth-access');

// Basic usage
app.use(authMiddleware());

// With options
app.use(authMiddleware({
  requiredScopes: ['read:services'],
  requiredRole: 'partner:premium'
}));

TypeScript:

import { authMiddleware, ExpressRequest } from '@localpro/auth-access';
import { Response } from 'express';

// Basic usage
app.use(authMiddleware());

// With options and type-safe request
app.use(authMiddleware({
  requiredScopes: ['read:services'],
  requiredRole: 'partner:premium'
}));

// Type-safe route handler
app.get('/profile', authMiddleware(), (req: ExpressRequest, res: Response) => {
  // TypeScript knows req.user, req.partnerId, req.role, req.scopes
  res.json({ partnerId: req.partnerId, role: req.role });
});

requireScopes(scopes)

Require specific scopes.

const { requireScopes } = require('@localpro/auth-access');

app.get('/analytics', requireScopes(['read:analytics']), (req, res) => {
  res.json({ analytics: 'data' });
});

requireRole(role)

Require specific role.

const { requireRole } = require('@localpro/auth-access');

app.get('/admin/users', requireRole('admin'), (req, res) => {
  res.json({ users: [] });
});

GraphQL Helpers

checkScopes(context, requiredScopes)

Check scopes in GraphQL resolvers.

JavaScript:

const { graphqlCheckScopes } = require('@localpro/auth-access');

const resolvers = {
  Query: {
    analytics: (parent, args, context) => {
      graphqlCheckScopes(context, ['read:analytics']);
      return { data: 'analytics' };
    }
  }
};

TypeScript:

import { graphqlCheckScopes, GraphQLContext } from '@localpro/auth-access';

const resolvers = {
  Query: {
    analytics: (parent: any, args: any, context: GraphQLContext) => {
      graphqlCheckScopes(context, ['read:analytics']);
      return { data: 'analytics' };
    }
  }
};

checkRole(context, requiredRole)

Check role in GraphQL resolvers.

JavaScript:

const { graphqlCheckRole } = require('@localpro/auth-access');

const resolvers = {
  Query: {
    adminData: (parent, args, context) => {
      graphqlCheckRole(context, 'admin');
      return { data: 'admin data' };
    }
  }
};

TypeScript:

import { graphqlCheckRole, GraphQLContext } from '@localpro/auth-access';

const resolvers = {
  Query: {
    adminData: (parent: any, args: any, context: GraphQLContext) => {
      graphqlCheckRole(context, 'admin');
      return { data: 'admin data' };
    }
  }
};

Roles and Scopes

Supported Roles

  • client - Basic client access
  • partner:basic - Basic partner access
  • partner:premium - Premium partner access
  • admin - Administrative access (all scopes)

Default Scopes by Role

  • client: read:own, write:own
  • partner:basic: read:own, write:own, read:services, write:services
  • partner:premium: All basic scopes + read:analytics, write:analytics
  • admin: * (all scopes)

Custom Scopes

You can add custom scopes when issuing tokens:

const token = await issueToken({
  partnerId: 'partner-123',
  role: 'partner:premium',
  scopes: ['custom:scope1', 'custom:scope2']
});

Security Best Practices

  1. Use RS256 (Asymmetric Keys): Always use RS256 for signing tokens
  2. Rotate Keys Safely: Implement key rotation strategy
  3. Validate on Every Request: Never skip token validation
  4. Log Failed Auth Attempts: Monitor and log authentication failures
  5. Set Appropriate Expiration: Use short-lived tokens (1h default)
  6. Use HTTPS: Always use HTTPS in production

Environment Variables

AUTH_ISSUER=localpro
AUTH_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
AUTH_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----\n...

Error Handling

The middleware throws specific errors:

  • 401 Unauthorized: Missing or invalid token
  • 403 Forbidden: Insufficient permissions or wrong role
app.use((err, req, res, next) => {
  if (err.name === 'JsonWebTokenError') {
    return res.status(401).json({ error: 'Invalid token' });
  }
  if (err.name === 'TokenExpiredError') {
    return res.status(401).json({ error: 'Token expired' });
  }
  next(err);
});

Examples

Express App with Multiple Middleware

const express = require('express');
const { initAuth, authMiddleware, requireRole } = require('@localpro/auth-access');

const app = express();

// Initialize
const authAccess = initAuth({
  issuer: process.env.AUTH_ISSUER,
  publicKey: process.env.AUTH_PUBLIC_KEY,
});
app.locals.authAccess = authAccess;

// Public routes
app.get('/health', (req, res) => res.json({ status: 'ok' }));

// Protected routes
app.use('/api', authMiddleware());

// Tier-specific routes
app.get('/api/premium', requireRole('partner:premium'), (req, res) => {
  res.json({ data: 'premium' });
});

// Role-specific routes
app.get('/api/admin', requireRole('admin'), (req, res) => {
  res.json({ data: 'admin' });
});

app.listen(3000);

Token Issuance Service

const { initAuth, issueToken } = require('@localpro/auth-access');

// Initialize with private key for signing
initAuth({
  issuer: 'localpro',
  privateKey: process.env.AUTH_PRIVATE_KEY
});

// Issue token for partner
async function createPartnerToken(partnerId, role, scopes = []) {
  return await issueToken({
    partnerId,
    role,
    scopes
  }, {
    expiresIn: '24h'
  });
}

License

MIT

Support

For issues and questions, please contact support or open an issue on GitHub.