Skip to main content

Audit events and observability

Every auth decision emits a structured event. The default LoggerAuditSink writes them through NestJS Logger with automatic redaction (JWT-shaped strings, cookies, traits, admin tokens all stripped).

To ship events elsewhere, provide your own sink:

import { Injectable } from '@nestjs/common';
import { AuditSink, AUDIT_SINK, IamAuditEvent } from 'ory-nestjs';

@Injectable()
export class OtelAuditSink implements AuditSink {
async emit(event: IamAuditEvent) {
// push to OTel log record, SIEM webhook, Kafka, whatever.
}
}

IamModule.forRoot({
tenants: { /* … */ },
auditSink: { provide: AUDIT_SINK, useClass: OtelAuditSink },
});

Events emitted:

EventSourceLevel
auth.successSessionGuard, OAuth2Guardinfo
auth.failure.missing_credentialSessionGuardwarn
auth.failure.expiredtransport/mapperwarn
auth.failure.malformedtransportwarn
auth.failure.token_inactiveOAuth2Guardwarn
auth.failure.unsigned_headerOathkeeperTransportwarn
auth.failure.upstreamSessionGuardwarn
auth.tenant_mismatchSessionGuardwarn
authz.role.denyRoleGuardwarn
authz.permission.grantPermissionGuard, PermissionService.grantinfo
authz.permission.denyPermissionGuardwarn
authz.permission.revokePermissionService.revokeinfo
authz.upstream_unavailablePermissionGuardwarn
authz.session.revokeSessionService.revoke, IdentityService.revokeSessioninfo
health.probe_failureIamHealthIndicatorwarn
config.boot_failureIamModuleerror

Health indicator (@nestjs/terminus)

import { TerminusModule, HealthCheckService } from '@nestjs/terminus';
import { IamHealthIndicator } from 'ory-nestjs';

@Controller('health')
export class HealthController {
constructor(private readonly health: HealthCheckService, private readonly iam: IamHealthIndicator) {}

@Get()
@Public()
check() {
return this.health.check([() => this.iam.isHealthy('ory-nestjs')]);
}
}

Probes every configured tenant × product (/health/ready) with a 500ms timeout. Failure payload names the failing tenant + product only — no URLs, tokens, or project slugs leak.

Correlation IDs

SessionGuard reads X-Request-Id off the request (or generates one), stamps it onto outbound Ory calls, and includes it on every audit event. Add your own AsyncLocalStorage-aware logger and requests across the stack will join neatly.