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

@endevre/id-client

v0.1.24

Published

Endevre ID client-side authentication library for SSO token exchange and session management.

Downloads

543

Readme

Endevre ID Client Auth

Client-side authentication helper for Endevre ID SSO flows.

Install

# Latest stable version
npm install @endevre/id-client
# or
yarn add @endevre/id-client

# Beta version
npm install @endevre/id-client@beta

# Development version
npm install @endevre/id-client@dev

Creating the client

import endevreID from '@endevre/id-client';

const client = endevreID.createClient('<clientID>', '<secret>', {
  // Optional cookie configuration
  cookiePrefix: 'endevre_id_',  // default
  cookieDomain: '.example.com', // optional
  cookieSecure: true,           // default inferred from https
  cookieSameSite: 'Lax',        // 'Lax' | 'Strict' | 'None' (default 'Lax')
  defaultOrigin: 'https://app.example.com',
  defaultReturnURL: 'https://app.example.com/logout', // used when window.origin unavailable
  onRedirect: (url) => console.log('Redirect to', url)
});

clientID and secret come from Endevre ID. Options control cookie behavior and can be omitted.

Browser-only client

If you only need to build login URLs or set browser cookies, you can instantiate a lightweight browser client that does not require the client secret:

const browserClient = endevreID.createBrowserClient('<clientID>');

const loginUrl = browserClient.createAuthUrl({
  finalredirecturi: 'https://app.example.com/after-login',
  redirecturis: ['https://app.example.com/start']
});

// Use server-issued tokens to set cookies and optionally control redirect handling
browserClient.setClientCookies(resultFromServer, 'pairing-id', {
  shouldRedirect: true,
  onRedirect: (url) => {
    // Called when running outside the browser
    console.log('Forward user to', url);
  }
});

Server-side usage

exchange, refreshToken, and getUserInfo can now run in non-browser environments (such as Node.js). When no browser is available, be sure to pass the required tokens explicitly:

// In a server context, provide the code yourself and include an origin header
const result = await client.exchange('authorization-code-from-callback', {
  origin: 'https://app.example.com'
});

// Likewise for refresh token operations
const refreshed = await client.refreshToken('stored-refresh-token', {
  origin: 'https://app.example.com'
});

Generate login URL

const url = client.createAuthUrl({
  finalredirecturi: 'https://app.example.com/after-login',
  redirecturis: [
    'https://app.example.com/start',
    'https://app.example.com/middle'
  ],
  // pairingID: 'optional-external-id', // optionally provide pairingID
});
// url looks like: https://id.endevre.com/ssologin?clientID=...&redirectURIs=https://app.example.com/start,https://app.example.com/middle,https://app.example.com/after-login

pairingID is optional. If omitted, Endevre ID will generate one on the server side. finalredirecturi is ensured to be the last item in redirectURIs.

Usage Patterns

Recommended: High-Level SSO Flow (Multi-Domain)

The simplest way to handle multi-domain SSO with automatic cookie management and redirects:

import endevreID from '@endevre/id-client';

const client = endevreID.createClient('<clientID>', '<secret>');

// === LOGIN FLOW ===
// 1. Generate login URL and redirect user to SSO
const loginUrl = client.createAuthUrl({
  finalredirecturi: 'https://app.example.com/after-login',
  redirecturis: ['https://app.example.com/callback']
});
window.location.href = loginUrl;

// 2. On callback page (after SSO redirects back with ?code=xxx&pairingID=yyy)
// This handles EVERYTHING: exchange, set cookies, redirect to continue SSO loop
const result = await client.handleAuthCallback();
// User is automatically redirected to continue multi-domain SSO loop

// === LOGOUT FLOW ===
// 1. Generate logout URL and redirect user to SSO
const logoutUrl = client.createLogoutUrl({
  finalredirecturi: 'https://app.example.com/after-logout',
  redirecturis: ['https://app.example.com/logout-callback']
});
window.location.href = logoutUrl;

// 2. On logout callback page (after SSO redirects back with ?pairingID=yyy)
// This handles EVERYTHING: remove cookies, redirect to continue SSO logout loop
client.handleLogoutCallback();
// User is automatically redirected to continue multi-domain SSO logout loop

Manual Control: Layered API

For custom flows, use the layered API with three levels of control:

// === LEVEL 1: Low-level (no redirects) ===
// Exchange code manually
const result = await client.exchange('authorization-code');

// Just set cookies, no redirect
client.setCookies(result);

// Later: just remove cookies, no redirect
client.removeCookies();

// === LEVEL 2: Mid-level (with redirects) ===
// Exchange and set cookies with redirect
const result = await client.exchange();
const pairingID = client.getPairingIDFromUrl();
client.setClientCookies(result, pairingID); // redirects to continue SSO loop

// Remove cookies with redirect
const pairingID = client.getPairingIDFromUrl();
client.removeClientCookies(pairingID); // redirects to continue SSO logout loop

// === LEVEL 3: High-level (complete orchestration) ===
await client.handleAuthCallback(); // does exchange + setCookies + redirect
client.handleLogoutCallback(); // does removeCookies + redirect

Checking Callback Type

Use utility methods to determine what type of callback you're handling:

if (client.isAuthCallback()) {
  // URL has ?code=xxx, handle login callback
  await client.handleAuthCallback();
} else if (client.isLogoutCallback()) {
  // URL has ?pairingID=xxx (no code), handle logout callback
  client.handleLogoutCallback();
}

Working with User Info

// Fetch user info for current token
const user = await client.getUserInfo();
console.log(user.firstName, user.email);

// Refresh token when needed
const refreshed = await client.refreshToken();

// Check if cookies are set
if (client.areCookiesSet()) {
  console.log('User is authenticated');
}

Exchange overloads and options

// Read code from URL, use default options
await client.exchange();

// Read from URL with custom options
await client.exchange({
  names: { token: 'id_token', refresh: 'id_refresh', scopes: 'id_scopes', expire: 'id_expire' },
  shouldRedirect: false
});

// Pass code directly
await client.exchange(code);

// Pass code with options
await client.exchange(code, { shouldRedirect: false });

Notes:

  • exchange() reads the code parameter from the URL when not provided directly
  • pairingID is only needed for setClientCookies() when redirecting
  • Outside the browser you must pass the authorization code explicitly (e.g. exchange('code')) and include an origin option (or configure defaultOrigin) so the Origin header is sent

Layered Cookie Management

Three levels of cookie control:

// LOW-LEVEL: Just set/remove cookies (no redirect)
client.setCookies(result, { isWildcardDomain: true });
client.removeCookies({ isWildcardDomain: true });

// MID-LEVEL: Set/remove cookies with optional redirect
client.setClientCookies(result, pairingID); // redirects by default
client.setClientCookies(result, undefined, { shouldRedirect: false }); // no redirect

client.removeClientCookies(pairingID); // redirects by default
client.removeClientCookies(undefined, { shouldRedirect: false }); // no redirect

// HIGH-LEVEL: Complete callback handling
await client.handleAuthCallback(); // extracts params, exchanges, sets cookies, redirects
client.handleLogoutCallback(); // extracts params, removes cookies, redirects

API Reference

Default export

import endevreID from '@endevre/id-client';
const client = endevreID.createClient(clientID, secret, options?);
const browserClient = endevreID.createBrowserClient(clientID, options?);

createClient(clientID: string, secret: string, options?: EndevreClientOptions)

  • clientID/secret: Provided by Endevre ID, used for server calls.
  • options.cookiePrefix: Prefix for cookie names. Default endevre_id_.
  • options.cookieDomain: Cookie domain.
  • options.cookieSecure: Force Secure cookie attribute. Defaults based on https:.
  • options.cookieSameSite: Lax (default) | Strict | None.
  • options.defaultOrigin: Fallback value used for the Origin header when the browser is unavailable.
  • options.defaultReturnURL: Fallback URL used by logout() when a browser origin isn't available.
  • options.onRedirect: Handler invoked when a redirect is requested but no browser environment is present.

createBrowserClient(clientID: string, options?: EndevreClientOptions)

  • Returns a browser-oriented client that skips server-only features (no secret required).
  • Provides createAuthUrl, setClientCookies, and logout helpers.
  • Server-side calls such as exchange() or refreshToken() require a full client created with createClient.

client.createAuthUrl(params: CreateAuthUrlParams): string

  • Builds https://id.endevre.com/ssologin with clientID and redirectURIs.
  • Ensures finalredirecturi is the last entry in redirectURIs.
  • pairingID is optional; include when you want to control correlation.

client.exchange(...)

Overloads:

  • exchange()
  • exchange(options)
  • exchange(code)
  • exchange(code, options)

Options shape:

type ExchangeOptions = {
  names?: { token?: string; refresh?: string; scopes?: string; expire?: string };
  shouldRedirect?: boolean; // default true
  origin?: string; // overrides Origin header for API requests
}

Behavior:

  • Reads code from the URL when not provided directly.
  • Returns exchange result without setting cookies (use setClientCookies() for that).

client.setClientCookies(result, pairingID?, options?)

  • Stores tokens from exchange result in cookies.
  • If shouldRedirect is true (default) and pairingID is provided, redirects to exchange endpoint.
  • Uses cookie names from options or defaults.

client.getUserInfo(): Promise<Record<string, unknown>>

  • Fetches user information for the current authenticated user.
  • Automatically handles token verification and validation.

client.refreshToken(): Promise<{ token, refresh, expire }>

Overloads:

  • refreshToken()
  • refreshToken(options)
  • refreshToken(refresh)
  • refreshToken(refresh, options)

Options shape:

type RefreshTokenOptions = {
  refresh?: string;
  names?: { token?: string; refresh?: string; scopes?: string; expire?: string };
  origin?: string; // overrides Origin header for API requests
}
  • Uses stored refresh token when not provided directly.
  • Updates cookies automatically with new tokens.
  • Supply an origin option (or configure defaultOrigin) outside the browser so the Origin header is sent.

client.handleAuthCallback(options?): Promise

High-level login callback handler

  • Extracts code and pairingID from URL automatically
  • Exchanges code for tokens
  • Sets cookies
  • Redirects to continue multi-domain SSO loop
// After SSO redirects back with ?code=xxx&pairingID=yyy
const result = await client.handleAuthCallback();
// Cookies set, user redirected automatically

Options:

{
  origin?: string;
  jwt?: boolean;
  names?: { token?, refresh?, scopes?, expire? };
  shouldRedirect?: boolean; // default true
  isWildcardDomain?: boolean;
  onRedirect?: (url: string) => void;
}

client.handleLogoutCallback(options?): void

High-level logout callback handler

  • Extracts pairingID from URL automatically
  • Removes authentication cookies
  • Redirects to continue multi-domain SSO logout loop
// After SSO redirects back with ?pairingID=yyy
client.handleLogoutCallback();
// Cookies removed, user redirected automatically

Options:

{
  names?: { token?, refresh?, scopes?, expire? };
  shouldRedirect?: boolean; // default true
  isWildcardDomain?: boolean;
  onRedirect?: (url: string) => void;
}

client.createLogoutUrl(params: CreateLogoutUrlParams): string

  • Builds SSO logout URL with clientID and redirectURIs
  • Ensures finalredirecturi is the last entry in redirectURIs
  • pairingID is optional
const logoutUrl = client.createLogoutUrl({
  finalredirecturi: 'https://app.example.com/after-logout',
  redirecturis: ['https://app.example.com/logout-callback']
});

client.setCookies(result, options?)

Low-level: Set cookies without redirect

client.setCookies(result, { 
  names: { token: 'custom_token' },
  isWildcardDomain: true 
});

client.setClientCookies(result, pairingID?, options?)

Mid-level: Set cookies with optional redirect

// With redirect (requires pairingID)
client.setClientCookies(result, pairingID);

// Without redirect
client.setClientCookies(result, undefined, { shouldRedirect: false });

client.removeCookies(options?)

Low-level: Remove cookies without redirect

client.removeCookies({ isWildcardDomain: true });

client.removeClientCookies(pairingID?, options?)

Mid-level: Remove cookies with optional redirect

// With redirect (requires pairingID)
client.removeClientCookies(pairingID);

// Without redirect
client.removeClientCookies(undefined, { shouldRedirect: false });

client.isAuthCallback(): boolean

Returns true if current URL has code parameter (login callback).

client.isLogoutCallback(): boolean

Returns true if current URL has pairingID but no code (logout callback).

client.getPairingIDFromUrl(): string | undefined

Extracts pairingID from current URL.

client.logout(params?): void

⚠️ Deprecated: Use createLogoutUrl() and handleLogoutCallback() for proper SSO logout.

Local-only logout (doesn't go through SSO):

client.logout({ returnURL: 'https://app.example.com' });

Configuration (cookies and options)

type EndevreClientOptions = {
  cookiePrefix?: string; // default 'endevre_id_'
  cookieDomain?: string;
  cookieSecure?: boolean; // default inferred from https
  cookieSameSite?: 'Lax' | 'Strict' | 'None'; // default 'Lax'
  defaultOrigin?: string; // used to populate Origin header outside the browser
  defaultReturnURL?: string; // used when logout runs outside the browser
  onRedirect?: (url: string) => void; // called when redirecting without browser APIs
}

Cookie names can be customized per-operation via options. For example:

await client.exchange({ names: { token: 'id_token', refresh: 'id_refresh' } });
await client.refreshToken({ names: { token: 'id_token', refresh: 'id_refresh' } });

Commands (Yarn)

# Install deps
yarn install

# Type-check
yarn typecheck

# Run unit tests
yarn test

# Build CJS+ESM with types
yarn build

Browser-only

The library targets browser environments for URL/cookie access and token verification.