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

@debito/zeko-auth-guard

v0.4.0

Published

Tiny shared Hapi auth guard for Zeko services — authenticates + authorizes via uauth

Downloads

6,262

Readme

@debito/zeko-auth-guard

Tiny shared Hapi auth guard for Zeko API services. It authenticates the caller and enforces app authorization by asking uauth (user_authentication_service) — the single source of truth — over HTTP, on every request. uauth resolves the session in Redis, so a terminated device fails on its next call.

What it does

Per request to a protected route:

  1. Read the auth_token cookie (the plugin registers server.state('auth_token', …) so it's never forgotten).
  2. Call uauth GET /auth/authorize?appId=… forwarding the cookie (one call per request).
  3. Map the result to Hapi auth: 401 (no/invalid/expired token, or requireOrg and no org), 403 (app disabled for org or user lacks access), else allow — attaching { userId, user: { id, name }, orgId, roles, permissions, is_support, authToken } to request.auth.credentials.

Service-to-service calls

Internal callers send an X-Service-Token header (a signed service JWT). A valid service token skips the per-user app gate — the downstream trusts the calling service. Credentials then include { isService: true, service }. When the caller also forwards the user's auth_token cookie (the proxy always does), they carry userId, orgId and user: { id, name } as well (0.2.1), so a service acting for a person can record who did it exactly as on a direct user request.

Usage

import { authGuard } from '@debito/zeko-auth-guard';

await server.register({
    plugin: authGuard,
    options: { appId: 'vm', requireOrg: true } // omit appId for identity-only (infra services)
});

// protect a route
server.route({
    method: 'GET',
    path: '/vendors',
    options: { auth: 'auth-guard' },
    handler: (request, h) => {
        const { userId, orgId, authToken } = request.auth.credentials;
        // ...
    }
});

Options

| option | default | meaning | |--------|---------|---------| | appId | null | app to authorize against; null = authenticate only (infra services) | | requireOrg | false | reject tokens without an orgId |

Env vars: UAUTH_API_URL (uauth base URL) and SERVICE_JWT_SECRET (verifies X-Service-Token).

The door out: createProxyRoutes

An app talks only to its own API. Anything it needs from another Zeko service goes through one route this package provides:

import { authGuard, createProxyRoutes } from '@debito/zeko-auth-guard';

server.route(createProxyRoutes('tasks', { crm: process.env.CRM_API_URL }));
// GET /proxy/crm/customers?x=1   ==>   GET {CRM_API_URL}/customers?x=1

The map is the whole list of what this app may reach; anything else answers unknown_service. Method, query, body and the upstream's status and JSON pass through. Every call carries an X-Service-Token signed on the fly from SERVICE_JWT_SECRET ({ type: 'service', service: '<appId>' }) plus the caller's auth_token cookie, so the far guard trusts the service and still resolves the user's org. No token is stored anywhere; the secret is the only thing a service holds.

The trail: createActivityClient

A service reports what it did to zeko-activity-api through one client this package provides (0.3.1), one call per mutation, after its own write succeeded:

import { createActivityClient } from '@debito/zeko-auth-guard';

const activity = createActivityClient('hippo', process.env.ACTIVITY_API_URL);
await activity.post(request, {
    action: 'account.update',                                  // <entity>.<verb>
    target: { type: 'account', id: 'account-bank', label: 'Bank', path: '/ledger/bank' },
    scope: { book: bookId },                                   // optional, any shape
    change: { before: { label: 'Bank' }, after: { label: 'Bank QA' } }  // optional, any shape
});

It signs X-Service-Token on the fly and forwards the caller's cookie, so the far guard stamps who did it; the payload never names the actor. Anything but 201 is logged as [activity] <action> failed and returns null — the user's action never fails because the trail did. With ACTIVITY_API_URL unset the client warns once and every post is a no-op. This is the one function a message queue replaces later.

The door to CouchDB: createCouch

The fleet has one CouchDB, named once in ~/pm2/shared.env as COUCHDB_URL, COUCHDB_USERNAME and COUCHDB_PASSWORD. A service builds its client from those three and nothing else (0.4.0):

import { createCouch } from '@debito/zeko-auth-guard';

const nanoInstance = createCouch();

A missing variable throws at boot. The instance is nano with parseUrl: false, so a path prefix in the URL (an nginx /db front) stays part of the server base. No service carries a CouchDB address or password in its own pm2 JSON.

Service tokens for external callers

A system outside the fleet (Quicko, say) has no secret, so mint it a long-lived token:

SERVICE_JWT_SECRET=<secret> node scripts/mint-service-token.js quicko

It sends that as X-Service-Token. Internal services never need this.

Test

npm install && npm test

Integration tests: a throwaway Hapi server with a stubbed fetch — no network, no running uauth. Covers 200 / 401 / 403 and the service-token bypass.