Skip to content

Permissions & Security

PropertyDescription
RolesUser roles allowed to access this route
ScopesOAuth-style scopes required to access this route

If a route has roles/scopes defined:

  • Users without the required role/scope cannot access the route
  • An error is thrown: 403 FORBIDDEN - Insufficient permissions

If a route has no roles/scopes:

  • The route is considered public
  • Any authenticated (or unauthenticated) user can access it
Route: GET /api/v1/users
Roles: ['ADMIN', 'MANAGER']

Only users with the ADMIN or MANAGER role can access this route.

Route: GET /api/v1/user/profile
Scopes: ['read:profile']

Only users with the read:profile scope can access this route.

Route: DELETE /api/v1/users/:id
Roles: ['ADMIN']
Scopes: ['write:users']

Users must have the ADMIN role or the write:users scope.


Values from the authenticated user context, available in all routes.

PrefixExampleUsage
##userIdReference in where clauses: user.id = #userId
##companyIdReference in assignments: companyId = #companyId

Global parameters are configured in the Global section and are typically populated by your authentication handler.

Common global parameters:

  • #userId – The authenticated user’s ID
  • #companyId – The user’s company/organization ID
  • #role – The user’s role
  • #email – The user’s email address

In where clauses:

-- Only show the authenticated user's data
user.id = #userId
-- Only show data from the user's company
order.companyId = #companyId
-- Filter by user role
user.role = #role

In assignments:

-- Automatically set the creator
createdBy = #userId
-- Set the company ownership
companyId = #companyId
export async function authenticate(req: RsRequest): Promise<GlobalParams> {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
throw new Error('No token provided');
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
return {
userId: decoded.userId,
companyId: decoded.companyId,
role: decoded.role,
email: decoded.email
};
}

Mark routes as deprecated to warn consumers before removal.

PropertyDescription
DateISO date when the route will be removed
MessageOptional explanation or migration instructions
Route: GET /api/v1/users/old-format
Deprecated: true
Date: 2025-06-01
Message: Use /api/v1/users instead. This endpoint will be removed on June 1, 2025.

Deprecated routes include a warning header:

Deprecation: true
Sunset: 2025-06-01T00:00:00Z
Link: </api/v1/users>; rel="alternate"
  1. Give ample notice – Deprecate at least 3-6 months before removal
  2. Provide alternatives – Always indicate which route to use instead
  3. Document changes – Include migration instructions in the message
  4. Monitor usage – Track calls to deprecated endpoints
  5. Communicate – Notify API consumers via email, changelog, or documentation

CodeHTTP StatusDescription
BAD_REQUEST400Invalid request parameters
UNAUTHORIZED401Missing or invalid authentication
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
VALIDATION_ERROR422Request validation failed
SERVER_ERROR500Internal server error
{
"error": {
"code": "BAD_REQUEST",
"message": "Parameter 'id' must be a number"
}
}

When parameter validation fails:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"email": "Invalid email format",
"age": "Must be at least 18"
}
}
}

When a user lacks required permissions:

{
"error": {
"code": "FORBIDDEN",
"message": "Insufficient permissions to access this resource"
}
}

In custom routes, use the response object’s sendError method:

// Simple error
res.sendError('NOT_FOUND', 'User not found');
// Error with details
res.sendError('VALIDATION_ERROR', 'Invalid input', {
fields: {
email: 'Email is already in use',
password: 'Password must be at least 8 characters'
}
});

  • Always validate JWT tokens in your authentication handler
  • Use strong secrets for token signing
  • Implement token expiration and refresh flows
  • Store sensitive data (passwords, tokens) securely
  • Use the principle of least privilege
  • Grant only the minimum necessary permissions
  • Implement both role-based and scope-based access control
  • Validate permissions at the route level
  • Use global parameters to filter data by user/company
  • Never trust client-provided IDs for ownership checks
  • Always validate that users can only access their own data
  • Use column-level permissions for sensitive fields
  • Always validate and sanitize user input
  • Use parameter validators for type checking
  • Implement business rule validation in custom routes
  • Protect against SQL injection (Restura uses parameterized queries)
  • Implement rate limiting for authentication endpoints
  • Throttle API calls per user/IP
  • Use exponential backoff for failed login attempts
  • Monitor for suspicious activity

Route: GET /api/v1/products
Roles: []
Scopes: []

Anyone can access this route.

Route: GET /api/v1/user/profile
Roles: []
Scopes: []
Where: user.id = #userId

No specific roles required, but the route uses #userId which requires authentication.

Route: DELETE /api/v1/users/:id
Roles: ['ADMIN']
Scopes: []

Only admins can delete users.

Route: GET /api/v1/orders
Roles: []
Scopes: []
Where: order.companyId = #companyId

Users can only see orders from their own company.

Route: GET /api/v1/reports
Roles: ['ADMIN', 'MANAGER']
Scopes: ['read:reports']

Users need either ADMIN or MANAGER role, plus the read:reports scope.