obsidian/wiki/payloadcms/local-api-access-control.md
2026-05-15 16:14:29 +01:00

65 lines
2.7 KiB
Markdown

---
title: "Local API — Access Control"
aliases: [local-api-access-control, payload-overrideAccess]
tags: [payloadcms, local-api, access-control, security, server-side]
sources: [raw/local-api__access-control.md]
created: 2026-05-15
updated: 2026-05-15
---
## Overview
Local API operations **skip access control by default**. This is by design — server-side scripts and admin tasks often run without a user context. You must explicitly opt in to access control enforcement when it matters.
## Default Behaviour: Access Control Skipped
```ts
// Access control is NOT checked — runs as a privileged operation
const doc = await payload.create({
collection: 'users',
data: { email: 'test@test.com', password: 'test' },
})
```
Useful for:
- Seed scripts and migrations
- Admin-only server jobs (cron tasks, background workers)
- Internal service-to-service operations
## Enforcing Access Control
Pass `overrideAccess: false` **and** the authenticated `user` object to make the operation respect collection-level access rules.
```ts
const doc = await payload.create({
collection: 'users',
overrideAccess: false, // enforce collection access control
user, // authenticated user to check against
data: { email: 'test@test.com', password: 'test' },
})
```
- If `user` lacks permission → Payload throws a `Forbidden` error
- Works on all CRUD operations: `create`, `find`, `findByID`, `update`, `delete`
## When to Use Each Mode
| Scenario | `overrideAccess` | Pass `user`? |
|----------|-----------------|--------------|
| Server scripts, cron jobs, seeds | `true` (default) | No |
| Server actions executing on behalf of a logged-in user | `false` | Yes — `req.user` |
| Webhooks / third-party integrations with trusted tokens | `true` (default) | No |
| Custom API endpoints that mirror REST behaviour | `false` | Yes |
## Key Takeaways
- Local API **bypasses access control by default** — safe for trusted server code, dangerous if exposed to untrusted input.
- To enforce permissions: set `overrideAccess: false` AND pass the `user` object.
- Both conditions are required — `overrideAccess: false` without a `user` will treat the request as unauthenticated (all collection-access functions receive `undefined` as user).
- In [[wiki/payloadcms/hooks-collections|Collection Hooks]] that run after user-triggered events, access control is already enforced by the REST/GraphQL layer; using the Local API inside hooks with `overrideAccess: true` lets you escalate privileges intentionally.
- See [[wiki/payloadcms/access-control|Access Control Overview]] for collection/field-level access function signatures.
## Sources
- `raw/local-api__access-control.md`
- https://payloadcms.com/docs/local-api/access-control