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

@lumen-id/auth-sdk

v0.1.0

Published

Official Lumen IDP SDK for web and backend applications

Readme

@lumen/auth-sdk

Official Lumen IDP SDK for web and backend applications. Provides seamless OAuth2/OIDC integration with PKCE flow.

Features

  • Framework-agnostic core - Works with any framework or vanilla JS
  • React integration - Provider and hook for easy React integration
  • Backend support - Express, Fastify, and NestJS middleware/guards
  • Automatic token refresh - Keeps sessions alive seamlessly
  • Granular data access - Field-level consent flow with requestDataAccess + getUserData()
  • TypeScript-first - Full TypeScript support with strict types
  • CDN ready - Use directly from unpkg or jsdelivr

Installation

npm install @lumen/auth-sdk
# or
yarn add @lumen/auth-sdk
# or
pnpm add @lumen/auth-sdk

CDN Usage

<!-- For core SDK -->
<script src="https://unpkg.com/@lumen/auth-sdk/dist/index.global.js"></script>

<!-- For React -->
<script src="https://unpkg.com/@lumen/auth-sdk/dist/react.global.js"></script>

Quick Start

1. Core SDK (Framework-agnostic)

import { LumenAuthClient } from '@lumen/auth-sdk';

const client = new LumenAuthClient({
  clientId: 'your-client-id',
  issuer: 'https://api.lumen.com/idp',
  redirectUri: 'http://localhost:5173/callback',
  scopes: ['openid', 'profile', 'email', 'offline_access'],
  autoRefresh: true,
});

// Initiate login
await client.login();

// Handle callback (on your callback page)
const session = await client.handleCallback();
console.log('User:', session.user);

// Check authentication status
if (client.isAuthenticated()) {
  const user = client.getSession()?.user;
}

// Logout
await client.logout();

2. React Integration

import { LumenAuthProvider, useLumenAuth } from '@lumen/auth-sdk/react';

// App setup
function App() {
  return (
    <LumenAuthProvider
      config={{
        clientId: 'your-client-id',
        issuer: 'https://api.lumen.com/idp',
        redirectUri: 'http://localhost:5173/callback',
        scopes: ['openid', 'profile', 'email'],
      }}
    >
      <YourApp />
    </LumenAuthProvider>
  );
}

// Component usage
function UserProfile() {
  const { isAuthenticated, user, login, logout, isLoading } = useLumenAuth();

  if (isLoading) return <div>Loading...</div>;

  if (!isAuthenticated) {
    return <button onClick={login}>Login with Lumen</button>;
  }

  return (
    <div>
      <h1>Welcome, {user?.name}</h1>
      <p>Email: {user?.email}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

3. Backend Integration

Express

import express from 'express';
import { createLumenAuthMiddleware } from '@lumen/auth-sdk/backend';

const app = express();

app.use(createLumenAuthMiddleware({
  issuer: 'https://api.lumen.com/idp',
  publicRoutes: ['/health', '/public'],
}));

app.get('/protected', (req, res) => {
  res.json({ 
    message: 'Hello, authenticated user!',
    user: req.user 
  });
});

Fastify

import Fastify from 'fastify';
import lumenAuth from '@lumen/auth-sdk/backend/fastify';

const app = Fastify();

app.register(lumenAuth, {
  issuer: 'https://api.lumen.com/idp',
  publicRoutes: ['/health'],
});

app.get('/protected', async (request, reply) => {
  return { user: request.user };
});

NestJS

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { createLumenAuthGuard, CurrentUser, Public } from '@lumen/auth-sdk/backend';

const LumenAuthGuard = createLumenAuthGuard({
  issuer: 'https://api.lumen.com/idp',
});

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: LumenAuthGuard,
    },
  ],
})
export class AppModule {}

// Controller
@Controller('api')
export class ApiController {
  @Public()
  @Get('health')
  health() {
    return { status: 'ok' };
  }

  @Get('profile')
  getProfile(@CurrentUser() user: UserInfo) {
    return user;
  }
}

Configuration

LumenAuthConfig

| Option | Type | Required | Default | Description | |--------|------|----------|---------|-------------| | clientId | string | Yes | - | OAuth client ID | | issuer | string | Yes | - | Lumen IDP issuer URL | | redirectUri | string | Yes | - | Callback URL after login | | apiBaseUrl | string | No | derived from issuer | Override API base URL for getUserData() | | scopes | string[] | No | ['openid', 'profile', 'email'] | Requested scopes | | requestDataAccess | boolean | { fields?: string[] } | No | - | Request field-level user data access (see below) | | autoRefresh | boolean | No | true | Auto-refresh tokens before expiry | | refreshBufferSeconds | number | No | 60 | Seconds before expiry to refresh |

Scopes

  • openid - Required for OIDC
  • profile - Access to name, preferred_username
  • email - Access to email, email_verified
  • offline_access - Get refresh_token for offline access
  • data - Required for reading user data fields (set automatically via requestDataAccess)

Data Access

Lumen lets users grant apps read access to specific fields in their data store. The user reviews the fields individually on the IdP consent screen before any code is issued — your app gets exactly the fields the user ticked.

Requesting access

Set requestDataAccess in the client config. The SDK automatically appends the data scope to the authorization URL and the Lumen IdP shows the field-consent screen before redirecting back.

const client = new LumenAuthClient({
  clientId: 'my-app',
  issuer: 'https://api.lumen.com/idp',
  redirectUri: 'https://myapp.com/callback',
  // Append 'data' scope → IdP shows field-consent screen
  requestDataAccess: true,
  // Or hint which fields you need (user still decides individually):
  // requestDataAccess: { fields: ['email', 'first_name'] },
});

Reading a consented field

After the user has logged in and consented, call getUserData(key) to read a field.

import type { AuthError } from '@lumen/auth-sdk';

try {
  const item = await client.getUserData('email');
  console.log(item.value); // "[email protected]"
} catch (err) {
  const e = err as AuthError;
  if (e.code === 'access_not_granted') {
    // User did not consent to this field, or later revoked it
  }
}

getUserData throws typed AuthError with these codes:

| Code | Cause | |------|-------| | unauthenticated | No active session | | access_not_granted | No active grant covering this field (HTTP 403) | | user_data_fetch_failed | Other API error |

React usage

import { LumenAuthProvider, useLumenAuth } from '@lumen/auth-sdk/react';
import type { AuthError } from '@lumen/auth-sdk';

function App() {
  return (
    <LumenAuthProvider
      config={{
        clientId: 'my-app',
        issuer: 'https://api.lumen.com/idp',
        redirectUri: window.location.origin + '/callback',
        requestDataAccess: { fields: ['email', 'first_name'] },
      }}
    >
      <Profile />
    </LumenAuthProvider>
  );
}

function Profile() {
  const { client, isAuthenticated, login } = useLumenAuth();
  const [email, setEmail] = useState<string | null>(null);

  useEffect(() => {
    if (!isAuthenticated) return;
    client
      .getUserData('email')
      .then((item) => setEmail(item.value as string))
      .catch((err) => {
        if ((err as AuthError).code === 'access_not_granted') {
          setEmail('(not shared)');
        }
      });
  }, [client, isAuthenticated]);

  if (!isAuthenticated) return <button onClick={login}>Login</button>;
  return <p>Email: {email ?? '…'}</p>;
}

How it works

  1. Your app calls client.login() with the data scope included
  2. The user authenticates on the Lumen IdP
  3. If no grant exists for your app, the IdP shows the field-consent screen — a list of the user's data fields with checkboxes
  4. The user ticks the fields they want to share and submits
  5. The IdP creates a DataAccessGrant with the selected fields and issues the authorization code
  6. Your app exchanges the code for tokens and can now call getUserData(key) for any consented field
  7. The user can view, adjust, and revoke access at any time on the Connected Services page in their Lumen account

Every read is verified server-side: the API checks the grant on each request, so revocations take effect immediately without waiting for a token to expire.

API Reference

Core Client

LumenAuthClient

Main SDK client class.

const client = new LumenAuthClient(config, storage?);

Methods:

  • login() - Redirect to Lumen IDP for authentication
  • handleCallback(url?) - Handle OAuth callback, exchange code for tokens
  • logout() - Revoke tokens and clear session
  • getSession() - Get current session
  • isAuthenticated() - Check if user is authenticated
  • refreshTokens() - Manually refresh access token
  • getUserData(key, userId?) - Read a consented user data field (requires requestDataAccess)

React Hook

useLumenAuth()

React hook for accessing auth state and methods.

const {
  session,           // Current session
  user,              // User info
  status,            // 'unauthenticated' | 'authorizing' | 'authenticated' | 'error'
  isAuthenticated,   // Boolean
  isLoading,         // Boolean
  isError,           // Boolean
  login,             // () => Promise<void>
  logout,            // () => Promise<void>
  handleCallback,    // (url?) => Promise<Session>
  refresh,           // () => Promise<Session | null>
} = useLumenAuth();

Error Handling

All errors are instances of AuthError with a code property:

import { AuthError } from '@lumen/auth-sdk';

try {
  await client.handleCallback();
} catch (error) {
  if (error instanceof AuthError) {
    console.log(error.code); // 'state_mismatch', 'token_exchange_failed', etc.
    console.log(error.message);
  }
}

Common error codes:

| Code | Source | Meaning | |------|--------|---------| | state_mismatch | handleCallback | CSRF protection triggered | | missing_code | handleCallback | No authorization code in callback URL | | token_exchange_failed | handleCallback | Token endpoint rejected the code | | no_refresh_token | refreshTokens | Session has no refresh token | | refresh_failed | refreshTokens | Refresh token was revoked or expired | | unauthenticated | getUserData | No active session | | access_not_granted | getUserData | No active grant covering this field | | user_data_fetch_failed | getUserData | Unexpected API error |

Storage

By default, the SDK uses localStorage for session persistence. You can provide a custom storage adapter:

import { LumenAuthClient, createMemoryStorageAdapter } from '@lumen/auth-sdk';

const memoryStorage = createMemoryStorageAdapter();
const client = new LumenAuthClient(config, memoryStorage);

Development

# Install dependencies
npm install

# Run in watch mode
npm run dev

# Build
npm run build

# Run tests
npm test

# Type check
npm run typecheck

# Lint
npm run lint

License

MIT © Lumen Team