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

@selorax/signin

v1.0.1

Published

Sign in with SeloraX — OIDC client SDK for Node.js and browser

Downloads

100

Readme

Sign in with SeloraX

npm version downloads dependencies node license

Official SDK for integrating SeloraX identity into your application. OAuth 2.0 Authorization Code flow with PKCE — for Node.js and the browser.


Overview

signin-with-selorax lets users authenticate with their SeloraX merchant or customer account — similar to "Sign in with Google". The SDK handles the full OpenID Connect flow: authorization URL generation, PKCE challenges, token exchange, user info retrieval, token refresh, and revocation.

Two modules, one package:

| Module | Environment | Import | |--------|-------------|--------| | SeloraxOIDC | Node.js (Express, Fastify, etc.) | require('signin-with-selorax') | | SeloraxSignIn | Browser (React, Next.js, Vue, vanilla) | require('signin-with-selorax/frontend/selorax-signin') |


Installation

npm install signin-with-selorax

How It Works

┌──────────┐     1. Redirect      ┌──────────────┐
│          │ ──────────────────>   │              │
│   Your   │                      │   SeloraX    │
│   App    │     2. Code           │   Identity   │
│          │ <──────────────────   │   Provider   │
└──────────┘                      └──────────────┘
      │
      │  3. Exchange code for tokens
      │  4. Fetch user info
      │
      v
┌──────────┐
│  Tokens  │  access_token (1hr)
│  + User  │  refresh_token (30d)
│  Info    │  sub: "merchant:7"
└──────────┘

Node.js — Quick Start

const express = require('express');
const session = require('express-session');
const { SeloraxOIDC, createCallbackHandler, requireAuth } = require('signin-with-selorax');

const app = express();
app.use(session({ secret: 'your-secret', resave: false, saveUninitialized: false }));

const oidc = new SeloraxOIDC({
    issuer: 'https://api.selorax.io',
    clientId: 'sx_oc_...',
    clientSecret: 'sx_os_...',
    redirectUri: 'http://localhost:3000/auth/callback',
});

// Redirect to SeloraX
app.get('/auth/login', (req, res) => {
    const { url, state, pkce } = oidc.getAuthorizationUrl();
    req.session.oauthState = state;
    req.session.oauthPkce = pkce?.verifier;
    res.redirect(url);
});

// Handle callback
app.get('/auth/callback', createCallbackHandler(oidc, {
    serverSideUserInfo: true,
    onSuccess: (req, res, { tokens, user }) => {
        req.session.user = user;
        req.session.tokens = {
            access_token: tokens.access_token,
            refresh_token: tokens.refresh_token,
            expires_at: Date.now() + (tokens.expires_in * 1000),
        };
        res.redirect('/dashboard');
    },
    onError: (req, res, error) => {
        res.redirect('/auth/login?error=' + encodeURIComponent(error.error));
    },
}));

// Protected route
app.get('/dashboard', requireAuth({ loginUrl: '/auth/login' }), (req, res) => {
    res.json({ user: req.seloraxUser });
});

// Logout
app.get('/auth/logout', async (req, res) => {
    if (req.session.tokens?.access_token) {
        await oidc.revokeToken(req.session.tokens.access_token).catch(() => {});
    }
    req.session.destroy(() => res.redirect('/'));
});

app.listen(3000);

Browser — Quick Start

import { SeloraxSignIn } from 'signin-with-selorax/frontend/selorax-signin';

const signIn = new SeloraxSignIn({
    issuer: 'https://api.selorax.io',
    clientId: 'sx_oc_...',
    redirectUri: window.location.origin + '/auth/callback',
});

Login page:

document.getElementById('login-btn').addEventListener('click', () => {
    signIn.login();
});

Callback page:

const result = signIn.parseCallback();

if (result.error) {
    console.error(result.error, result.description);
} else {
    // Send code + verifier to your backend for token exchange
    const res = await fetch('/auth/exchange', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code: result.code, verifier: result.verifier }),
    });
}

API Reference

SeloraxOIDC — Server-Side Client

Constructor

new SeloraxOIDC(config)

Falls back to environment variables when config values are omitted:

SELORAX_OIDC_ISSUER=https://api.selorax.io
SELORAX_OIDC_CLIENT_ID=sx_oc_...
SELORAX_OIDC_CLIENT_SECRET=sx_os_...
SELORAX_OIDC_REDIRECT_URI=http://localhost:3000/auth/callback

Methods


getAuthorizationUrl(options?) — Generate the login redirect URL.

const { url, state, pkce } = oidc.getAuthorizationUrl({
    scopes: ['openid', 'profile', 'email', 'store'],  // optional override
    storeId: 22,   // optional: request access for a specific store
    nonce: '...',   // optional: OIDC nonce
    usePKCE: true,  // default: true (always true for public clients)
});
// url    → full authorization URL to redirect to
// state  → CSRF token to validate on callback
// pkce   → { verifier, challenge, method } or null

exchangeCode(code, codeVerifier?) — Exchange authorization code for tokens.

const tokens = await oidc.exchangeCode(code, pkce.verifier);
// {
//   access_token:  "sx_it_...",
//   refresh_token: "sx_ir_...",
//   token_type:    "Bearer",
//   expires_in:    3600,
//   scope:         "openid profile email"
// }

getUserInfo(accessToken) — Fetch user info with Bearer token (GET request).

const user = await oidc.getUserInfo(tokens.access_token);

getUserInfoServer(accessToken) — Fetch user info with client credentials (POST request). Validates that the token was issued to your client. Requires clientSecret.

const user = await oidc.getUserInfoServer(tokens.access_token);
// {
//   sub:            "merchant:7",
//   name:           "John Doe",
//   picture:        "https://...",
//   email:          "[email protected]",
//   email_verified: true,
//   store_id:       22,
//   store_name:     "My Store",
//   role:           "admin"
// }

refreshToken(refreshToken) — Get a new access token.

const newTokens = await oidc.refreshToken(tokens.refresh_token);

revokeToken(token, tokenTypeHint?) — Revoke an access or refresh token.

await oidc.revokeToken(tokens.access_token, 'access_token');
await oidc.revokeToken(tokens.refresh_token, 'refresh_token');

getDiscovery() — Fetch the OpenID Connect discovery document.

const config = await oidc.getDiscovery();
// config.authorization_endpoint, config.scopes_supported, etc.

Static Helpers

SeloraxOIDC.parseSubject(sub) — Extract user type and ID from the sub claim.

const { userType, userId } = SeloraxOIDC.parseSubject('merchant:7');
// userType: 'merchant', userId: 7

SeloraxOIDC.validateState(received, expected) — Timing-safe state comparison.

if (!SeloraxOIDC.validateState(req.query.state, req.session.oauthState)) {
    return res.status(400).send('Invalid state');
}

SeloraxOIDC.checkCallbackError(query) — Check if the callback URL contains an OAuth error.

const error = SeloraxOIDC.checkCallbackError(req.query);
if (error) console.error(error.error, error.description);

SeloraxSignIn — Browser Client

Constructor

new SeloraxSignIn(config)

Methods

login(options?) — Redirect the user to SeloraX authorization. Generates state + PKCE automatically and stores them in sessionStorage.

await signIn.login();
await signIn.login({ scopes: ['openid', 'store'], storeId: 22 });

parseCallback(url?) — Parse the callback URL after redirect. Validates state and returns the authorization code + PKCE verifier.

const result = signIn.parseCallback();

// Success: { code: "sx_ic_...", state: "...", verifier: "..." }
// Error:   { error: "access_denied", description: "..." }

getVerifier() / getState() — Access stored values for manual flows.


Express Middleware

createCallbackHandler(oidc, options)

Creates an Express route handler that processes the OAuth callback automatically: validates state, exchanges the code, optionally fetches user info, and calls your callback.

app.get('/auth/callback', createCallbackHandler(oidc, {
    onSuccess: (req, res, { tokens, user }) => { ... },
    onError: (req, res, error) => { ... },
    fetchUser: true,            // default: true
    serverSideUserInfo: false,  // default: false
}));

Expects req.session.oauthState and req.session.oauthPkce to be set by the login route.


requireAuth(options?)

Middleware that protects routes by checking for an authenticated session.

// Return 401 if not authenticated
app.get('/api/me', requireAuth(), handler);

// Redirect to login page
app.get('/dashboard', requireAuth({ loginUrl: '/auth/login' }), handler);

// Restrict to merchants only
app.get('/admin', requireAuth({ userType: 'merchant' }), handler);

// Custom user extraction
app.get('/api/data', requireAuth({ getUser: (req) => req.customSession?.user }), handler);

Sets req.seloraxUser on authenticated requests.


Scopes

SeloraX supports two categories of scopes: identity scopes (control which user claims are returned) and resource scopes (control access to store data via the API).

Identity Scopes

Resource Scopes

Resource scopes follow the read:resource / write:resource pattern. Requesting write:X implies read:X — you don't need to request both.

Requesting Resource Scopes

Pass resource scopes alongside identity scopes when generating the authorization URL:

const { url, state, pkce } = oidc.getAuthorizationUrl({
    scopes: ['openid', 'profile', 'email', 'read:orders', 'write:products'],
});

The store owner will see a consent screen listing the requested permissions. If the app later requests additional scopes, the user is re-prompted for consent only for the new scopes.

Consent Behavior

  • First authorization: User sees all requested scopes and must approve
  • Returning authorization: If all scopes were previously granted, consent is skipped
  • Scope changes: If the app requests new scopes not previously granted, the consent screen re-appears with "NEW" badges on the additional permissions
  • Token refresh: Refreshed tokens receive the intersection of the originally granted scopes and the client's current allowed_scopes — if a scope is removed from the client, refreshed tokens no longer include it

User Types

The sub claim identifies the user type and ID:

| Format | Description | |--------|-------------| | merchant:7 | Store administrator | | customer:42 | Store customer |

const { userType, userId } = SeloraxOIDC.parseSubject(user.sub);

Token Lifetimes

| Token | Lifetime | Prefix | |-------|----------|--------| | Authorization code | 60 seconds | sx_ic_ | | Access token | 1 hour | sx_it_ | | Refresh token | 30 days | sx_ir_ |


Client Types


Error Handling

The SDK throws typed errors for different failure scenarios:

const { SeloraxOIDCError, SeloraxTokenError, SeloraxAuthorizationError } = require('signin-with-selorax');

try {
    const tokens = await oidc.exchangeCode(code, verifier);
} catch (err) {
    if (err instanceof SeloraxTokenError) {
        // Token exchange or refresh failed
        console.error(err.code);       // e.g. 'invalid_grant'
        console.error(err.response);   // raw server response
    } else if (err instanceof SeloraxOIDCError) {
        // Network error, timeout, or config issue
        console.error(err.code);       // e.g. 'timeout', 'network_error'
    }
}

| Error Class | Codes | When | |-------------|-------|------| | SeloraxOIDCError | missing_config, timeout, network_error, discovery_failed | Configuration or network issues | | SeloraxTokenError | token_exchange_failed, token_refresh_failed, userinfo_failed, invalid_grant | Token endpoint failures | | SeloraxAuthorizationError | authorization_error | Authorization flow errors |


PKCE Utilities

For advanced use cases, PKCE functions are exported directly:

const { generatePKCE, generateCodeVerifier, generateCodeChallenge } = require('signin-with-selorax');

const { verifier, challenge, method } = generatePKCE();
// method is always 'S256'

// Or individually
const verifier = generateCodeVerifier(64);
const challenge = generateCodeChallenge(verifier);

Constants

const {
    SCOPES, TOKEN_PREFIXES, ENDPOINTS,
    RESOURCE_SCOPES, SCOPE_GROUPS, ALL_VALID_SCOPES,
} = require('signin-with-selorax');

// Identity scope names
SCOPES.OPENID       // 'openid'
SCOPES.STORE         // 'store'

// Token prefixes
TOKEN_PREFIXES.ACCESS_TOKEN   // 'sx_it_'
TOKEN_PREFIXES.CLIENT_ID      // 'sx_oc_'

// API endpoints
ENDPOINTS.AUTHORIZE  // '/api/oauth/authorize'
ENDPOINTS.TOKEN      // '/api/oauth/token'
ENDPOINTS.USERINFO   // '/api/oauth/userinfo'

// Resource scope metadata (keyed by scope string)
RESOURCE_SCOPES['read:orders']
// { resource: 'orders', action: 'read', label: 'Read Orders', description: 'View orders and order details' }

// Scope groups (keyed by resource name, useful for building scope selection UIs)
SCOPE_GROUPS.orders
// { label: 'Orders', description: 'Access to store orders', read: 'read:orders', write: 'write:orders', icon: 'Package' }

// All 27 valid scopes (5 identity + 22 resource)
ALL_VALID_SCOPES  // ['openid', 'profile', 'email', 'phone', 'store', 'read:orders', ...]

Discovery

The OpenID Connect discovery document is available at:

GET {issuer}/.well-known/openid-configuration
const discovery = await oidc.getDiscovery();

Returns all supported endpoints, scopes, grant types, and claim types as defined by the OpenID Connect Discovery spec.


Project Structure

signin-with-selorax/
  index.js                         Main entry point
  lib/
    selorax-oidc.js                Core OIDC client (Node.js)
    express.js                     Express callback handler + auth middleware
    pkce.js                        PKCE S256 utilities
    constants.js                   Scopes, endpoints, token prefixes
    errors.js                      Error classes
  frontend/
    selorax-signin.js              Browser client (PKCE via Web Crypto API)

Zero runtime dependencies. Uses Node.js built-in crypto module and native fetch (Node 18+). The browser module uses the Web Crypto API.


License

MIT