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

@plinthjs/passport

v0.1.0

Published

Mason OAuth2 server (the Laravel Passport equivalent).

Readme

@plinthjs/passport

A dependency-free OAuth2 authorization server for Mason, the Laravel Passport equivalent. It ships client registration, the four core grants (authorization code with PKCE S256, client credentials, refresh token, password), a scope registry, an AuthorizationServer that drives the authorize, token and revoke endpoints, and a ResourceServer that protects APIs. Persistence sits behind store interfaces with in-memory Array* defaults, and the clock and id/secret generators are injectable.

Install

npm install @plinthjs/passport

Usage

import {
  ArrayAuthCodeStore,
  ArrayClientStore,
  ArrayRefreshTokenStore,
  ArrayTokenStore,
  AuthorizationServer,
  Client,
  hashClientSecret,
  ResourceServer,
  ScopeRegistry,
} from '@plinthjs/passport'

const clients = new ArrayClientStore([
  Client.from({
    id: 'client-1',
    name: 'First Party',
    secretHash: hashClientSecret('client-secret'),
    redirectUris: ['https://app.test/callback'],
    grantTypes: ['authorization_code', 'client_credentials', 'refresh_token'],
    scopes: ['users:read', 'users:write'],
    confidential: true,
  }),
])
const tokens = new ArrayTokenStore()

const server = new AuthorizationServer({
  clients,
  tokens,
  refreshTokens: new ArrayRefreshTokenStore(),
  authCodes: new ArrayAuthCodeStore(),
  scopes: new ScopeRegistry()
    .define('users:read', 'Read users')
    .define('users:write', 'Write users'),
  ttl: { accessTokenTtlSeconds: 3600 },
})

// Machine-to-machine: no user, no refresh token.
const issued = server.issueToken('client_credentials', {
  client_id: 'client-1',
  client_secret: 'client-secret',
  scope: 'users:read',
})
issued.accessToken // 'id.secret', returned exactly once
issued.expiresIn // 3600

// Protect an API.
const resource = new ResourceServer({ tokens })
resource.validate(issued.accessToken) // AccessToken | null
resource.validateForScope(issued.accessToken, 'users:read') // true

Authorization code flow

// GET /oauth/authorize: validate, then show the consent screen.
const request = server.validateAuthorizationRequest(query) // client_id, redirect_uri, scope, state, code_challenge...

// After the user decides:
const { redirectTo } = server.completeAuthorizationRequest({
  request,
  userId: '42',
  approved: true,
})

// POST /oauth/token: exchange the code (code_verifier required when a PKCE challenge was sent).
const pair = server.issueToken('authorization_code', body)
const rotated = server.issueToken('refresh_token', {
  refresh_token: pair.refreshToken!,
  client_id: 'client-1',
  client_secret: 'client-secret',
})

Public (secret-less) clients must use PKCE; deriveCodeChallenge(verifier) computes the S256 challenge. Refresh tokens rotate: the presented pair is revoked and a new pair is issued. The password grant is enabled only when verifyCredentials: (username, password) => userId | null is configured.

Revocation

server.revokeToken(issued.accessToken) // true
server.revokeToken(pair.accessToken) // also revokes its paired refresh token
server.revokeToken(rotated.refreshToken!, 'refresh_token') // optional token_type_hint

Unknown tokens return false rather than throwing (RFC 7009).

Tokens and errors

Tokens and codes are opaque, not JWTs: the plaintext id.secret is returned once and only sha256(secret) is stored, verified in constant time. Tokens are therefore only verifiable by this server. A resolved AccessToken exposes clientId, userId, scopes, can(scope) and cant(scope).

Every failure throws an OAuthError with an RFC 6749 code:

import { OAuthError } from '@plinthjs/passport'

function tokenEndpoint(grantType: string, params: Record<string, string>) {
  try {
    return { status: 200, body: server.issueToken(grantType, params) }
  } catch (error) {
    if (error instanceof OAuthError) {
      return { status: 400, body: error.toResponseBody() } // { error, error_description }
    }
    throw error
  }
}