Guards & Scopes
ApiKeysGuard reads the Authorization header, verifies the key, enforces route and origin policy, and attaches ApiKeyContext to the request. A conventional Bearer prefix is accepted and stripped; the raw key value is also accepted for compatibility.
Protecting a route
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiKeysGuard, RequireScope } from '@nestarc/api-keys';
@Controller('reports')
@UseGuards(ApiKeysGuard)
export class ReportsController {
@Get()
@RequireScope('reports', 'read')
list() {
return [];
}
}Apply @UseGuards(ApiKeysGuard) at the controller level when every route uses API-key auth. Use @RequireScope(resource, level) per handler to specify the required permission.
Scope model
Scopes are { resource, level } pairs:
resourceis a free-form string likereports,invoices,projects.levelisreadorwrite.writeimpliesread— a key withreports:writesatisfies@RequireScope('reports', 'read').
const { id, key } = await apiKeys.create({
tenantId: 'tenant_123',
name: 'Primary',
scopes: [
{ resource: 'reports', level: 'write' }, // also grants reports:read
{ resource: 'projects', level: 'read' },
],
});Reading the context in a handler
import { Controller, Get, UseGuards } from '@nestjs/common';
import {
ApiKeyContext,
ApiKeysGuard,
CurrentApiKey,
} from '@nestarc/api-keys';
@Controller('reports')
@UseGuards(ApiKeysGuard)
export class ReportsController {
@Get()
list(@CurrentApiKey() apiKey: ApiKeyContext) {
return this.service.listForTenant(apiKey.tenantId);
}
}ApiKeyContext surfaces:
keyId— the key's record idprefix— a safe identifier for logs and displaystenantIdenvironment(liveortest)scopes— the full scope list granted to this keyallowedIpCidrs— the normalized origin policy
Use getApiKeyContext(request) in middleware or framework-level code. A configured contextWriter can copy this context into tenancy, RLS, or request-local infrastructure after all guard checks pass.
Enforcement order
After credential verification, the guard checks required environment, IP allowlist, and required scope in that order. The request context and contextWriter are populated only after every check succeeds.
Failures
| Situation | Error code | HTTP |
|---|---|---|
No Authorization header | api_key_missing | 401 |
| Header doesn't match the expected format | api_key_malformed | 401 |
| Key not found or secret mismatch | api_key_invalid | 401 |
| Key was revoked | api_key_revoked | 401 |
Key is past expiresAt | api_key_expired | 401 |
Route requires live but key is test (or vice versa) | api_key_environment_mismatch | 403 |
| Client IP is missing, invalid, or outside a restricted key's allowlist | api_key_ip_not_allowed | 403 |
| Key lacks the required scope | api_key_scope_insufficient | 403 |
Branch on the code value — not the message — in clients and structured logs.
When combining the package with @nestarc/rbac, run ApiKeysGuard before RbacGuard. Embedded scopes and RBAC role bindings are independent authorization layers, so both requirements must pass.