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 
ApiKeyModuleandApiKeyGuard - ā 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
- Go to Auth tab in Studio
 - Find the API Key section
 - Paste one of the test keys:
sk_test_public_demo_key_12345 - 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, orsettings - 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)
| Tool | Description | Arguments | 
|---|---|---|
get_public_info | Get public information | topic (string) | 
Protected Tools (API Key Required)
| Tool | Description | Arguments | 
|---|---|---|
get_protected_data | Get protected data | dataType (user/account/settings) | 
perform_protected_action | Perform protected action | action, resourceType, resourceId? | 
check_api_key_status | Check API key status | none | 
Multi-Auth Tools
| Tool | Description | Auth Required | 
|---|---|---|
get_flexible_data | Flexible data access | JWT OR API Key | 
perform_critical_action | Critical operation | JWT AND API Key | 
check_auth_methods | Check active auth | JWT 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:
- Go to Auth tab
 - Find API Key section
 - Paste: 
sk_test_public_demo_key_12345 - Click Set Key
 
"Invalid API key" Error
Causes:
- Wrong key format
 - Key not in 
.envfile - Environment variables not loaded
 
Solution:
- Check 
.envhas the key - Restart the dev server: 
npm run dev - Verify key in Studio matches 
.env 
Public Tools Not Working
Public tools should work without authentication. If they fail:
- Check server logs for errors
 - Ensure no guards are accidentally applied
 - Test in Studio Tools tab
 
Next Steps
- ā
 Explore the code in 
src/ - ā Add your own protected tools
 - ā
 Combine with JWT auth (see 
typescript-authtemplate) - ā Add custom validation logic
 - ā 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!