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

@dooor-ai/auth-node

v0.3.0

Published

Server-side verification for Dooor Auth access tokens: offline JWKS verification with kid caching, plus Express and generic guard adapters.

Readme

@dooor-ai/auth-node

Offline verification of Dooor Auth access tokens: fetches the issuer's public JWKS, caches keys by kid (5 min TTL, instant refetch on an unknown kid), and allowlists RS256 only. No secret is ever shared with the Dooor platform.

Install

npm i @dooor-ai/auth-node

Generic usage

import { verifyDooorToken } from "@dooor-ai/auth-node";

const claims = await verifyDooorToken(token, { audience: process.env.DOOOR_AUTH_APP_ID! });
// { sub, aud, sid, realm, app_user, org, email, roles, iat, exp, jti }

For API bearer authentication, use verifyDooorAccessToken or one of the middleware adapters. They reject signed ID tokens and prevent token substitution:

import { verifyDooorAccessToken } from "@dooor-ai/auth-node";

const claims = await verifyDooorAccessToken(token, {
  audience: process.env.DOOOR_AUTH_APP_ID!,
});

issuer defaults to DOOOR_AUTH_ISSUER (falling back to https://api.os.dooor.ai), and audience defaults to DOOOR_AUTH_APP_ID. Both env vars are injected automatically into apps deployed on the Dooor OS runtime; nothing to configure by hand there.

Express

import express from "express";
import { requireDooorAuth } from "@dooor-ai/auth-node/express";

const app = express();
app.use(requireDooorAuth()); // reads DOOOR_AUTH_ISSUER / DOOOR_AUTH_APP_ID from env
app.get("/me", (req, res) => res.json(req.dooor));

Pass { optional: true } to let requests through without a valid token (req.dooor stays undefined) instead of responding 401. req.dooor is typed for you - no cast needed.

Role checks are built in. A valid token that lacks the role gets a 403, not a 401:

app.get("/admin", requireDooorAuth({ roles: ["admin"] }), handler);
app.get("/billing", requireDooorAuth({ roles: ["admin", "billing"], requireAllRoles: true }), handler);

NestJS

import { DooorAuthModule } from "@dooor-ai/auth-node/nest";

@Module({ imports: [DooorAuthModule.forRoot()] }) // issuer/audience from env
export class AppModule {}
import { CurrentUser, DooorAuthGuard, DooorRoles, Public } from "@dooor-ai/auth-node/nest";
import type { DooorTokenPayload } from "@dooor-ai/auth-node";

@UseGuards(DooorAuthGuard)
@Controller("reports")
export class ReportsController {
  @Get("me")
  me(@CurrentUser() user: DooorTokenPayload, @CurrentUser("org") orgId: string) {
    return { user, orgId };
  }

  @DooorRoles("admin")
  @Delete(":id")
  remove(@Param("id") id: string) {}

  @Public() // opts out when the guard is registered globally via APP_GUARD
  @Get("health")
  health() {
    return { ok: true };
  }
}

Register it globally instead of per-controller with the standard Nest provider:

providers: [{ provide: APP_GUARD, useClass: DooorAuthGuard }]

@nestjs/common and @nestjs/core are optional peer dependencies - the rest of the package works without them.

Fastify

import { dooorAuthHook } from "@dooor-ai/auth-node/fastify";

app.addHook("preHandler", dooorAuthHook());
app.get("/me", (request) => request.dooor);

// Or scoped to a subtree, with a role requirement:
app.register(async (instance) => {
  instance.addHook("preHandler", dooorAuthHook({ roles: ["admin"] }));
  instance.get("/admin/stats", handler);
});

Generic guard (any framework)

createAuthGuard accepts anything with a headers bag or a Headers instance, which covers Hono, Elysia, Koa, and any Fetch-based runtime:

import { createAuthGuard } from "@dooor-ai/auth-node";

const guard = createAuthGuard({ audience: process.env.DOOOR_AUTH_APP_ID! });

// Hono (or any framework exposing the raw Request):
app.use(async (c, next) => {
  c.set("dooor", await guard(c.req.raw));
  await next();
});

Roles

hasRoles / assertRoles run authorization checks against already-verified claims. Roles are resolved per app at token issuance:

import { assertRoles, verifyDooorAccessToken } from "@dooor-ai/auth-node";

const claims = await verifyDooorAccessToken(token);
assertRoles(claims, ["admin"]);            // throws DooorAuthError("insufficient_role")
assertRoles(claims, ["a", "b"], { requireAll: true });

Security notes

  • Only RS256 is accepted; alg: none and symmetric-key downgrade attempts are rejected before signature verification.
  • aud must match the app's id exactly; a token minted for one app is rejected by another app's verifier.
  • API guards require token_use=access; a signed ID token is rejected as a bearer token.
  • Role checks are authorization on top of verification, never a replacement for it: assertRoles assumes the claims already came out of verifyDooorAccessToken.
  • The JWKS cache refetches on TTL expiry (5 min) or immediately when a kid it hasn't seen is presented, so key rotation doesn't require a deploy or restart.

License

MIT