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

@startdoing/identity-sdk

v0.1.33

Published

TypeScript SDK for startdoing identity-service

Downloads

817

Readme

@startdoing/identity-sdk

TypeScript client for startdoing Identity Service: OAuth 2.0 Authorization Code flow with PKCE (browser-safe), token exchange + refresh-with-rotation, /oauth/userinfo, /oauth/revoke, /oauth/introspect, OIDC discovery + JWKS, and optional first-party /auth/* cookie session helpers.

Ecosystem apps should use this package instead of hand-rolling HTTP against the IdP so URLs, JSON fields, PKCE, and error handling stay aligned with the server.

| More reading (repo) | | | --- | --- | | OAuth flow, prerequisites, security | docs/integration-guide.md | | Raw HTTP contracts | docs/api-reference.md | | Publish / build this package (maintainers) | docs/sdk.md |

Entry point: src/index.ts.


Install

npm install @startdoing/identity-sdk

Requires a global fetch (Node 18+, Bun, modern browsers). Pass a custom fetchFn if you need mocks or a polyfill.


Choose how your app talks to the IdP

| Approach | When to use | Secrets | Typical host | | --- | --- | --- | --- | | Confidential OAuth client + your backend | Production SSR apps, third-party–style clients, SPAs with a server callback | clientSecret only on the server | API routes / BFF that hold the secret and perform exchangeAuthorizationCode / refreshAccessToken | | Public OAuth client (PKCE-only) | SPA / mobile / native clients with no server-held secret | None — PKCE S256 mandatory | Browser / device that performs PKCE end-to-end | | First-party session | Browser app that shares IdP cookies (same eTLD, or SameSite=None IdP cookie + CORS + credentials) | No client secret; user password only in your trusted UI calling the IdP | Browser |

Do not put clientSecret in browser JavaScript for SPA bundles — register a public client instead.


IdentityServiceClient

import { IdentityServiceClient } from "@startdoing/identity-sdk";

const idp = new IdentityServiceClient({
  baseUrl: "https://idp.example.com", // no trailing slash required; normalized
  clientId: process.env.IDENTITY_CLIENT_ID,
  clientSecret: process.env.IDENTITY_CLIENT_SECRET, // omit in browser-only first-party usage
  fetchFn: optionalCustomFetch,
});

Constructor options

| Option | Required | Description | | --- | --- | --- | | baseUrl | yes | Identity Service origin (e.g. https://idp.example.com). | | clientId | for OAuth URL + token calls | Default client_id. Overridable per call. | | clientSecret | confidential clients | Default client_secret for token endpoints. Omit for public (SPA/mobile) clients. | | tokenEndpointAuthMethod | no | "client_secret_basic" (default for confidential), "client_secret_post", or "none" (auto-selected for public clients). | | fetchFn | no | Defaults to global fetch. |

If a required field is missing the client throws before the HTTP request.


OAuth: authorize → callback → tokens → userinfo

1. Start login (PKCE + state + optional nonce / scope)

Generate a PKCE pair, a random state, and (for OIDC) a random nonce. Persist the verifier, state, and nonce for the callback.

import {
  IdentityServiceClient,
  createPkcePair,
  createRandomState,
} from "@startdoing/identity-sdk";

const idp = new IdentityServiceClient({
  baseUrl: process.env.IDENTITY_BASE_URL!,
  clientId: process.env.IDENTITY_CLIENT_ID!,
  clientSecret: process.env.IDENTITY_CLIENT_SECRET, // omit for public clients
});

const { verifier, challenge } = await createPkcePair();
const state = createRandomState();
const nonce = createRandomState();
const authorizeUrl = idp.createAuthorizeUrl({
  redirectUri: process.env.IDENTITY_REDIRECT_URI!,
  state,
  nonce,
  scope: "openid email profile",
  codeChallenge: challenge,
});

2. Callback: exchange code for tokens

After validating state, call:

const tokens = await idp.exchangeAuthorizationCode({
  code: callbackCode,
  redirectUri: process.env.IDENTITY_REDIRECT_URI!,
  codeVerifier: verifier, // required if you sent code_challenge on authorize
});
// tokens: access_token, refresh_token, id_token?, token_type, expires_in

3. Load user profile

const user = await idp.getUserInfo(tokens.access_token);
// user: { sub, email }

4. Refresh access token

Refresh tokens are rotated: each success returns a new refresh_token; replace the stored value and stop using the old one. Re-presenting an already-rotated token revokes the entire refresh-token family server-side.

const refreshed = await idp.refreshAccessToken({
  refreshToken: storedRefreshToken,
});

5. Revoke + introspect

await idp.revokeToken({
  token: storedRefreshToken,
  tokenTypeHint: "refresh_token",
});

const meta = await idp.introspectToken({ token: accessToken });
if (!meta.active) {
  // token unusable
}

revokeToken always resolves successfully (RFC 7009); use it as part of your logout flow. introspectToken requires confidential client credentials.

6. Discovery + JWKS (verify ID tokens out-of-band)

const meta = await idp.fetchDiscovery();
const jwks = await idp.fetchJwks();

Use these to drive a JOSE library that verifies ID tokens against the published RS256 keys (kid header → JWKS lookup).


Per-call OAuth credentials

You can set clientId and clientSecret only on exchangeAuthorizationCode / refreshAccessToken if the client was constructed without defaults:

await idp.exchangeAuthorizationCode({
  code,
  redirectUri,
  codeVerifier,
  clientId: "...",
  clientSecret: "...",
});

First-party session (cookie flow)

These call /auth/register, /auth/login, /auth/user, /auth/logout with credentials: "include". The IdP must allow your app origin in CORS (CORS_ORIGINS) and you must call from a context that sends cookies (browser, same-site or configured cross-site cookies).

const idp = new IdentityServiceClient({ baseUrl: "https://idp.example.com" });

await idp.register("[email protected]", "SecurePassword123");
await idp.login("[email protected]", "SecurePassword123");
const me = await idp.getSessionUser();
await idp.logout();

This path does not use clientSecret. It is not a substitute for OAuth when you need delegated access for a server-side app that does not share the IdP session cookie.


PKCE helpers

All helpers are browser-safe (Web Crypto with a node:crypto fallback).

| Function | Purpose | | --- | --- | | createPkceVerifier(length?) | Random verifier string. | | createPkceChallengeS256(verifier) | code_challenge for S256 (async). | | createPkcePair() | Convenience: { verifier, challenge } (async). | | createRandomState(bytes?) | Random state / nonce parameter. |


Errors

Non-2xx responses throw IdentityServiceError (extends Error):

| Property | Meaning | | --- | --- | | status | HTTP status. | | code | Server error field when present (e.g. OAuth error code). | | requestId | From JSON request_id or X-Request-Id header. | | body | Parsed response body (JSON or fallback). |

import { IdentityServiceError } from "@startdoing/identity-sdk";

try {
  await idp.refreshAccessToken({ refreshToken });
} catch (err) {
  if (err instanceof IdentityServiceError) {
    console.error(err.status, err.code, err.requestId);
  }
  throw err;
}

Exported types

AuthorizeUrlParams, ExchangeCodeParams, RefreshTokenParams, RevokeTokenParams, IntrospectTokenParams, IdentityServiceClientOptions, OAuthTokenResponse, UserInfoResponse, IntrospectionResponse, DiscoveryDocument, RegisterResponse, LoginResponse, LogoutResponse, IdentityServiceError.


Environment variables (consuming apps)

Typical server-side OAuth setup:

  • IDENTITY_BASE_URL — IdP origin
  • IDENTITY_CLIENT_ID
  • IDENTITY_CLIENT_SECRET
  • IDENTITY_REDIRECT_URI — must match the registered redirect URI exactly

Security checklist

  • Keep clientSecret on the server. Use a public client (no secret) with PKCE for SPAs.
  • Perform code exchange and refresh on trusted infrastructure.
  • Validate state on the OAuth callback before exchanging the code.
  • For OIDC, validate nonce on the returned id_token.
  • Store refresh tokens securely; rotation is mandatory and the entire family is revoked on reuse.
  • Prefer HTTPS for baseUrl and redirect URIs in production.

AI coding agents (TanStack Intent)

This package includes TanStack Intent skills under skills/ (shipped in the npm package). To wire them into your editor or agent, run npx @tanstack/intent@latest install. To list skills from the installed package, run npx @tanstack/intent@latest list.

Monorepo: develop and publish

From the repository root: build with bun run sdk:build, test local tarballs with bun run sdk:pack, publishing is described in docs/sdk.md.