Authentication & Authorization
Authentication & Authorization
⚠️ Important: Authentication and authorization are application-layer responsibilities. ObjectQL provides the framework and hooks, but you must implement the actual authentication and permission checking logic.
Authentication Methods
ObjectQL supports multiple authentication strategies through server middleware:
1. JWT Tokens (Recommended)
Implement JWT authentication in your server middleware:
// Express middleware example
import jwt from 'jsonwebtoken';
app.use('/api/objectql', async (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
});Client Request:
POST /api/objectql
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json2. API Keys
Implement API key authentication in middleware:
app.use('/api/objectql', async (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'No API key provided' });
}
const user = await validateApiKey(apiKey);
if (!user) {
return res.status(401).json({ error: 'Invalid API key' });
}
req.user = user;
next();
});Client Request:
POST /api/objectql
X-API-Key: your_api_key_here
Content-Type: application/json3. Session Cookies
Implement session-based authentication:
import session from 'express-session';
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}));
app.use('/api/objectql', (req, res, next) => {
if (!req.session.user) {
return res.status(401).json({ error: 'Not authenticated' });
}
req.user = req.session.user;
next();
});Client Request:
POST /api/objectql
Cookie: session_id=abc123...
Content-Type: application/jsonAuthorization (Permissions)
📋 Implementation Status: Permission enforcement is NOT automatic. You must implement it using hooks or middleware.
Pattern 1: Implement in Hooks
See the Security Guide for complete patterns.
// project.hook.ts
const hooks: ObjectHookDefinition = {
beforeCreate: async (ctx) => {
if (!ctx.user?.roles?.includes('manager')) {
throw new Error('Unauthorized: Only managers can create projects');
}
},
beforeUpdate: async (ctx) => {
const project = ctx.previousData;
if (project.owner !== ctx.user.id && !ctx.user.isAdmin) {
throw new Error('Unauthorized: You can only update your own projects');
}
}
};Pattern 2: Row-Level Security
Filter data by user in beforeFind hooks:
app.on('before:find', '*', async (ctx) => {
if (!ctx.user?.isAdmin) {
if (!ctx.query.filters) ctx.query.filters = [];
ctx.query.filters.push(['owner', '=', ctx.user.id]);
}
});Pattern 3: Field-Level Security
Remove sensitive fields in afterFind hooks:
app.on('after:find', 'user', async (ctx) => {
if (!ctx.user?.isAdmin) {
ctx.result = ctx.result.map(record => {
const { password, ssn, ...safeRecord } = record;
return safeRecord;
});
}
});Permission Metadata (Planned Feature)
⚠️ Note: Permission YAML files can be created, but they are not automatically enforced. Use them as documentation or implement enforcement in hooks.
Example Permission Config (Documentation Only):
# File: project.permission.yml
roles:
- admin
- manager
- user
object_permissions:
create: [admin, manager]
read: [admin, manager, user]
update: [admin, manager]
delete: [admin]
field_permissions:
budget:
read: [admin, manager]
update: [admin]
record_rules:
- name: owner_access
description: Owner has full access
condition:
field: owner_id
operator: "="
value: $current_user.id
permissions:
read: true
update: true
delete: trueThis configuration serves as documentation but requires implementation in hooks to be enforced.
Best Practices
✅ Do
- Implement authentication in middleware - Verify identity before requests reach ObjectQL
- Create ObjectQL context with user info - Pass authenticated user to context
- Check permissions in hooks - Enforce authorization for all operations
- Use HTTPS in production - Encrypt data in transit
- Validate all input - Use ObjectQL's validation system
- Log security events - Track authentication failures and permission denials
❌ Don't
- Don't assume automatic permission enforcement - You must implement it
- Don't trust client input - Always validate server-side
- Don't hardcode credentials - Use environment variables
- Don't expose sensitive errors - Return generic error messages
Complete Example
import express from 'express';
import { ObjectQL } from '@objectql/core';
import jwt from 'jsonwebtoken';
const app = express();
const objectql = new ObjectQL({ /* config */ });
// 1. Authentication Middleware
app.use('/api/objectql', async (req, res, next) => {
try {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'No token' });
const user = jwt.verify(token, process.env.JWT_SECRET);
req.user = user;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
});
// 2. Create Context with User
app.post('/api/objectql', async (req, res) => {
const ctx = objectql.createContext({
userId: req.user.id,
user: req.user
});
try {
const result = await ctx.object(req.body.object).find(req.body.args);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 3. Enforce Permissions in Hooks
objectql.on('before:create', 'project', async (ctx) => {
if (!ctx.user?.roles?.includes('manager')) {
throw new Error('Unauthorized');
}
});
await objectql.init();
app.listen(3000);Related Documentation
- Security Guide - Complete security patterns and examples
- Hooks - Implement permission checks in hooks
- Server Integration - Express/Next.js setup
See Also
- Implementation Status - Current feature status