Socket
Book a DemoInstallSign in
Socket

graphql-knifey

Package Overview
Dependencies
Maintainers
1
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-knifey

Tools for dealing with graphql server side

3.0.5
latest
npmnpm
Version published
Weekly downloads
1.4K
9486.67%
Maintainers
1
Weekly downloads
 
Created
Source

GraphQL Knifey

Reuse code for generic GraphQL projects with built-in session/cookie authentication support.

Version 2.7.0 Features

  • 🍪 Cookie-based session authentication with Redis storage
  • 🔐 Multiple auth strategies: Cookie, JWT, and Hybrid modes
  • 🔄 Seamless migration path from JWT to sessions
  • 🏗️ Clean architecture with separated context creation logic
  • 📦 DI-why integration for dependency injection

Table of Contents

  • Basic Usage
  • Session & Cookie Authentication
  • Architecture
  • Migration from JWT to Sessions
  • Customize/Augment appConfig
  • Cookies vs Authorization Header

Basic Usage

import diContainer, { apolloContextLDEGen, apolloServerLDEGen, appConfigLDEGen } from 'graphql-knifey';

// create your injection dict
const myInjectionDict = {
  // map config from env
  apolloContext: apolloContextLDEGen({
    userService: 'userService',
    tokenAuthService: 'tokenAuthService',
  }),
  apolloServer: apolloServerLDEGen(resolvers, graphqlSchema),
  aService: aServiceLoadDictElement,
  // ... can override graphql-knifey entries
};

// pass it to graphql-knifey's default one
diContainer.addToLoadDict(myInjectionDict);

graphql-knifey now includes comprehensive session management and cookie-based authentication support, making it easy to migrate from JWT tokens to secure HTTP-only cookies.

Quick Start with Sessions

import { 
  apolloContextWithAuthLDEGen,
  sessionServiceLDEGen,
  cookieStrategyLDEGen,
  apolloStandaloneServerLDEGen
} from 'graphql-knifey';

diContainer.addToLoadDict({
  // Session service with Redis storage
  sessionService: sessionServiceLDEGen({
    sessionTTL: 7200,    // 2 hours
    refreshTTL: 604800,  // 7 days
  }),
  
  // Cookie-based authentication strategy
  authStrategy: cookieStrategyLDEGen(),
  
  // Configure authentication mode in appConfig
  appConfig: appConfigLDEGen({
    authMode: 'cookie', // 'cookie' | 'jwt' | 'hybrid'
    sessionCookieName: 'sid',
    refreshCookieName: 'rid',
  }),
  
  // Apollo context with auth support
  apolloContext: apolloContextWithAuthLDEGen({
    userService: 'userService',
    authService: 'authService',
    // any other services needed by resolvers
  }),
  
  // Apollo server (already configured for cookies)
  apolloServer: apolloStandaloneServerLDEGen(resolvers, typeDefs),
});

Session Service

The SessionService manages server-side sessions with Redis or any compatible storage:

import { SessionService, SessionServiceInterface } from 'graphql-knifey';

interface SessionData {
  userId: string;
  metadata?: Record<string, any>;
}

// Methods available:
sessionService.create(userId, metadata)       // Returns { sessionId, refreshId }
sessionService.validate(sessionId)            // Returns SessionData or null
sessionService.refresh(refreshId)             // Returns new { sessionId, refreshId }
sessionService.revoke(sessionId)              // Revokes a session
sessionService.revokeAllUserSessions(userId)  // Revokes all user sessions

Authentication Strategies

Three strategies are provided for different use cases:

import { cookieStrategyLDEGen } from 'graphql-knifey';

authStrategy: cookieStrategyLDEGen()

2. JWT Strategy (Backward Compatibility)

import { jwtStrategyLDEGen } from 'graphql-knifey';

authStrategy: jwtStrategyLDEGen()

3. Hybrid Strategy (Migration)

import { hybridStrategyLDEGen } from 'graphql-knifey';

authStrategy: hybridStrategyLDEGen(true) // true = prefer cookies

Apollo Context with Auth

The enhanced Apollo context provides auth helpers in your resolvers:

// Your resolver context will include:
interface AuthContextResult {
  // Request/Response
  req: Request;
  res: Response;
  IP: string;
  
  // Auth state
  authenticated: boolean;
  userId?: string;
  user?: any;
  
  // Cookie helpers
  sessionId?: string;
  refreshId?: string;
  setAuthCookies: (args: {
    sessionId?: string;
    refreshId?: string;
    sessionMaxAgeSec?: number;
    refreshMaxAgeSec?: number;
  }) => void;
  clearAuthCookies: () => void;
  
  // Authentication configuration used
  config: {
    authStrategy?: AuthStrategy;
    sessionService?: SessionServiceInterface;
    sessionAdapter?: any;
  }
  
  // Backward compatibility
  token?: string;
  
  // Your injected services
  [key: string]: any;
}

Example Resolvers

const resolvers = {
  Mutation: {
    login: async (_, { email, password }, context) => {
      const user = await context.authService.verify(email, password);
      if (!user) return { success: false };
      
      // Create session using the configured session service
      const { sessionId, refreshId } = await context.config.sessionService.create(user.id);
      
      // Set HTTP-only cookies
      context.setAuthCookies({
        sessionId,
        refreshId,
        sessionMaxAgeSec: 7200,     // 2 hours
        refreshMaxAgeSec: 604800,   // 7 days
      });
      
      return { success: true, user };
    },
    
    logout: async (_, __, context) => {
      if (context.sessionId && context.config.sessionService) {
        await context.config.sessionService.revoke(context.sessionId);
      }
      context.clearAuthCookies();
      return { success: true };
    },
    
    refreshSession: async (_, __, context) => {
      if (!context.refreshId || !context.config.sessionService) {
        return { success: false };
      }
      
      const result = await context.config.sessionService.refresh(context.refreshId);
      if (!result) return { success: false };
      
      context.setAuthCookies({
        sessionId: result.sessionId,
        refreshId: result.refreshId,
      });
      
      return { success: true };
    }
  },
  
  Query: {
    me: async (_, __, context) => {
      if (!context.authenticated) {
        throw new Error('Not authenticated');
      }
      return context.user;
    }
  }
};

Architecture

Context Creation Flow

The authentication context is created through a clean separation of concerns:

  • Configuration - Authentication settings (mode, cookie names) come from appConfig
  • Context Factory - apolloContextWithAuthLDEGen creates the context factory function
  • Context Creation - createAuthContext handles the actual authentication logic per request
import { createAuthContext, apolloContextWithAuthLDEGen } from 'graphql-knifey';

// The loader generator fetches configuration and services
apolloContext: apolloContextWithAuthLDEGen({
  // Services to inject into resolver context
  userService: 'userService',
  postService: 'postService',
})

// Behind the scenes, createAuthContext is called for each request:
// createAuthContext(params, { config, sharedContext })

The createAuthContext function:

  • Extracts authentication from cookies or JWT headers
  • Validates sessions or tokens
  • Returns a unified context with auth state and helpers
  • Handles all three auth modes: cookie, JWT, and hybrid

Migration from JWT to Sessions

Migration Adapter

The SessionToJWTAdapter helps existing JWT-based code work with sessions:

import { sessionToJWTAdapterLDEGen, authServiceAdapterLDEGen } from 'graphql-knifey';

diContainer.addToLoadDict({
  // Makes sessions work like JWTs for existing code
  sessionAdapter: sessionToJWTAdapterLDEGen(),
  
  // Wraps existing auth service to support sessions
  authServiceAdapter: authServiceAdapterLDEGen(),
  
  // Use hybrid mode during migration
  authStrategy: hybridStrategyLDEGen(true),
});

Gradual Migration Path

  • Phase 1: Add Session Support (Hybrid Mode)

    authMode: 'hybrid' // Supports both JWT and cookies
    
    • Existing JWT clients continue working
    • New clients can use cookies
    • Both auth methods work simultaneously
  • Phase 2: Migrate Clients

    • Update clients to use credentials: 'include'
    • Remove Authorization header logic
    • Test with cookies
  • Phase 3: Cookie-Only Mode

    authMode: 'cookie' // Only cookies accepted
    
    • Remove JWT generation code
    • Simplify auth logic
    • Better security with HTTP-only cookies

Environment Configuration

Add these to your .env:

# Cookie Configuration
COOKIE_SECRET=your-secret-key-here
COOKIE_DOMAIN=.example.com
SESSION_COOKIE_NAME=sid
REFRESH_COOKIE_NAME=rid
SESSION_TTL=7200
REFRESH_TTL=604800
AUTH_MODE=hybrid  # cookie | jwt | hybrid

App Config Updates

const appConfigGen = (env) => ({
  // ... existing config
  
  // Cookie/session configuration
  cookieSecret: env.COOKIE_SECRET,
  cookieDomain: env.COOKIE_DOMAIN,
  accessCookieName: env.SESSION_COOKIE_NAME || 'sid',
  refreshCookieName: env.REFRESH_COOKIE_NAME || 'rid',
  sessionTTL: parseInt(env.SESSION_TTL) || 7200,
  refreshTTL: parseInt(env.REFRESH_TTL) || 604800,
  authMode: env.AUTH_MODE || 'hybrid',
});

Customize/Augment appConfig

import diContainer, { appConfigLDEGen, mergeToDefaultAppConfigMap } from 'graphql-knifey';
import localAppConfigMap from '../config/appConfig';

// create your injection dict
const myInjectionDict = {
  // override defaultAppConfig with a merge between defaultAppConfigMap and your localAppConfigMap
  appConfig: appConfigLDEGen(mergeToDefaultAppConfigMap(localAppConfigMap)),
  // ... rest of entries as in previous example
};

// pass it to graphql-knifey's default one
diContainer.addToLoadDict(myInjectionDict);

Cookies vs Authorization Header

Cookies in a GraphQL API can get tricky because they involve both server CORS setup and browser behavior. Let's break it down in context of two setups (public vs. subgraph).

1. Why cookies are special

Unlike headers (Authorization: Bearer ...), cookies are automatically attached by browsers only if three conditions are satisfied:

  • CORS allows credentials On the server:

    cors({ origin: "https://app.example.com", credentials: true })
    

    On the client (fetch/Apollo Client):

    fetch("/graphql", {
      method: "POST",
      credentials: "include",   // <— critical
    });
    
  • Cookie attributes must be set properly:

    • Secure: required if served over HTTPS (mandatory in Chrome if SameSite=None).
    • HttpOnly: prevents JS from reading the cookie — highly recommended for session tokens.
    • SameSite:
      • Lax (default): cookie sent for top-level navigation (GET), but not for subrequests (like GraphQL fetch from a SPA).
      • Strict: cookie only sent if the site is the same origin — breaks cross-origin APIs.
      • None: cookie always sent, but must also have Secure.
  • Domain and path must cover your frontend. Example: if API is on api.example.com and frontend on app.example.com, you'd typically set:

    Set-Cookie: session=abc123; Domain=.example.com; Path=/; SameSite=None; Secure; HttpOnly
    

2. Public Graph (browser-facing)

Here you must configure cookies + CORS correctly if you use them for auth:

  • Server CORS config:

    app.use(
      "/graphql",
      cors({
        origin: "https://app.example.com",
        credentials: true,   // must be true for cookies
      }),
      express.json(),
      expressMiddleware(server, { context })
    );
    
  • Client fetch/Apollo Client config:

    const client = new ApolloClient({
      uri: "https://api.example.com/graphql",
      cache: new InMemoryCache(),
      credentials: "include",   // tells browser to send cookies
    });
    
  • Cookie issue example: If you forget SameSite=None; Secure, the cookie won't be sent in cross-origin requests (browser silently drops it). This is the #1 cause of "why isn't my cookie auth working?"

3. Subgraph (behind a gateway)

Cookies almost never matter here:

  • Browsers don't talk to the subgraph directly → no CORS, no cookies.
  • The gateway might forward headers (e.g., Authorization) to the subgraphs.
  • If you wanted to forward cookies, the gateway would parse them into headers first.

👉 So: you don't need cookie setup on the subgraph at all.

4. Security Best Practices

  • For public graph:

    • Use cookies only if you want true browser-based sessions (good for web apps, SSR, refresh tokens).
    • Always set SameSite=None; Secure; HttpOnly for cross-origin.
    • Configure CORS with credentials: true on both sides.
    • Use CSRF tokens for state-changing operations.
    • Implement rate limiting and session monitoring.
  • For subgraph: ignore cookies; forward an Authorization header from the gateway.

  • Session Security:

    • Use strong session IDs (UUIDs)
    • Implement session rotation on privilege escalation
    • Store minimal data in sessions
    • Set appropriate TTLs (short for sessions, longer for refresh tokens)
    • Implement proper session revocation

TypeScript Support

All exports are fully typed. Key types include:

import type {
  SessionServiceInterface,
  SessionData,
  AuthStrategy,
  AuthResult,
  ValidationResult,
  PublicGraphContextWithAuth,
} from 'graphql-knifey';

Requirements

  • Node.js 18+
  • Redis (for session storage)
  • TypeScript 4.5+

License

ISC

FAQs

Package last updated on 05 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.