/templates
/api key

API Key Authentication Template

Overview

The typescript-auth-api-key template demonstrates how to build an MCP server with API key authentication using NitroStack's built-in ApiKeyModule.

What you'll learn:

  • āœ… How to add API key authentication to your MCP server
  • āœ… How to create public and protected tools
  • āœ… How to use ApiKeyModule and ApiKeyGuard
  • āœ… How to test API key auth in NitroStack Studio
  • āœ… How to combine JWT and API key authentication (multi-auth)

Quick Start

# Create a new project
nitrostack init my-api-server

# Choose: šŸ”‘ API Key Auth template
# cd into project
cd my-api-server

# Start development
npm run dev

Test API Keys

The template provides test keys for immediate testing:

sk_test_public_demo_key_12345
sk_test_admin_demo_key_67890

āš ļø Important: These are demo keys only. Never use them in production!

Project Structure

typescript-auth-api-key/
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ guards/
│   │   ā”œā”€ā”€ apikey.guard.ts        # API key validation guard
│   │   └── multi-auth.guard.ts    # Multi-auth guards
│   ā”œā”€ā”€ modules/
│   │   └── demo/
│   │       ā”œā”€ā”€ demo.module.ts     # Demo module
│   │       ā”œā”€ā”€ demo.tools.ts      # Example tools
│   │       └── multi-auth.tools.ts # Multi-auth examples
│   ā”œā”€ā”€ app.module.ts              # Root module with ApiKeyModule
│   └── index.ts                   # Entry point
ā”œā”€ā”€ .env                           # Test API keys
ā”œā”€ā”€ package.json
└── README.md

Key Features

1. ApiKeyModule Configuration

// src/app.module.ts
@McpApp({
  module: AppModule,
  server: {
    name: 'API Key Auth MCP Server',
    version: '1.0.0',
  },
})
@Module({
  name: 'app',
  imports: [
    ApiKeyModule.forRoot({
      keysEnvPrefix: 'API_KEY',  // Reads API_KEY_1, API_KEY_2, etc.
      headerName: 'x-api-key',
      metadataField: 'apiKey',
      hashed: false,  // Set to true in production
    }),
    DemoModule,
  ],
})
export class AppModule {}

2. API Key Guard

// src/guards/apikey.guard.ts
export class ApiKeyGuard implements Guard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const apiKey = context.metadata?.apiKey || context.metadata?.['x-api-key'];
    
    if (!apiKey) {
      throw new Error('API key required');
    }
    
    const isValid = await ApiKeyModule.validate(apiKey as string);
    
    if (!isValid) {
      throw new Error('Invalid API key');
    }
    
    context.auth = {
      subject: `apikey_${(apiKey as string).substring(0, 12)}`,
      scopes: ['*'],
    };
    
    return true;
  }
}

3. Example Tools

Public Tool (No Auth):

@Tool({
  name: 'get_public_info',
  description: 'Get public information (no authentication required)',
})
async getPublicInfo(args: { topic: string }) {
  return { data: 'public' };
}

Protected Tool (API Key Required):

@Tool({
  name: 'get_protected_data',
  description: 'Get protected data (requires API key)',
})
@UseGuards(ApiKeyGuard)  // šŸ”’ Protected!
async getProtectedData(args: { dataType: string }, context?: ExecutionContext) {
  const apiKeySubject = context?.auth?.subject;
  return { data: 'protected', accessedBy: apiKeySubject };
}

4. Multi-Auth Support

Flexible Auth (JWT OR API Key):

@Tool({
  name: 'get_flexible_data',
  description: 'Accepts JWT token OR API key',
})
@UseGuards(MultiAuthGuard)  // Either works!
async getFlexibleData(args: { query: string }) {
  // Accessible with EITHER JWT OR API key
}

Dual Auth (JWT AND API Key):

@Tool({
  name: 'perform_critical_action',
  description: 'Requires BOTH JWT token AND API key',
})
@UseGuards(DualAuthGuard)  // Both required!
async performCriticalAction(args: { actionType: string }) {
  // Only accessible with BOTH JWT AND API key
}

Testing in Studio

1. Start the Server

npm run dev

Studio will auto-open at http://localhost:3000

2. Set Your API Key

  1. Go to Auth tab in Studio
  2. Find the API Key section
  3. Paste one of the test keys:
    sk_test_public_demo_key_12345
    
  4. Click Set Key

3. Test Tools

Public Tool:

  • Select get_public_info
  • Enter any topic
  • Execute āœ“ - Works without API key!

Protected Tool:

  • Select get_protected_data
  • Choose data type: user, account, or settings
  • Execute āœ“ - Works with valid API key!
  • Try clearing your API key → Authentication error

Multi-Auth Tool:

  • Select get_flexible_data
  • Set either JWT token OR API key
  • Execute āœ“ - Works with either!

Available Tools

Public Tools (No Auth)

ToolDescriptionArguments
get_public_infoGet public informationtopic (string)

Protected Tools (API Key Required)

ToolDescriptionArguments
get_protected_dataGet protected datadataType (user/account/settings)
perform_protected_actionPerform protected actionaction, resourceType, resourceId?
check_api_key_statusCheck API key statusnone

Multi-Auth Tools

ToolDescriptionAuth Required
get_flexible_dataFlexible data accessJWT OR API Key
perform_critical_actionCritical operationJWT AND API Key
check_auth_methodsCheck active authJWT OR API Key

Environment Configuration

# .env
API_KEY_1=sk_test_public_demo_key_12345
API_KEY_2=sk_test_admin_demo_key_67890

# Add more keys by incrementing the number
API_KEY_3=your_custom_key_here

Production Deployment

1. Generate Secure Keys

import { ApiKeyModule } from 'nitrostack';

const apiKey = ApiKeyModule.generateKey('sk');
console.log('API Key:', apiKey);
// sk_vK8mQ2xPnL...

2. Use Hashed Keys

// Hash the key
const hashed = ApiKeyModule.hashKey(apiKey);

// Store hashed value in .env
// API_KEY_1=<hashed_value>

// Enable hashed mode in app.module.ts
ApiKeyModule.forRoot({
  keysEnvPrefix: 'API_KEY',
  hashed: true,  // āœ“ Secure!
})

3. Custom Validation

ApiKeyModule.forRoot({
  keysEnvPrefix: 'API_KEY',
  customValidation: async (key) => {
    // Check against database
    const keyRecord = await db.apiKeys.findOne({ key });
    
    // Check expiration
    if (keyRecord.expiresAt < new Date()) {
      return false;
    }
    
    // Check rate limits, permissions, etc.
    return keyRecord.active;
  },
})

Best Practices

āœ… Do

  • Use strong, randomly generated keys
  • Store keys as SHA-256 hashes in production
  • Use environment variables, never hardcode
  • Rotate keys regularly
  • Implement rate limiting
  • Log API key usage
  • Support key expiration
  • Use sk_ prefix for secret keys

āŒ Don't

  • Commit keys to git
  • Share keys between environments
  • Use predictable key patterns
  • Store keys in plain text
  • Reuse keys across services

Studio Integration

NitroStack Studio provides a dedicated API Key UI:

āœ… Auth Tab → API Key Section

  • Enter your API key
  • It's automatically included in all tool calls
  • Persists across page refreshes
  • Clear button to reset

How Keys Are Sent:

When you execute a tool in Studio, the API key is sent in the _meta field:

{
  args: { /* your tool arguments */ },
  _meta: {
    apiKey: "sk_test_public_demo_key_12345",
    "x-api-key": "sk_test_public_demo_key_12345"
  }
}

Common Patterns

Pattern 1: Service-to-Service Auth

// Service A calls Service B with API key
const client = new McpClient({
  transport: 'http',
  url: 'https://service-b.com',
  headers: {
    'X-API-Key': 'sk_service_a_key'
  }
});

Pattern 2: User + Service Auth

// Tool requires both user JWT and service API key
@UseGuards(JWTGuard, ApiKeyGuard)
async secureOperation() {
  // User is authenticated (JWT)
  // Service is authorized (API key)
}

Pattern 3: Progressive Auth

// Different features require different auth levels
@Tool({ name: 'read_data' })
// No auth - public read

@Tool({ name: 'write_data' })
@UseGuards(ApiKeyGuard)
// API key - write access

@Tool({ name: 'admin_action' })
@UseGuards(JWTGuard, ApiKeyGuard)
// Both - admin access

Troubleshooting

"API key required" Error

Solution: Set your API key in Studio Auth tab:

  1. Go to Auth tab
  2. Find API Key section
  3. Paste: sk_test_public_demo_key_12345
  4. Click Set Key

"Invalid API key" Error

Causes:

  • Wrong key format
  • Key not in .env file
  • Environment variables not loaded

Solution:

  1. Check .env has the key
  2. Restart the dev server: npm run dev
  3. Verify key in Studio matches .env

Public Tools Not Working

Public tools should work without authentication. If they fail:

  1. Check server logs for errors
  2. Ensure no guards are accidentally applied
  3. Test in Studio Tools tab

Next Steps

  1. āœ… Explore the code in src/
  2. āœ… Add your own protected tools
  3. āœ… Combine with JWT auth (see typescript-auth template)
  4. āœ… Add custom validation logic
  5. āœ… Deploy to production with secure keys

Related Resources


Ready to build? šŸš€

Start with this template and customize it for your needs. API key authentication is simple, secure, and perfect for service-to-service communication!