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

@schematize/authorization-code

v0.6.7

Published

Schematize Authorization Code Auth Library

Readme

@schematize/authorization-code

OAuth 2.0 authorization code flow with PKCE for browser apps. The library exchanges codes for tokens, optionally validates OpenID Connect id_token claims and signatures (RS256 via JWKS), can call a userinfo endpoint, and schedules refresh_token exchanges before access token expiry.

Runtime requirements

  • Browser only (uses fetch, location, sessionStorage, localStorage, crypto.subtle).
  • globalThis.isSecureContext must be true (HTTPS or localhost); otherwise the constructor throws.

Features

  • Authorization code + PKCE S256 (code_verifier in localStorage so return flows work across tabs).
  • state wraps current search / hash plus optional app fields; restored after callback via history.replaceState.
  • Token endpoint supports application/json or application/x-www-form-urlencoded (including optional resource parameters from config.resources).
  • Stored session: access (and refresh) credentials in sessionStorage; PKCE verifier, OAuth state, and redirect_uri in localStorage.
  • refresh_token grant with a timer to refresh roughly 10 minutes before access token expiry.
  • revoke: optional RFC 7009-style POST to endpoints.revoke before clearing local state (used from signout when revoke is configured and the access token is not yet expired).
  • getUserInfo: GET to endpoints.userInfo with bearer header when configured; used as fallback if id_token validation fails.

Dependencies

  • @schematize/refs
  • @schematize/metamodel
  • @schematize/util-common (base64 / binary helpers for PKCE and JWT handling)

Installation

npm install @schematize/authorization-code

Usage

import AuthorizationCode from '@schematize/authorization-code';

const auth = AuthorizationCode({
  endpoints: {
    authorize: 'https://auth.example.com/authorize',
    token: 'https://auth.example.com/token',
    revoke: 'https://auth.example.com/revoke',
    userInfo: 'https://auth.example.com/userinfo',
    wellKnownJwks: 'https://auth.example.com/.well-known/jwks.json',
    signout: 'https://auth.example.com/signout',
  },
  clientId: 'your-client-id',
  scope: 'openid profile email',
  responseType: 'code',
  responseMode: 'fragment',
  issuer: 'https://auth.example.com',
});

// Initialize and check authorization status
await auth.initialized;

if (!auth.authorized) {
  // Redirect to authorization server
  await auth.authorize();
} else {
  // Use the access token
  console.log('Access token:', auth.accessToken);
  console.log('User info:', auth.userInfo);
}

AuthorizationCode is invoked as AuthorizationCode(config) or new AuthorizationCode(config). Identical (authorize URL, clientId, scope, responseType, resources fingerprint) configurations return the same instance (singleton per key).

Configuration

endpoints (required shape)

| Key | Required | Description | | --- | --- | --- | | authorize | yes | Authorization server URL (query string is appended). | | token | yes | Token URL string, or { url, contentType } with contentType 'application/json' or 'application/x-www-form-urlencoded'. | | revoke | no | Same string or { url, contentType } pattern as token. Used by signoutrevoke when present. | | userInfo | no | UserInfo URL for getUserInfo and id_token fallback. | | wellKnownJwks | needed for id_token path | JWKS document URL. Required when the token response includes id_token and you rely on local signature verification. | | signout | required to call signout() | SSO sign-out URL; signout appends ?redirect_uri=…. |

Other fields

  • clientId (required)
  • scope (required for restore logic: stored token scopes must cover every scope in config.scope)
  • responseType: use 'code' for the code flow (default in examples).
  • responseMode: 'query' reads location.search; 'fragment' reads location.hash for code, state, and errors.
  • issuer: optional; when set, validated against id_token iss.
  • resources: optional string[]; sent as repeated resource parameters (form) or resource: [...] (JSON) on token and refresh requests.

API

auth.initialized

Promise started in the constructor; await auth.initialized waits for initialize() (code exchange from URL, or load from storage, or OAuth error params).

await auth.initialized;

if (auth.authorized) {
  console.log(`Signed in`);
} else if (auth.error) {
  console.error(auth.error, auth.errorDescription);
}

initialize()

Same work as initialized; you normally do not need to call it explicitly.

await auth.initialize();

authorize(options?)

  • Awaits initialized first.
  • If already authorized and options.force is not true, returns this.
  • If error is set and force is not true, throws Error with message built from error / error_description / error_uri.
  • Otherwise clears token storage, stores PKCE verifier and state, sets location.href to the authorize URL, and returns false (pending navigation).

Options:

  • force: re-run authorize even if authorized or in error state.
  • redirectUri: override redirect URI (default location.origin + location.pathname).
  • state: shallow-merged into the wrapped state object (must be a plain object if provided).
// First visit: redirects the browser; execution may continue briefly until unload.
const redirecting = await auth.authorize();
if (redirecting === false) {
  return;
}

// Already authorized: same instance returned
const same = await auth.authorize();
console.log(same === auth);

// Force a new login (e.g. “switch account”)
await auth.authorize({ force: true });

// Fixed callback URL
await auth.authorize({
  redirectUri: `https://app.example.com/oauth/callback`,
});

// Extra fields stored in wrapped state (merged into OAuth state payload)
await auth.authorize({
  state: { returnTo: `/dashboard` },
});

signout(options?)

  • Awaits initialized, calls revoke() (best-effort fetch), clears timers and storage, resets auth fields, then sets location.href to endpoints.signout?redirect_uri=… (default redirect is location.href).
  • Returns false when navigation is pending.

options.redirectUri overrides the post-sign-out redirect query value.

const leaving = await auth.signout();
if (leaving === false) {
  return;
}

await auth.signout({
  redirectUri: `https://app.example.com/`,
});

refresh()

Uses grant_type=refresh_token with the same token endpoint encoding rules and optional resource list. Guarded by isRefreshingToken so overlapping calls are skipped. On failure after init, callers may see thrown errors from refresh (unlike the initial code exchange, which only sets error on the instance).

try {
  await auth.refresh();
} catch (e) {
  console.error(`Refresh failed`, e);
}

revoke()

If endpoints.revoke exists and the access token is not considered expired (expires is in seconds on the instance from stored creds), POSTs client_id, token, token_type_hint (access_token or refresh_token). Errors are logged, not thrown.

await auth.initialized;
await auth.revoke();

getUserInfo()

If endpoints.userInfo is missing or there is no bearer header, returns undefined. Otherwise GETs JSON and assigns userInfo. Returns cached userInfo when already set.

await auth.initialized;

const profile = await auth.getUserInfo();
if (profile === undefined) {
  console.log(`No userInfo endpoint or not yet authorized`);
} else {
  console.log(profile);
}

Properties (after init / token handling)

| Property | Notes | | --- | --- | | authorized | Boolean | | accessToken | String | | refreshToken | String when issued | | idToken | Raw id_token string when present | | expires | Access token expiry as seconds since epoch (internal scheduling uses this) | | headers | e.g. { Authorization: 'Bearer …' } | | userInfo | OIDC claims object from validated id_token payload, storage replay, or UserInfo endpoint | | error, errorDescription, errorUri | From token error JSON or authorize redirect error query |

OpenID / id_token behavior

  • Validates RS256 JWS using wellKnownJwks, checks exp / iat, optional issuer match, and aud equals clientId (single-audience case).
  • Nonce (nonce in auth request) is not fully wired end-to-end yet (see code TODO).
  • On validation failure, falls back to getUserInfo() when possible.

Security notes

  • Prefer sessionStorage for tokens (current default); PKCE/state/redirect use localStorage for cross-tab flows (see OWASP notes in source).
  • Token and revoke responses are read with response.json(); endpoints must return JSON bodies for those requests.

License

MIT

Author

Benjamin Bytheway