---
title: "Authentication — JWT Strategy"
aliases: [payload-jwt, jwt-auth-payload]
tags: [payloadcms, authentication, jwt, security]
sources: [raw/authentication__jwt.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Payload supports JWT-based authentication alongside [[wiki/payloadcms/authentication-cookies|HTTP-only cookie auth]]. Tokens are returned from `login`, `logout`, `refresh`, and `me` operations.

## Using the Authorization Header

Pass the JWT via `Authorization` header for stateless/API clients:

```ts
// 1. Login to get token
const { token } = await fetch('http://localhost:3000/api/users/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'dev@payloadcms.com', password: 'password' }),
}).then(r => r.json())

// 2. Authenticate subsequent requests
const response = await fetch('http://localhost:3000/api/posts', {
  headers: { Authorization: `JWT ${token}` },
})
```

- Format: `Authorization: JWT <token>` (not `Bearer`)
- Alternative to cookie — useful for mobile apps, external services, scripts

## Accessing the Logged-In User

Within [[wiki/payloadcms/local-api|access control]] and hooks, use `req.user`:

```ts
access: {
  read: ({ req }) => req.user?.role === 'admin',
}
```

## Removing Token from Responses

If you don't want the JWT returned in API responses (e.g., cookie-only flows):

```ts
export const Users: CollectionConfig = {
  slug: 'users',
  auth: {
    removeTokenFromResponses: true,
  },
}
```

## External JWT Validation

When validating Payload-issued JWTs in **external services**, you must replicate Payload's secret processing — it doesn't use your raw `PAYLOAD_SECRET`:

```ts
import crypto from 'node:crypto'

// Payload's actual signing secret:
const secret = crypto
  .createHash('sha256')
  .update(process.env.PAYLOAD_SECRET)
  .digest('hex')
  .slice(0, 32)
```

- Payload SHA-256 hashes the env secret, then takes the first 32 hex characters
- Use this derived value in your external verifier (e.g., `jsonwebtoken.verify(token, secret)`)
- **Gotcha:** using the raw `PAYLOAD_SECRET` in an external verifier will fail silently

## Key Takeaways

- JWT header format is `Authorization: JWT <token>` — **not** `Bearer`
- `req.user` is available in all access control functions and hooks
- `removeTokenFromResponses: true` hides the token from API responses without disabling JWT auth
- External validation requires deriving the signing secret via SHA-256 hash + first 32 chars
- Cookie and JWT auth can coexist — each request uses whichever is present

## Related

- [[wiki/payloadcms/authentication-cookies|Cookie Strategy]] — alternative auth method
- [[wiki/payloadcms/authentication-api-keys|API Keys]] — non-expiring key auth for integrations
- [[wiki/payloadcms/authentication-custom-strategies|Custom Auth Strategies]] — replace/extend built-in auth
- [[wiki/payloadcms/rest-api|REST API]] — all auth endpoints (`/login`, `/me`, `/refresh`)
