89 lines
3.1 KiB
Markdown
89 lines
3.1 KiB
Markdown
---
|
|
title: Use @request Context in API Rules
|
|
impact: CRITICAL
|
|
impactDescription: Enables dynamic, user-aware access control
|
|
tags: api-rules, security, request-context, authentication
|
|
---
|
|
|
|
## Use @request Context in API Rules
|
|
|
|
The `@request` object provides access to the current request context including authenticated user, request body, query parameters, and headers. Use it to build dynamic access rules.
|
|
|
|
**Incorrect (hardcoded or missing auth checks):**
|
|
|
|
```javascript
|
|
// No authentication check
|
|
const collection = {
|
|
listRule: '', // Anyone can see everything
|
|
createRule: '' // Anyone can create
|
|
};
|
|
|
|
// Hardcoded user ID (never do this)
|
|
const collection = {
|
|
listRule: 'owner = "specific_user_id"' // Only one user can access
|
|
};
|
|
```
|
|
|
|
**Correct (using @request context):**
|
|
|
|
```javascript
|
|
// Check if user is authenticated
|
|
createRule: '@request.auth.id != ""'
|
|
|
|
// Check ownership via auth record
|
|
listRule: 'owner = @request.auth.id'
|
|
viewRule: 'owner = @request.auth.id'
|
|
updateRule: 'owner = @request.auth.id'
|
|
deleteRule: 'owner = @request.auth.id'
|
|
|
|
// Access auth record fields
|
|
// IMPORTANT: If using custom role fields, ensure update rules prevent
|
|
// users from modifying their own role: @request.body.role:isset = false
|
|
listRule: '@request.auth.role = "admin"'
|
|
listRule: '@request.auth.verified = true'
|
|
|
|
// Validate request body on create/update
|
|
createRule: '@request.auth.id != "" && @request.body.owner = @request.auth.id'
|
|
|
|
// Prevent changing certain fields
|
|
updateRule: 'owner = @request.auth.id && @request.body.owner:isset = false'
|
|
|
|
// WARNING: Query parameters are user-controlled and should NOT be used
|
|
// for authorization decisions. Use them only for optional filtering behavior
|
|
// where the fallback is equally safe.
|
|
// listRule: '@request.query.publicOnly = "true" || owner = @request.auth.id'
|
|
// The above is UNSAFE - users can bypass ownership by adding ?publicOnly=true
|
|
// Instead, use separate endpoints or server-side logic for public vs. private views.
|
|
listRule: 'owner = @request.auth.id || public = true' // Use a record field, not query param
|
|
|
|
// Access nested auth relations
|
|
listRule: 'team.members ?= @request.auth.id'
|
|
```
|
|
|
|
**Available @request fields:**
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `@request.auth.id` | Authenticated user's ID (empty string if not authenticated) |
|
|
| `@request.auth.*` | Any field from auth record (role, verified, email, etc.) |
|
|
| `@request.body.*` | Request body fields (create/update only) |
|
|
| `@request.query.*` | URL query parameters |
|
|
| `@request.headers.*` | Request headers |
|
|
| `@request.method` | HTTP method (GET, POST, etc.) |
|
|
| `@request.context` | Request context: `default`, `oauth2`, `otp`, `password`, `realtime`, `protectedFile` |
|
|
|
|
**Body field modifiers:**
|
|
|
|
```javascript
|
|
// Check if field is being set
|
|
updateRule: '@request.body.status:isset = false' // Can't change status
|
|
|
|
// Check if field changed from current value
|
|
updateRule: '@request.body.owner:changed = false' // Can't change owner
|
|
|
|
// Get length of array/string
|
|
createRule: '@request.body.tags:length <= 5' // Max 5 tags
|
|
```
|
|
|
|
Reference: [PocketBase API Rules](https://pocketbase.io/docs/api-rules-and-filters/#available-fields)
|