Skip to main content

API Keys / Service Tokens

node-auth provides a complete, zero-dependency API key system for machine-to-machine authentication. Raw keys are never stored — only a bcrypt hash is persisted.

Key format: ak_<48 hex characters> (~196 bits of entropy).


Authentication flow


Setup

1. Implement IApiKeyStore

import { IApiKeyStore, ApiKey } from '@nik2208/node-auth';

export class MyApiKeyStore implements IApiKeyStore {
async save(key: ApiKey): Promise<void> {
await db('api_keys').insert(key);
}
async findByPrefix(prefix: string): Promise<ApiKey | null> {
return db('api_keys').where({ keyPrefix: prefix, isActive: true }).first() ?? null;
}
async findById(id: string): Promise<ApiKey | null> {
return db('api_keys').where({ id }).first() ?? null;
}
async revoke(id: string): Promise<void> {
await db('api_keys').where({ id }).update({ isActive: false });
}
async updateLastUsed(id: string): Promise<void> {
await db('api_keys').where({ id }).update({ lastUsedAt: new Date() });
}
}

2. Create a key with ApiKeyService

import { ApiKeyService } from '@nik2208/node-auth';

const apiKeyService = new ApiKeyService();
const { rawKey, record } = await apiKeyService.createKey(apiKeyStore, {
name: 'stripe-webhook',
serviceId: 'tenant-abc',
scopes: ['tools:read', 'webhooks:receive'],
expiresAt: new Date('2025-12-31'),
allowedIps: ['203.0.113.0/24'],
});

// rawKey is shown exactly once — store it securely
console.log('Your key (shown once):', rawKey);

3. Protect routes with createApiKeyMiddleware

import { createApiKeyMiddleware } from '@nik2208/node-auth';

app.use(
'/tools',
createApiKeyMiddleware(apiKeyStore, {
requiredScopes: ['tools:read'],
}),
);

// Access authenticated context in handlers
app.get('/tools/data', (req, res) => {
console.log(req.apiKey); // { keyId, keyPrefix, name, serviceId, scopes }
res.json({ ok: true });
});

Accepted header formats

Authorization: ApiKey ak_abc123…
X-Api-Key: ak_abc123…

Both formats are supported. Use whichever fits your client.


IApiKeyStore interface

MethodRequiredDescription
save(key)Persist a newly created key record
findByPrefix(prefix)Look up an active key by its prefix
findById(id)Look up a key by ID
revoke(id)Mark a key inactive
updateLastUsed(id)Record last-used timestamp
listByServiceId(serviceId)optionalReturn all keys for a service identity
listAll(limit, offset)optionalPaginated admin listing
delete(id)optionalPermanently delete (prefer revoke)
logUsage(entry)optionalPer-key access audit log

ApiKeyService methods

class ApiKeyService {
createKey(store: IApiKeyStore, options: CreateApiKeyOptions): Promise<CreatedApiKey>;
verifyKey(rawKey: string, hash: string): Promise<boolean>;
extractPrefix(rawKey: string): string;
}

interface CreateApiKeyOptions {
name: string;
serviceId?: string;
scopes?: string[];
allowedIps?: string[];
expiresAt?: Date;
saltRounds?: number; // default: 10
}

interface CreatedApiKey {
rawKey: string; // plaintext key — never stored, shown once
record: ApiKey; // persisted record (hash, prefix, metadata)
}

ApiKeyStrategyOptions

interface ApiKeyStrategyOptions {
requiredScopes?: string[]; // all listed scopes must be present
checkIp?: boolean; // enforce allowedIps (default: true)
onSuccess?: (context: ApiKeyContext) => void | Promise<void>;
}