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

@aria-framework/auth

v0.12.0

Published

Aria App Framework — auth module. An injectable RBAC permission resolver (createRbac) and an Express staff-session guard + permission gates (createAuth). Catalog, DB loader, session hooks, and views all injected; no app-specific content baked in.

Readme

@aria-framework/auth

Aria App Framework — the shared auth/authz layer. Two factories, no module-level state and no app-specific content baked in:

  • createRbac — an in-memory role→permission cache for O(1) checks. You supply how to load your roles and your full permission set; the resolver does the rest. admin (configurable) always holds every permission.
  • createAuth — an Express staff-session guard (requireAuth) plus the permission gates (requirePermission/requireAnyPermission/requireRole), built from an rbac instance and a few injected hooks.

A second principal (a customer-portal contact, an API JWT user) is not here — that's app-specific; keep its guard in the app. This package is the staff/ admin session path both apps share.

createRbac

const { createRbac } = require('@aria-framework/auth');
const { ALL_PERMISSIONS } = require('./permissions'); // your catalog

const rbac = createRbac({
  loadRolePerms: () => db.invoke('Role', 'permissionList'), // -> [{name, permissions:[]}]
  allPermissions: ALL_PERMISSIONS,   // admin superset + isValidPermission oracle
  adminRole: 'admin',                // default 'admin'
  logger
});

await rbac.load();                   // at boot (and refresh() after role edits)
rbac.can('agent', 'tickets.edit');   // O(1), case-insensitive role
rbac.permsFor('agent');              // string[]
rbac.isValidPermission('tickets.x'); // catalog membership

The permission catalog (category structure, role defaults, any implied-view expansion) stays in the app — it's app-specific content. The engine only needs the flat set of valid permissions and a role loader.

createAuth

const { createAuth } = require('@aria-framework/auth');

const { requireAuth, requirePermission, requireAnyPermission, requireRole } = createAuth({
  rbac,
  getSessionTimeoutMs: () => dbClient.getSessionTimeoutMs(), // sync, cached
  getSessionVersion: (userId) => dbClient.invoke('User', 'getSessionVersion', userId),
  loginPath: '/login',                       // default '/login'
  forbidden: (req, res) => res.status(403).render('errors/403', { title: 'Access Denied' })
});

app.use('/tickets', requireAuth, ticketsRouter);
router.post('/x', requirePermission('tickets.edit'), handler);

requireAuth enforces: session presence (anon → loginPath, stashing a returnTo for real page navigations), an idle timeout (getSessionTimeoutMs), and a session-version check — when req.session.sessionVersion is set, it's compared to getSessionVersion(userId) and the session is destroyed on mismatch (a role/password change or deactivation drops existing sessions next request); it fails closed if the version read throws. On success it sets res.locals.currentUser and res.locals.currentPath. The lastActivity write is throttled (default 60s) to avoid whole-blob session-save races.

The gates read req.session.user.role and consult rbac.can; anon → loginPath, insufficient → your forbidden(req,res) (default: plain-text 403).

Session shape expected

req.session.user (with .id and .role), optional .sessionVersion, .lastActivity, .returnTo; req.session.destroy(cb). Standard express-session.

createEntraAuth({ getStore, logger }) (0.2.0)

The server-side Microsoft Entra ID OIDC authorization-code flow. Returns { getConfig, isSignInConfigured, reset, generatePkce, getAuthCodeUrl, redeemCode }.

const { createEntraAuth } = require('@aria-framework/auth');
const entraAuth = createEntraAuth({ getStore: () => encryption.getSecureStore(), logger });

Reads the entra SecureStore row (enabled, client_id, tenant_id, client_secret). The browser is only ever redirected — Microsoft tokens never touch page JavaScript — and the code exchange happens over the authenticated back-channel, so no JWKS or signature validation is needed here.

redeemCode() returns { ok, username, displayName, oid, tid, claims } and fails closed when the token carries neither oid nor sub: falling through to username-only resolution is the mutable-identifier bug both consumers fixed independently. oid and tid are coerced to strings — they end up as DB bind parameters.

What it deliberately does NOT do: resolve a local user, gate on auth_source, bind an identity, touch a session, audit, or flash. That is the app's callback route. Values in, values out; it never sees req/res.

Two behaviours that look removable and are not: the client is memoised on clientId|tenantId|clientSecret, so a rotated secret rebuilds rather than serving a client holding the old one until restart; and isSignInConfigured() caches for 30s because it decrypts, and both the login page and a mobile probe call it per request.

expandImpliedViews(perms, categories) (0.2.0)

For every category in which a role holds at least one permission, also grant that category's <id>.view — when the catalog declares one. Lets a module mount gate require <category>.view strictly without locking a manage-only custom role out of its own module.

Apply at write time (role create/update), so what is stored is what is enforced. Pure, and defensive by design: bad input returns the input rather than throwing, because it runs inside a role save.

Promoted from Acc101, which had the general rule; the other consumer had solved the same problem once by hand with a migration.

Changelog

  • 0.2.0 — adds createEntraAuth (the server-side Entra OIDC sign-in flow, promoted on the two-consumer rule: both apps held copies whose exported surface was character-for-character identical) and expandImpliedViews (promoted from Acc101, which had the general rule where the other app had patched one pair by migration). Additive — createRbac and createAuth are unchanged. MINOR because nothing here is urgent and a consumer must change code to benefit, so a manifest bump costs nothing it was not already paying.

  • 0.1.1 — the expired-session redirect now appends expired=1 with the correct separator, so a loginPath that already carries a query string isn't mangled into a double-? URL.

  • 0.1.0 — first release. Extracted from Support101/Acc101 lib/rbac.js + middleware/auth.js (near-identical forks). The mechanism is shared; each app injects its permission catalog, role loader, session hooks, login path, and 403 view. App-specific second principals (portal contact) stay in the app.