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

@partechpartners/auth

v4.4.1

Published

Server-side helpers for Partech authentication. Install `@partechpartners/auth` and `axios`; Express apps also need `express` and `cookie-parser`.

Readme

@partechpartners/auth

Server-side helpers for Partech authentication. Install @partechpartners/auth and axios; Express apps also need express and cookie-parser.

This package is available on the public npm registry.

Existing internal services

import {
  createAuthApi,
  createAuthExpressMiddleware,
  handleExpressSSO,
} from '@partechpartners/auth';

const authApi = createAuthApi({
  internalServiceUrl: 'http://auth-v2-internal',
  publicServiceUrl: 'https://auth.partechpartners.com',
  service: 'my-service',
});

const authenticated = createAuthExpressMiddleware({
  authApi,
  applicationUrl: 'https://my-service.partechpartners.com',
});

app.use(cookieParser());
app.get('/private', authenticated({ includeUser: true }), (req, res) => {
  res.json({ email: req.user?.email });
});
app.get('/login', handleExpressSSO({ authApi }));

For return URLs, pass allowedRedirectOrigins to handleExpressSSO; it rejects unlisted origins. applicationUrl gives the middleware a trusted origin for automatic post-login redirects.

External app mode

An external app uses an app token issued by auth-v2. Store the token only in server environment variables. Never use a NEXT_PUBLIC_ variable, render the token into HTML, pass it as a browser header, or log the Axios request. The backend binds the token to one registered app; the SDK rejects requests for another service.

import { createAuthApi } from '@partechpartners/auth';

const authApi = createAuthApi({
  mode: 'external',
  externalAuthUrl: process.env.AUTH_BACKEND_URL!,
  appToken: process.env.AUTH_APP_TOKEN!,
  publicAuthUrl: process.env.AUTH_PUBLIC_URL!,
});

// Load once before using synchronous getSsoUrl() directly.
const config = await authApi.service.config();
// config.service_name and config.cookie_name come from the app token.
const ssoUrl = authApi.getSsoUrl();

The external backend supports config, session validation, and logout. authenticateSessionToken and disableSessionToken keep their existing signatures. Session validation uses the backend's normal refresh behavior, so the app makes no separate refresh call. Other auth API methods, including app-token authentication and Microsoft on-behalf-of calls, reject clearly in external mode. A valid session without the app's required roles returns 403. A rejected app token or unreachable backend fails closed with a service error; it does not start another SSO cycle.

Logout invalidates the shared production-group session, so it signs the user out of other apps sharing that cookie. Show that consequence in the app's sign-out UI.

Vercel Next.js starter

Register a routed app at the environment's shared app origin: https://apps.dev.partechpartners.com/<app> in dev, https://apps.staging.partechpartners.com/<app> in staging, or https://apps.partechpartners.com/<app> in production. The app's routing team still determines ownership and access policy, while its catalog classification determines menu grouping. Each app has its own auth-v2 service in the shared production group, which uses the p_production cookie. The app does not create its own session cookie. Existing direct-domain services may keep their registered URLs.

In Vercel, set these server-only environment variables for the selected deployment. The shared app host points to the app router, which forwards the app path to the Vercel target origin:

| Variable | Production value | | --- | --- | | AUTH_BACKEND_URL | https://auth.partechpartners.com/v2 | | AUTH_PUBLIC_URL | https://auth.partechpartners.com/v2 | | AUTH_APP_TOKEN | The one-time token value issued for this registered app by auth-v2 |

Use the corresponding auth-v2 base URL and token in other environments. Both URLs include /v2: the SDK appends /api/external-auth/* for backend calls and /oidc/{service_name} for browser SSO. Keep AUTH_APP_TOKEN out of NEXT_PUBLIC_ variables, browser code, response headers, logs, and build-time public assets. The SDK sends it only from the server to auth-v2. The registered app can require roles; auth-v2 enforces those roles during session validation, so a signed-in user without access receives 403.

// middleware.ts — executes on the server/edge, never in a Client Component
import { NextResponse, type NextRequest } from 'next/server';
import { authNextMiddleware } from '@partechpartners/auth/next';

export async function middleware(request: NextRequest) {
  const response = NextResponse.next();
  const result = await authNextMiddleware(request, response, {
    AuthApiParams: {
      mode: 'external',
      externalAuthUrl: process.env.AUTH_BACKEND_URL!,
      appToken: process.env.AUTH_APP_TOKEN!,
      publicAuthUrl: process.env.AUTH_PUBLIC_URL!,
    },
    applicationUrl: 'https://apps.partechpartners.com/my-app',
    publicRoutes: [],
  });
  return result ?? response;
}

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

The auth middleware handles /auth/logout, /auth/switch-account, and /auth/insufficient-permissions. Keep them inside the matcher. The public app URL must match the registered URL; avoid preview-host redirects for the shared production cookie. On Vercel, use applicationUrl so redirects cannot derive an origin from a request header.

For a routed app at https://apps.dev.partechpartners.com/expenses, build Next.js with basePath: '/expenses' and pass the full public URL as applicationUrl. Next.js removes the base path from request.nextUrl.pathname in middleware; the SDK uses the trusted public URL for login, logout, and return redirects and forwards it for server-side URL reconstruction. basePath may also be passed explicitly, but it must match the path in applicationUrl. The router must preserve /expenses when forwarding to the target origin. Keep the matcher on all pages and APIs. If target ownership verification is required, exempt only the exact /.well-known/partech-app-verification pathname and serve the registry's challenge as plain text there.

authNextMiddleware creates an auth client for each invocation, so its service config may be fetched on every request. The 15-minute config cache applies only within one createAuthApi instance; it is not a shared cache across Next.js requests.

The sign-out control can link to /auth/logout. Tell users that this invalidates the shared session for other apps in the production group too. The middleware never forwards AUTH_APP_TOKEN to the browser; it forwards only validated user identity to the protected application request.

Express external app

import cookieParser from 'cookie-parser';
import {
  createAuthApi,
  createAuthExpressMiddleware,
  handleExpressSSO,
} from '@partechpartners/auth';

const authApi = createAuthApi({
  mode: 'external',
  externalAuthUrl: process.env.AUTH_BACKEND_URL!,
  appToken: process.env.AUTH_APP_TOKEN!,
  publicAuthUrl: process.env.AUTH_PUBLIC_URL!,
});
const authenticated = createAuthExpressMiddleware({
  authApi,
  applicationUrl: 'https://apps.partechpartners.com/my-app',
  token: false,
});
app.use(cookieParser());
app.get('/login', handleExpressSSO({ authApi }));
app.get('/private', authenticated({ includeUser: true, redirectToSSOIfNotAuthenticated: true }), (req, res) => {
  res.json({ email: req.user?.email });
});

MCP Resource Server

Use the explicit MCP subpath for MCP Resource Server wiring:

import {
  createNextMcpHandlers,
  defineMcpTool,
  mountExpressMcpResourceServer,
  type McpPrincipal,
} from '@partechpartners/auth/mcp';

Do not import MCP helpers from @partechpartners/auth or @partechpartners/auth/next. The MCP subpath is ESM-only because the MCP SDK is ESM-first. Root @partechpartners/auth and @partechpartners/auth/next stay separate so non-MCP consumers do not load MCP SDK runtime or type constraints.

MCP projects must install the MCP SDK peer:

yarn add @partechpartners/auth @modelcontextprotocol/sdk

If the tool schema uses Zod, also install your app's Zod version:

yarn add zod

Import safety

@partechpartners/auth/mcp is safe for Next App Router projects and ESM-compatible Express projects:

  • The ./mcp package export has its own types and import entry.
  • The generated MCP declarations do not import Express types; Next projects do not need express installed just to import @partechpartners/auth/mcp.
  • The root . and ./next exports do not re-export MCP helpers, so regular auth consumers do not inherit MCP SDK constraints.
  • Because ./mcp intentionally has no require condition, CommonJS require('@partechpartners/auth/mcp') is not supported. CommonJS Express apps must load it with dynamic import() during startup or move the MCP entrypoint to ESM.

Express example

import express from 'express';
import { createAuthApi } from '@partechpartners/auth';
import {
  defineMcpTool,
  mountExpressMcpResourceServer,
} from '@partechpartners/auth/mcp';
import { z } from 'zod';

const authApi = createAuthApi({
  internalServiceUrl: process.env.AUTH_INTERNAL_URL!,
  publicServiceUrl: process.env.AUTH_PUBLIC_URL!,
  service: process.env.AUTH_SERVICE_NAME!,
});

type SearchDocumentsInput = {
  q?: string;
  limit?: number;
  includes_metadata?: boolean;
  includes_facets?: boolean;
};

const searchDocuments = defineMcpTool<SearchDocumentsInput>({
  name: 'search_documents',
  title: 'Search Documents',
  description: 'Search documents using permission-aware filters.',
  requiredScopes: ['edm:documents:search'],
  inputSchema: {
    q: z.string().optional(),
    limit: z.coerce.number().int().min(1).max(25).default(10),
    includes_metadata: z.boolean().default(true),
    includes_facets: z.boolean().default(false),
  },
  handler: async ({ input, principal }) => {
    return {
      documents: [],
      found: 0,
      has_more: false,
      user_id: principal.userId,
      query: input.q ?? null,
      limit: input.limit ?? 10,
    };
  },
});

const app = express();
app.use(express.json());

mountExpressMcpResourceServer(app, {
  enabled: process.env.MCP_ENABLED !== 'false',
  path: process.env.MCP_PATH ?? '/mcp',
  resourceUrl: process.env.MCP_PUBLIC_RESOURCE_URL!,
  resourceName: process.env.MCP_RESOURCE_NAME ?? 'Documents MCP',
  issuerUrl: process.env.AUTH_PUBLIC_URL!,
  authApi,
  requiredRoles: ['EDM', 'ADMIN_EDM', 'SUPER_ADMIN'],
  server: {
    name: 'documents-mcp',
    version: '1.0.0',
  },
  tools: [searchDocuments],
});

The helper mounts:

  • GET /.well-known/oauth-protected-resource/<mcp path>
  • GET /.well-known/oauth-authorization-server
  • POST <mcp path>
  • authenticated GET and DELETE <mcp path> method-not-allowed responses

When the service is published through mcp-edge, keep path as the internal mount path, usually /mcp, but set resourceUrl to the public edge resource, for example https://mcp.dev.partechpartners.com/edm. The auth-v2 MCP Server registry row must use the same generated resource URL and an upstream_url pointing to the internal MCP endpoint.

Next App Router example

Create shared MCP handlers in server-only code:

// src/lib/mcp.ts
import { createAuthApi } from '@partechpartners/auth';
import {
  createNextMcpHandlers,
  defineMcpTool,
} from '@partechpartners/auth/mcp';
import { z } from 'zod';

const authApi = createAuthApi({
  internalServiceUrl: process.env.AUTH_INTERNAL_URL!,
  publicServiceUrl: process.env.AUTH_PUBLIC_URL!,
  service: process.env.AUTH_SERVICE_NAME!,
});

type SearchDocumentsInput = {
  q?: string;
  limit?: number;
  includes_metadata?: boolean;
  includes_facets?: boolean;
};

const searchDocuments = defineMcpTool<SearchDocumentsInput>({
  name: 'search_documents',
  title: 'Search Documents',
  description: 'Search documents using permission-aware filters.',
  requiredScopes: ['edm:documents:search'],
  inputSchema: {
    q: z.string().optional(),
    limit: z.coerce.number().int().min(1).max(25).default(10),
    includes_metadata: z.boolean().default(true),
    includes_facets: z.boolean().default(false),
  },
  handler: async ({ input, principal }) => {
    return {
      documents: [],
      found: 0,
      has_more: false,
      user_id: principal.userId,
      query: input.q ?? null,
      limit: input.limit ?? 10,
    };
  },
});

export const mcpHandlers = createNextMcpHandlers({
  enabled: process.env.MCP_ENABLED !== 'false',
  path: process.env.MCP_PATH ?? '/mcp',
  resourceUrl: process.env.MCP_PUBLIC_RESOURCE_URL!,
  resourceName: process.env.MCP_RESOURCE_NAME ?? 'Documents MCP',
  issuerUrl: process.env.AUTH_PUBLIC_URL!,
  authApi,
  requiredRoles: ['EDM', 'ADMIN_EDM', 'SUPER_ADMIN'],
  server: {
    name: 'documents-mcp',
    version: '1.0.0',
  },
  tools: [searchDocuments],
});

Expose the MCP route:

// src/app/mcp/route.ts
import { mcpHandlers } from '@/lib/mcp';

export const GET = mcpHandlers.GET;
export const POST = mcpHandlers.POST;
export const DELETE = mcpHandlers.DELETE;

Expose protected-resource metadata for /mcp:

// src/app/.well-known/oauth-protected-resource/mcp/route.ts
import { mcpHandlers } from '@/lib/mcp';

export const GET = mcpHandlers.metadata.GET;

Expose OAuth server metadata:

// src/app/.well-known/oauth-authorization-server/route.ts
import { mcpHandlers } from '@/lib/mcp';

export const GET = mcpHandlers.metadata.GET;

For a resource URL such as https://api.example.com/mcp, the protected resource metadata URL is:

https://api.example.com/.well-known/oauth-protected-resource/mcp

The resourceUrl value must match the MCP Server registry row in auth-v2. With mcp-edge, that value is the public edge URL, not the internal upstream URL.