Skip to content

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

typescript
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:

  • resource is a free-form string like reports, invoices, projects.
  • level is read or write.
  • write implies read — a key with reports:write satisfies @RequireScope('reports', 'read').
typescript
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

typescript
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 id
  • prefix — a safe identifier for logs and displays
  • tenantId
  • environment (live or test)
  • scopes — the full scope list granted to this key
  • allowedIpCidrs — 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

SituationError codeHTTP
No Authorization headerapi_key_missing401
Header doesn't match the expected formatapi_key_malformed401
Key not found or secret mismatchapi_key_invalid401
Key was revokedapi_key_revoked401
Key is past expiresAtapi_key_expired401
Route requires live but key is test (or vice versa)api_key_environment_mismatch403
Client IP is missing, invalid, or outside a restricted key's allowlistapi_key_ip_not_allowed403
Key lacks the required scopeapi_key_scope_insufficient403

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.

Released under the MIT License.