# How Plane's Authentication System Works: Backend and Frontend Implementation

> Discover how Plane's robust authentication system works. Explore backend API key validation and frontend session management, including magic links and OAuth.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: internals
- Published: 2026-06-19

---

**Plane's authentication system combines Django backend middleware for API key validation and CSRF protection with a React/Next.js frontend service that manages session-based login, magic links, OAuth, and password resets.**

Plane (makeplane/plane) is an open-source project management platform built with a layered authentication architecture spanning both backend and frontend components. The system supports multiple authentication methods—traditional email/password, magic links, OAuth providers, and API keys—while centralizing error handling and security tokens through specific middleware and service layers.

## Architecture Overview

The authentication system operates across five distinct layers, each with specific responsibilities:

| Layer | Responsibility | Core Implementation |
|------|----------------|----------------------|
| **API Authentication** | Validates API-key tokens, issues CSRF tokens, handles password-reset, magic-link and sign-out flows | `APIKeyAuthentication` middleware in [`apps/api/plane/app/middleware/api_authentication.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/middleware/api_authentication.py) |
| **Auth Service** | Wraps HTTP calls to backend endpoints, provides helpers for CSRF, email verification, password reset, and sign-out | `AuthService` class in [`packages/services/src/auth/auth.service.ts`](https://github.com/makeplane/plane/blob/main/packages/services/src/auth/auth.service.ts) |
| **Authentication UI** | Renders login, sign-up, magic-link, OAuth and password-reset screens; maps backend error codes to messages | Helper enums and `authErrorHandler` in [`apps/web/helpers/authentication.helper.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/helpers/authentication.helper.tsx) |
| **OAuth Integration** | Optional third-party login (Google, GitHub, GitLab) when configured | UI components in [`apps/admin/components/authentication/authentication-method-card.tsx`](https://github.com/makeplane/plane/blob/main/apps/admin/components/authentication/authentication-method-card.tsx) |
| **Session Management** | Maintains user state via Django session cookies or API keys for external clients | Django's authentication middleware and explicit `APIKeyAuthentication` |

## API Authentication Layer

The backend authentication layer in Plane handles credential validation through Django middleware and dedicated endpoints.

### API Key Validation

The `APIKeyAuthentication` middleware processes the `X-Api-Key` header on incoming requests. When an external service sends a request with this header, the middleware looks up the corresponding `APIToken` object, checks expiration and activity status, updates the `last_used` timestamp, and returns the associated user.

```python

# Conceptual flow based on apps/api/plane/app/middleware/api_authentication.py

class APIKeyAuthentication:
    def authenticate(self, request):
        api_key = request.headers.get("X-Api-Key")
        token = APIToken.objects.get(token=api_key)
        if token.is_active and not token.is_expired:
            token.update_last_used()
            return token.user

```

### CSRF Token Management

Before any state-changing POST request (sign-in, password reset, magic link generation), the frontend must obtain a CSRF token from the `/auth/get-csrf-token/` endpoint. This token must be included as the `X-CSRFTOKEN` header in subsequent requests to prevent cross-site request forgery attacks.

## Frontend Authentication Service

The frontend layer abstracts all authentication logic into a centralized service layer that React components consume.

### AuthService Implementation

The `AuthService` class extends `APIService` and implements methods for all authentication flows. Located in [`packages/services/src/auth/auth.service.ts`](https://github.com/makeplane/plane/blob/main/packages/services/src/auth/auth.service.ts), this service handles:

- **CSRF Retrieval**: `requestCSRFToken()` fetches tokens from `/auth/get-csrf-token/`
- **Email Verification**: `emailCheck()` validates addresses via `/auth/email-check/`
- **Magic Link Generation**: `generateUniqueCode()` requests short-lived codes from `/auth/magic-generate/`
- **Password Reset**: Methods for initiating and completing password reset flows
- **Sign-Out**: `signOut(baseUrl)` constructs and submits a sign-out form to `/auth/sign-out/`

```typescript
// Example: Requesting CSRF token and signing in
import { AuthService } from "@plane/services/auth";
import { authErrorHandler, EAuthenticationErrorCodes } from "./authentication.helper";

const auth = new AuthService();

async function signIn(email: string, password: string) {
  // Retrieve CSRF token first
  const { csrf_token } = await auth.requestCSRFToken();

  try {
    await auth.post("/auth/login/", { email, password }, {
      headers: { "X-CSRFTOKEN": csrf_token },
    });
    // Django sets session cookie automatically; redirect to dashboard
  } catch (err: any) {
    const errorInfo = authErrorHandler(
      err?.response?.data?.code as EAuthenticationErrorCodes,
      email
    );
    if (errorInfo) {
      showBanner(errorInfo.title, errorInfo.message);
    }
  }
}

```

### Error Handling and UI Mapping

Authentication errors are encoded as numeric strings defined in the `EAuthenticationErrorCodes` enum. The `authErrorHandler` function in [`apps/web/helpers/authentication.helper.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/helpers/authentication.helper.tsx) maps these backend error codes to user-friendly banner messages, ensuring consistent error presentation across the application.

## Authentication Flows

### Email and Password Authentication

The standard login flow begins with the frontend rendering `EAuthModes.SIGN_IN` from the authentication helper enums. The user submits credentials, which the frontend sends to `/auth/login/` with the CSRF token. Upon successful validation, Django creates a session cookie that subsequent requests include automatically.

### Magic Link Login

For passwordless authentication, the flow uses `AuthService.generateUniqueCode()` to request a short-lived code from `/auth/magic-generate/`. The backend sends this code via email. When the user enters the code, the backend validates it and issues a session cookie, completing the authentication without requiring a password.

### OAuth Integration

When users select Google, GitHub, or GitLab authentication, the frontend redirects to the provider's OAuth endpoint. After authorization, the backend callback creates or updates the user record and issues a session cookie. The OAuth UI components reside in [`apps/admin/components/authentication/authentication-method-card.tsx`](https://github.com/makeplane/plane/blob/main/apps/admin/components/authentication/authentication-method-card.tsx).

### API Key Access

External services and API-only clients bypass session cookies by including the `X-Api-Key` header in requests. The `APIKeyAuthentication` middleware validates these tokens against the `APIToken` model, enabling programmatic access to Plane's API without interactive login flows.

```typescript
// Example: Validating an API key in a custom client
import axios from "axios";

async function fetchProjects(apiKey: string) {
  const response = await axios.get("/api/projects/", {
    baseURL: "https://app.plane.so",
    headers: { "X-Api-Key": apiKey },
  });
  return response.data;
}

```

## Sign-Out Process

The sign-out flow requires special handling to ensure CSRF protection. The `AuthService.signOut()` method fetches a fresh CSRF token, constructs a hidden HTML form that posts to `/auth/sign-out/`, and programmatically submits it. This approach ensures Django properly invalidates the session cookie and clears the authentication state.

## Summary

- **Plane's authentication system** spans Django backend middleware and React/Next.js frontend services to support multiple login methods.
- **API keys** are validated via the `APIKeyAuthentication` middleware in [`apps/api/plane/app/middleware/api_authentication.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/middleware/api_authentication.py), which checks token validity and updates usage timestamps.
- **CSRF protection** requires tokens obtained from `/auth/get-csrf-token/` before any POST request, handled by `AuthService.requestCSRFToken()`.
- **Error handling** is centralized through `EAuthenticationErrorCodes` enums and the `authErrorHandler` function in [`apps/web/helpers/authentication.helper.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/helpers/authentication.helper.tsx).
- **Authentication methods** include traditional email/password, magic links, OAuth providers, and API keys, with session cookies for web clients and header-based tokens for API access.

## Frequently Asked Questions

### How does Plane handle CSRF protection?

Plane requires a CSRF token for all state-changing POST requests. The frontend calls `AuthService.requestCSRFToken()` to retrieve a token from the `/auth/get-csrf-token/` endpoint, then includes it as the `X-CSRFTOKEN` header in subsequent requests. This prevents cross-site request forgery attacks while maintaining session security.

### What is the difference between session-based and API key authentication in Plane?

Session-based authentication uses Django's built-in session cookies set after login via email/password, magic links, or OAuth, automatically included with each request. API key authentication uses the `X-Api-Key` header validated by the `APIKeyAuthentication` middleware, designed for external services and programmatic access without interactive login flows.

### How does the magic link authentication flow work?

The frontend calls `AuthService.generateUniqueCode()` which requests a short-lived code from the `/auth/magic-generate/` endpoint. The backend emails this code to the user, who enters it in the UI. Upon validation, the backend issues a session cookie, completing authentication without requiring a password.

### Where are authentication errors handled in the Plane frontend?

Authentication errors are handled in [`apps/web/helpers/authentication.helper.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/helpers/authentication.helper.tsx), which defines the `EAuthenticationErrorCodes` enum and the `authErrorHandler` function. This utility maps backend error codes to user-friendly messages, ensuring consistent error display across login, sign-up, password reset, and magic link flows.