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

astro-sessionkit

v0.1.14

Published

Simple session access and route protection for Astro applications

Readme

🔐 Astro SessionKit

Simple session access and route protection for Astro applications.

Features

  • Simple API - Access session data anywhere in your app
  • 🛡️ Route Protection - Declarative route guards with roles/permissions
  • 🚀 Type Safe - Full TypeScript support
  • 🎯 Flexible - Works with any session storage (cookies, Redis, DB, etc.)
  • Fast - Uses AsyncLocalStorage for zero-overhead access

Installation

npm install astro-sessionkit

Quick Start

1. Configure the Integration

// astro.config.mjs
import { defineConfig } from 'astro/config';
import sessionkit from 'astro-sessionkit';

export default defineConfig({
    integrations: [
        sessionkit({
            loginPath: '/login',
            protect: [
                // Protect admin routes
                { pattern: '/admin/**', role: 'admin' },

                // Protect dashboard for authenticated users
                { pattern: '/dashboard', roles: ['user', 'admin'] },

                // Protect by permission
                { pattern: '/settings', permission: 'settings:write' },

                // Custom logic
                {
                    pattern: '/premium/**',
                    allow: (session) => session?.subscription === 'premium'
                }
            ]
        })
    ]
});

2. Set Up Your Session

SessionKit reads from context.session.get('__session__'). You set it up in your middleware:

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';

export const onRequest = defineMiddleware(async (context, next) => {
  // Get session from wherever you store it
  // (cookies, Redis, database, etc.)
  const sessionId = context.cookies.get('session_id')?.value;
  
  if (sessionId) {
    const user = await db.getUserBySessionId(sessionId);
    
    // Set session for SessionKit to read
    context.session.set('__session__', {
      userId: user.id,
      email: user.email,
      role: user.role,
      permissions: user.permissions
    });
  }
  
  return next();
});

3. Register Sessions After Authentication

Use the provided helpers to register sessions after successful authentication:

// src/pages/api/login.ts
import type { APIRoute } from 'astro';
import { setSession } from 'astro-sessionkit/server';

export const POST: APIRoute = async (context) => {
  const { email, password } = await context.request.json();
  
  // Verify credentials (YOUR authentication logic)
  const user = await verifyCredentials(email, password);
  
  if (user) {
    // Register session with SessionKit
    setSession(context, {
      userId: user.id,
      email: user.email,
      role: user.role,
      permissions: user.permissions
    });
    
    // Store session ID (YOUR storage logic)
    const sessionId = await createSessionInDatabase(user.id);
    context.cookies.set('session_id', sessionId, {
      httpOnly: true,
      secure: true,
      sameSite: 'lax',
      maxAge: 60 * 60 * 24 * 7 // 7 days
    });
    
    return new Response(JSON.stringify({ success: true }));
  }
  
  return new Response(JSON.stringify({ error: 'Invalid credentials' }), {
    status: 401
  });
};

4. Use Session in Your Pages

---
// src/pages/dashboard.astro
import { getSession, requireSession } from 'astro-sessionkit/server';

// Get session (returns null if not authenticated)
const session = getSession();

// Or require authentication (throws 401 if not authenticated)
const session = requireSession();
---

<h1>Welcome, {session.email}</h1>

API Reference

Session Management Functions

setSession(context, session)

Register a session after successful authentication.

Parameters:

  • context: APIContext - Astro API context
  • session: Session - Session data to register

Throws: Error if session structure is invalid

import { setSession } from 'astro-sessionkit/server';

export const POST: APIRoute = async (context) => {
  const user = await authenticateUser(credentials);
  
  setSession(context, {
    userId: user.id,
    email: user.email,
    role: user.role,
    permissions: user.permissions
  });
  
  // Also store in cookies/database
  context.cookies.set('session_id', sessionId);
};

clearSession(context)

Clear the session during logout.

import { clearSession } from 'astro-sessionkit/server';

export const POST: APIRoute = async (context) => {
  clearSession(context);
  
  // Also delete from cookies/database
  context.cookies.delete('session_id');
  await db.deleteSession(sessionId);
  
  return context.redirect('/');
};

updateSession(context, updates)

Update specific fields in the current session.

Parameters:

  • context: APIContext - Astro API context
  • updates: Partial<Session> - Fields to update

Throws: Error if no session exists or updated session is invalid

import { updateSession } from 'astro-sessionkit/server';

export const POST: APIRoute = async (context) => {
  // Update user's role
  updateSession(context, { 
    role: 'admin',
    permissions: ['admin:read', 'admin:write']
  });
  
  // Also update in your storage
  await db.updateSession(sessionId, updates);
};

Session Access Functions

All functions are imported from astro-sessionkit/server:

getSession()

Get the current session (returns null if not authenticated).

import { getSession } from 'astro-sessionkit/server';

const session = getSession();
if (session) {
  console.log('User ID:', session.userId);
}

requireSession()

Get the current session or throw 401 if not authenticated.

import { requireSession } from 'astro-sessionkit/server';

const session = requireSession();
// TypeScript knows session is not null here

isAuthenticated()

Check if the user is authenticated.

import { isAuthenticated } from 'astro-sessionkit/server';

if (isAuthenticated()) {
  // User is logged in
}

hasRole(role: string)

Check if user has a specific role.

import { hasRole } from 'astro-sessionkit/server';

if (hasRole('admin')) {
  // User is an admin
}

hasPermission(permission: string)

Check if user has a specific permission.

import { hasPermission } from 'astro-sessionkit/server';

if (hasPermission('posts:delete')) {
  // User can delete posts
}

hasAllPermissions(...permissions: string[])

Check if user has ALL specified permissions.

import { hasAllPermissions } from 'astro-sessionkit/server';

if (hasAllPermissions('posts:read', 'posts:write')) {
  // User has both permissions
}

hasAnyPermission(...permissions: string[])

Check if user has ANY of the specified permissions.

import { hasAnyPermission } from 'astro-sessionkit/server';

if (hasAnyPermission('posts:delete', 'admin:panel')) {
  // User has at least one permission
}

hasRolePermission(role: string, permission: string)

Check if user has a specific role AND a specific permission.

import { hasRolePermission } from 'astro-sessionkit/server';

if (hasRolePermission('admin', 'delete users')) {
  // User is admin AND has 'delete users' permission
}

Route Protection

Protection Rules

By Role

Require a specific role:

const rule = { pattern: '/admin/**', role: 'admin' };

By Multiple Roles

User must have ONE of these roles:

const rule = { pattern: '/dashboard', roles: ['user', 'admin', 'moderator'] };

By Permission

Require a specific permission:

const rule = { pattern: '/settings', permission: 'settings:write' };

By Multiple Permissions

User must have ALL of these permissions:

{ pattern: '/admin/users', permissions: ['users:read', 'users:write'] }

Custom Logic

Use a custom function for complex logic:

{ 
  pattern: '/premium/**', 
  allow: (session) => {
    return session?.subscription === 'premium' && !session?.banned;
  }
}

Custom Redirect

Override the default login path per rule:

{ 
  pattern: '/admin/**', 
  role: 'admin',
  redirectTo: '/unauthorized'
}

Pattern Matching

Patterns support glob syntax:

  • /admin - Exact match
  • /admin/* - One or more segments (/admin/users, /admin/users/123)
  • /admin/** - Any path under admin (/admin, /admin/users, /admin/x/y/z)

Session Type

The session object must have this shape:

interface Session {
  userId: string;           // Required
  email?: string;
  role?: string;
  roles?: string[];
  permissions?: string[];
  [key: string]: unknown;   // Add any custom fields
}

You control what goes in the session - SessionKit just reads it.

Advanced Configuration

Custom Access Hooks

Override how roles/permissions are extracted:

sessionkit({
  access: {
    // Custom role extraction
    getRole: (session) => session?.primaryRole ?? null,
    
    // Custom permissions extraction
    getPermissions: (session) => {
      return [...session?.permissions ?? [], ...session?.dynamicPerms ?? []];
    },
    
    // Override all built-in checks
    check: (rule, session) => {
      // Your custom logic
      return session?.customField === 'allowed';
    }
  }
})

Custom Context Store

By default, SessionKit uses Node's AsyncLocalStorage to manage the session context. In some environments (like certain edge runtimes), you might need to provide your own context management.

sessionkit({
  // Provide a custom way to run code within a context
  runWithContext: (context, fn) => {
    // Your custom context implementation
    return myCustomStorage.run(context, fn);
  },

  // Provide a custom way to retrieve the current context
  getContextStore: () => {
    return myCustomStorage.getStore();
  }
})

Logout Flow

// src/pages/api/logout.ts
import type { APIRoute } from 'astro';
import { clearSession } from 'astro-sessionkit/server';

export const POST: APIRoute = async (context) => {
  const sessionId = context.cookies.get('session_id')?.value;
  
  // Clear from SessionKit
  clearSession(context);
  
  // Delete from storage
  if (sessionId) {
    await db.deleteSession(sessionId);
  }
  context.cookies.delete('session_id');
  
  return context.redirect('/login');
};

Update Session Data

// src/pages/api/update-role.ts
import type { APIRoute } from 'astro';
import { updateSession } from 'astro-sessionkit/server';

export const POST: APIRoute = async (context) => {
  const { newRole } = await context.request.json();
  
  // Update in SessionKit
  updateSession(context, { role: newRole });
  
  // Also update in your database
  const sessionId = context.cookies.get('session_id')?.value;
  await db.updateUserRole(sessionId, newRole);
  
  return new Response(JSON.stringify({ success: true }));
};

Examples

Complete Authentication Flow

// src/pages/api/auth/login.ts
import type { APIRoute } from 'astro';
import { setSession } from 'astro-sessionkit/server';
import { hashPassword, generateSessionId } from './utils';

export const POST: APIRoute = async (context) => {
  const { email, password } = await context.request.json();
  
  // 1. Verify credentials
  const user = await db.findUserByEmail(email);
  if (!user || !await hashPassword.verify(password, user.hashedPassword)) {
    return new Response(JSON.stringify({ error: 'Invalid credentials' }), {
      status: 401
    });
  }
  
  // 2. Create session ID
  const sessionId = generateSessionId();
  await db.createSession({
    id: sessionId,
    userId: user.id,
    expiresAt: Date.now() + (7 * 24 * 60 * 60 * 1000) // 7 days
  });
  
  // 3. Register with SessionKit
  setSession(context, {
    userId: user.id,
    email: user.email,
    role: user.role,
    permissions: user.permissions
  });
  
  // 4. Set secure cookie
  context.cookies.set('session_id', sessionId, {
    httpOnly: true,
    secure: import.meta.env.PROD,
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7, // 7 days
    path: '/'
  });
  
  return new Response(JSON.stringify({ 
    success: true,
    user: { email: user.email, role: user.role }
  }));
};

Examples

Protect Multiple Route Patterns

sessionkit({
  protect: [
    // Admin section
    { pattern: '/admin/**', role: 'admin', redirectTo: '/unauthorized' },
    
    // User dashboard
    { pattern: '/dashboard', roles: ['user', 'admin'] },
    
    // Settings require specific permission
    { pattern: '/settings/**', permission: 'settings:access' },
    
    // Premium content
    { 
      pattern: '/premium/**',
      allow: (session) => session?.tier === 'premium'
    }
  ]
})

Conditional Rendering

---
import { hasRole, hasPermission } from 'astro-sessionkit/server';
---

{hasRole('admin') && (
  <a href="/admin">Admin Panel</a>
)}

{hasPermission('posts:create') && (
  <button>Create Post</button>
)}

API Route Protection

// src/pages/api/admin.ts
import type { APIRoute } from 'astro';
import { requireSession, hasRole } from 'astro-sessionkit/server';

export const GET: APIRoute = async () => {
    const session = requireSession();

    if (!hasRole('admin')) {
        return new Response('Forbidden', { status: 403 });
    }

    // Admin logic here
    return new Response(JSON.stringify({ data: 'secret' }));
};

How It Works

  1. You set the session in context.locals.session via your own middleware
  2. SessionKit reads it and makes it available via AsyncLocalStorage
  3. Route guards automatically protect paths based on your rules
  4. Helper functions provide easy access throughout your app

Security

⚠️ Important: SessionKit handles session access and route protection, but does NOT handle:

  • Session creation/storage
  • Authentication
  • Session expiration
  • CSRF protection

These are your responsibility. See SECURITY.md for a complete security guide.

Quick Security Checklist

Before production:

  • ✅ Encrypt/sign your sessions (use lucia-auth, @auth/astro, or iron-session)
  • ✅ Set secure cookie flags (HttpOnly, Secure, SameSite)
  • ✅ Implement session expiration
  • ✅ Add CSRF protection for state-changing operations
  • ✅ Use HTTPS in production

License

MIT License © Alex Mora