# Best Practices for Securing APIs in Twenty CRM: NestJS Guard Architecture Explained

> Discover best practices for securing Twenty CRM APIs. Learn how NestJS guard architecture combines JWT, API keys, OAuth 2.0 SSO, and RBAC for robust API security.

- Repository: [Twenty/twenty](https://github.com/twentyhq/twenty)
- Tags: best-practices
- Published: 2026-03-27

---

**Twenty CRM secures its GraphQL and REST APIs using a layered NestJS architecture that combines JWT session tokens, API key validation, OAuth 2.0 SSO, and workspace-level RBAC enforced through centralized authentication context and specialized route guards.**

Twenty CRM implements enterprise-grade API security within its `core-modules/auth` package, utilizing NestJS guards and middleware to protect all public endpoints. Understanding the best practices for securing APIs in Twenty CRM requires examining how the repository combines centralized authentication context with multiple validation strategies to enforce multi-tenant isolation and role-based permissions.

## Establish a Centralized Authentication Context

Every incoming request passes through [`workspace-auth-context.middleware.ts`](https://github.com/twentyhq/twenty/blob/main/workspace-auth-context.middleware.ts) to establish a security context before reaching business logic. This middleware extracts tokens, API keys, or SSO payloads from headers, then invokes [`auth.service.ts`](https://github.com/twentyhq/twenty/blob/main/auth.service.ts) to build a structured **auth context** describing exactly who is making the call—whether a user, workspace, application, or API key.

The middleware stores this context in [`workspace-auth-context.storage.ts`](https://github.com/twentyhq/twenty/blob/main/workspace-auth-context.storage.ts), an in-memory store that propagates authentication data through the entire request lifecycle. By centralizing auth data in this manner, downstream guards and resolvers can enforce permissions consistently without re-parsing tokens or querying databases repeatedly.

## Enforce Endpoint Protection with Specialized Guards

Twenty CRM implements distinct NestJS guards for different authentication scenarios, each residing in `packages/twenty-server/src/engine/core-modules/auth/guards/`.

### JWT Session Authentication

The [`jwt-auth.guard.ts`](https://github.com/twentyhq/twenty/blob/main/jwt-auth.guard.ts) validates the `Authorization: Bearer <jwt>` header on every protected request. It delegates signature verification and expiration checks to [`jwt.auth.strategy.ts`](https://github.com/twentyhq/twenty/blob/main/jwt.auth.strategy.ts). Upon successful validation, the guard injects a **user-auth context** containing the user ID, workspace IDs, and role assignments into the request object.

```typescript
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '@twenty/server/src/engine/core-modules/auth/guards/jwt-auth.guard';
import { AuthUser } from '@twenty/server/src/engine/core-modules/auth/decorators/auth-user.decorator';

@Controller('api/contacts')
export class ContactsController {
  @Get()
  @UseGuards(JwtAuthGuard)               // ← Enforces a valid JWT
  async list(@AuthUser() user) {
    // `user` contains { userId, workspaceIds, roles, permissions }
    return this.contactService.findAllForWorkspace(user.workspaceId);
  }
}

```

### API Key Validation for Service-to-Service Communication

For programmatic access without user sessions, Twenty CRM uses the combination of [`auth-api-key.decorator.ts`](https://github.com/twentyhq/twenty/blob/main/auth-api-key.decorator.ts) and [`is-api-key-auth-context.guard.ts`](https://github.com/twentyhq/twenty/blob/main/is-api-key-auth-context.guard.ts). This guard validates static or rotating API keys passed via the `x-api-key` header, enabling secure machine-to-machine authentication for internal services and third-party integrations.

```typescript
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';

@Injectable()
export class IntegrationClient {
  constructor(private http: HttpService) {}

  async fetchData() {
    const response = await this.http
      .get('https://api.twentyhq.com/v1/external', {
        headers: { 'x-api-key': process.env.TWENTY_INTERNAL_API_KEY },
      })
      .toPromise();

    return response.data;
  }
}

```

The receiving endpoint must apply `@UseGuards(ApiKeyAuthGuard)` to ensure the key is valid and possesses the proper scope for the requested resource.

### OAuth 2.0 and SSO Integration

Twenty CRM supports enterprise single sign-on through dedicated guards that delegate to Passport strategies:
- **Google SSO**: [`google-oauth.guard.ts`](https://github.com/twentyhq/twenty/blob/main/google-oauth.guard.ts) utilizes [`google.auth.strategy.ts`](https://github.com/twentyhq/twenty/blob/main/google.auth.strategy.ts) to handle OAuth flows and token exchange.
- **Microsoft Entra**: [`microsoft-oauth.guard.ts`](https://github.com/twentyhq/twenty/blob/main/microsoft-oauth.guard.ts) pairs with [`microsoft.auth.strategy.ts`](https://github.com/twentyhq/twenty/blob/main/microsoft.auth.strategy.ts) for Azure AD authentication.
- **SAML 2.0**: [`saml-auth.guard.ts`](https://github.com/twentyhq/twenty/blob/main/saml-auth.guard.ts) processes enterprise identity provider assertions.

Each strategy handles the complete OAuth handshake, validates ID tokens, and provisions users automatically while issuing short-lived JWTs for session management.

```tsx
import { useMutation } from '@apollo/client';
import { authorizeApp } from '@/modules/auth/graphql/mutations/authorizeApp';
import { GoogleLoginButton } from '@/components/GoogleLoginButton';

export const GoogleSSO = () => {
  const [login] = useMutation(authorizeApp);

  const handleGoogleSuccess = async (googleUser) => {
    const idToken = googleUser.getAuthResponse().id_token;
    await login({ variables: { provider: 'google', token: idToken } });
  };

  return <GoogleLoginButton onSuccess={handleGoogleSuccess} />;
};

```

The `authorizeApp` mutation forwards the Google ID token to the server, where `GoogleAuthStrategy` validates the signature against Google's public keys before establishing the session.

### Workspace-Level Tenant Isolation

The [`workspace-auth.guard.ts`](https://github.com/twentyhq/twenty/blob/main/workspace-auth.guard.ts) provides **multi-tenant isolation** by verifying that the authenticated user or API key belongs to the workspace specified in the request. This prevents cross-workspace data leakage by enforcing strict boundary checks before any data access occurs.

## Implement Role-Based Access Control (RBAC)

Permission enforcement occurs inside GraphQL resolvers and service methods using the `@AuthUser()` decorator defined in [`auth-user.decorator.ts`](https://github.com/twentyhq/twenty/blob/main/auth-user.decorator.ts). This decorator extracts the enriched auth context from the request, allowing fine-grained permission checks against workspace-level roles stored in the database.

```typescript
@Resolver(() => Contact)
export class ContactResolver {
  @Query(() => [Contact])
  async contacts(@AuthUser() user: AuthUserContext) {
    // Enforce workspace-level permission
    if (!user.hasPermission('contact.read')) {
      throw new ForbiddenException('Insufficient permissions');
    }
    // Proceed with business logic …
  }
}

```

Permissions are cached per request to minimize database queries while ensuring consistent authorization decisions across the API surface.

## Secure Token Lifecycle Management

Twenty CRM implements **stateful token management** for enhanced security:
- **Refresh tokens** are encrypted at rest in the database and rotated immediately upon use, preventing token replay attacks.
- **Access tokens** remain short-lived (JWTs) with signatures verified on every request via [`jwt.auth.strategy.ts`](https://github.com/twentyhq/twenty/blob/main/jwt.auth.strategy.ts).
- **CSRF protection** requires the `X-CSRF-Token` header for all non-GET operations initiated from the web UI, mitigating cross-site request forgery.

## Monitor and Rate-Limit Authentication Endpoints

The security layer logs all authentication events—successful logins, token refreshes, and failed attempts—to **Redis** for real-time analysis and optional forwarding to external audit services. Rate-limiting middleware specifically caps request rates on `/auth/login` and `/auth/token` endpoints to neutralize brute-force attacks against user credentials.

The repository validates these security mechanisms through comprehensive testing suites, including [`jwt.auth.strategy.spec.ts`](https://github.com/twentyhq/twenty/blob/main/jwt.auth.strategy.spec.ts) for unit testing token validation and [`oauth.integration-spec.ts`](https://github.com/twentyhq/twenty/blob/main/oauth.integration-spec.ts) for end-to-end OAuth flow verification.

## Summary

- **Centralize authentication context** using [`workspace-auth-context.middleware.ts`](https://github.com/twentyhq/twenty/blob/main/workspace-auth-context.middleware.ts) to ensure consistent identity propagation across all request handlers.
- **Apply specialized guards** (`JwtAuthGuard`, `ApiKeyAuthGuard`, `WorkspaceAuthGuard`) to enforce the appropriate authentication method for each endpoint type.
- **Implement RBAC** via the `@AuthUser()` decorator and explicit permission checks within resolvers to prevent privilege escalation.
- **Encrypt and rotate refresh tokens** while keeping JWT access tokens short-lived and strictly validated.
- **Enable rate limiting and audit logging** on all authentication endpoints to detect and prevent brute-force attacks.

## Frequently Asked Questions

### How does Twenty CRM prevent cross-workspace data leakage?

Twenty CRM utilizes [`workspace-auth.guard.ts`](https://github.com/twentyhq/twenty/blob/main/workspace-auth.guard.ts) to enforce multi-tenant isolation at the API boundary. This guard verifies that the authenticated user or API key explicitly belongs to the workspace ID specified in the request path or headers, rejecting any cross-tenant access attempts before they reach business logic.

### What authentication methods does Twenty CRM support for API access?

According to the twentyhq/twenty source code, the platform supports four primary authentication methods: **JWT session tokens** for browser-based users, **API keys** (`x-api-key` header) for service-to-service integration, **OAuth 2.0** through Google and Microsoft providers, and **SAML 2.0** for enterprise identity providers. Each method maps to a specific NestJS guard and Passport strategy combination within the `core-modules/auth` package.

### How are permissions enforced in Twenty CRM GraphQL resolvers?

Permissions are enforced using the `@AuthUser()` decorator imported from [`auth-user.decorator.ts`](https://github.com/twentyhq/twenty/blob/main/auth-user.decorator.ts). This decorator extracts the auth context populated by the JWT or API key guard, exposing a `hasPermission()` method that resolvers call to validate workspace-specific entitlements before executing database queries.

### Where does Twenty CRM store refresh tokens and how are they secured?

Refresh tokens are stored encrypted in the database via the `refresh-tokens-manager` services and rotated immediately upon each use. This stateful approach allows Twenty CRM to invalidate compromised tokens instantly while maintaining secure, long-lived sessions for legitimate users.