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

@blue.ts/auth

v0.2.3

Published

Authentication package for blue.ts. Supports JWT, session cookies, API keys, and HTTP Basic auth — applied per route or group, never globally.

Readme

@blue.ts/auth

Authentication package for blue.ts. Supports JWT, session cookies, API keys, and HTTP Basic auth — applied per route or group, never globally.

Installation

bun add @blue.ts/auth

Quick Start

1. Register the provider

import { AuthProvider } from '@blue.ts/auth';

const auth = new AuthProvider({
    jwt: {
        url: 'https://your-issuer/.well-known/jwks.json',
        issuer: 'https://your-issuer',
        audience: 'your-api',
    },
});

app.registerProvider(auth);

The provider creates and owns the adapter as a singleton in the DI container. Auth middleware is not added globally — apply it explicitly to the routes that need it.

2. Protect routes

import { requireRole, getAuthUser } from '@blue.ts/auth';

// Public routes — no middleware
app.post('/login', loginHandler);

// Protected group — reference auth.jwtMiddleware directly
app.group('/api', [auth.jwtMiddleware], (r) => {
    r.get('/profile', (ctx) => {
        const user = getAuthUser(ctx.req);
        return Context.json({ user });
    });

    // Require a specific role on top of auth
    r.delete('/admin/users/:id', adminHandler, {
        middlewares: [requireRole('admin')],
    });
});

Auth Strategies

JWT

Verifies Bearer tokens using jose. Supports remote JWKS or static keys.

import { JWTAdapter } from '@blue.ts/auth';

// Remote JWKS (recommended)
const jwtAdapter = new JWTAdapter({
    url: 'https://your-issuer/.well-known/jwks.json',
    issuer: 'https://your-issuer',
    audience: 'your-api',
});

// Static keys
const jwtAdapterStatic = new JWTAdapter({ keys: [{ kty: 'oct', k: '...' }] });

// Custom header (default is 'Authorization: Bearer ...')
jwtAdapter.setHeader('X-Auth-Token');

Sessions

Cookie-based sessions backed by a SessionStore. Use the built-in MemorySessionStore or implement your own.

import { MemorySessionStore, AuthProvider } from '@blue.ts/auth';

const store = new MemorySessionStore();
const auth = new AuthProvider({ session: { store, cookie: 'sid' } });

app.registerProvider(auth);

// Protected routes
app.group('/dashboard', [auth.sessionMiddleware], (r) => { ... });

Login handler — create a session and set the cookie:

import { randomUUID } from 'crypto';

app.post('/login', async (ctx) => {
    const { username, password } = await ctx.json<{ username: string; password: string }>();

    const user = await verifyCredentials(username, password);
    if (!user) return Context.json({ error: 'Invalid credentials' }, { status: 401 });

    const sessionId = randomUUID();
    await store.set(sessionId, { id: user.id, roles: user.roles }, 3600); // 1h TTL

    return new Response(null, {
        status: 204,
        headers: { 'Set-Cookie': `sid=${sessionId}; HttpOnly; Path=/; SameSite=Strict` },
    });
});

Logout handler — destroy the session:

app.post('/logout', async (ctx) => {
    const sessionId = ctx.cookies.get('sid');
    if (sessionId) await store.delete(sessionId);

    return new Response(null, {
        status: 204,
        headers: { 'Set-Cookie': 'sid=; HttpOnly; Path=/; Max-Age=0' },
    });
});

API Keys

const auth = new AuthProvider({
    apiKey: {
        keys: ['key-abc', 'key-xyz'],
        header: 'x-api-key', // optional, this is the default
    },
});

app.registerProvider(auth);
app.group('/api', [auth.apiKeyMiddleware], (r) => { ... });

Basic Auth

Provide a verify callback — credentials are never hardcoded in the adapter.

const auth = new AuthProvider({
    basic: {
        verify: async (username, password) => {
            const user = await db.users.findByCredentials(username, password);
            return user ? { id: user.id, roles: user.roles } : null;
        },
    },
});

app.registerProvider(auth);
app.group('/internal', [auth.basicMiddleware], (r) => { ... });

Custom Session Store

Implement SessionStore to use any backend (Redis, database, etc.):

import type { SessionStore, AuthUser } from '@blue.ts/auth';

class RedisSessionStore implements SessionStore {
    async get(id: string): Promise<AuthUser | null> {
        const data = await redis.get(id);
        return data ? JSON.parse(data) : null;
    }

    async set(id: string, user: AuthUser, ttlSeconds = 3600): Promise<void> {
        await redis.set(id, JSON.stringify(user), 'EX', ttlSeconds);
    }

    async delete(id: string): Promise<void> {
        await redis.del(id);
    }
}

Custom Adapter

Extend Adapter and pass it directly to createAuthMiddleware for strategies not covered by the built-in options:

import { Adapter, createAuthMiddleware } from '@blue.ts/auth';
import type { AuthUser } from '@blue.ts/auth';

class HMACAdapter extends Adapter {
    async authenticate(request: Request): Promise<AuthUser | null> {
        const sig = request.headers.get('x-signature');
        if (!sig || !verifyHMAC(request, sig)) return null;
        return { id: 'service-account' };
    }
}

const hmacMiddleware = createAuthMiddleware(new HMACAdapter());
app.group('/webhooks', [hmacMiddleware], (r) => { ... });

Reading the Authenticated User

After createAuthMiddleware runs, the authenticated user is available anywhere in the request lifecycle:

import { getAuthUser } from '@blue.ts/auth';

app.get('/me', (ctx) => {
    const user = getAuthUser(ctx.req); // AuthUser | undefined
    return Context.json(user);
});

RBAC

requireRole must be used after createAuthMiddleware in the middleware chain:

import { createAuthMiddleware, requireRole } from '@blue.ts/auth';

app.group('/admin', [createAuthMiddleware(jwtAdapter), requireRole('admin')], (r) => {
    r.get('/dashboard', dashboardHandler);
});

// Or per-route
app.delete('/posts/:id', deleteHandler, {
    middlewares: [createAuthMiddleware(jwtAdapter), requireRole('editor', 'admin')],
});