Skip to Content
GuidesServer GuidesAuthorization

Authorization

Control who can read, write, and delete data with fine-grained permissions.

API Reference: PrismaAdapter · SyncServer

Current State (v1.0)

Gluonic v1.0 has ownership-based authorization:

import { PrismaAdapter } from '@gluonic/server-prisma' const database = PrismaAdapter({ prisma, models: { post: { ownership: 'userId', // Only owner can read/write versioning: 'version' } } })

Behavior:

  • ✅ Bootstrap only returns records where userId = currentUser.id
  • ✅ Mutations only allowed if userId = currentUser.id
  • ❌ Can’t have “public read, owner write” (see limitations below)

Limitations

Binary Permissions

Current model is all-or-nothing:

ConfigurationReadWriteUse Case
ownership: 'userId'Owner onlyOwner onlyPrivate data
No ownershipEveryoneEveryonePublic data

Missing: Public read + Owner write (e.g., blog comments).

Workaround

Use client-side checks (NOT SECURE):

// Remove ownership from model comment: { versioning: 'version' } // No ownership = public // Client-side check (can be bypassed!) const canEdit = comment.authorId === currentUserId {canEdit && <EditButton />}

Problem: Malicious client can bypass this.

Planned: Authorization Adapter (v0.2+)

Fine-grained permissions coming soon:

import { OwnershipAuthorization } from '@gluonic/auth-ownership' const authz = OwnershipAuthorization({ comment: { read: 'public', // Everyone can read write: 'owner', // Only author can write delete: 'owner' // Only author can delete }, post: { read: (userId, record) => { // Custom logic return record.published || record.authorId === userId }, write: 'owner', delete: 'owner' } }) const database = PrismaAdapter({ prisma, authorization: authz // Plug in! })

Will support:

  • ✅ Granular permissions (read/write/delete)
  • ✅ Public read + owner write
  • ✅ Custom authorization functions
  • ✅ Role-based access control (RBAC)
  • ✅ Server-side enforcement

Current Best Practices

Until authorization adapter is available:

1. Ownership-Based Models

For private data:

user: { ownership: 'userId', versioning: 'version' } // Only user sees their own data ✓

2. Public Models

For shared data (careful!):

tag: { versioning: 'version' } // Everyone can read AND write ⚠️

3. Server-Side Validation

Add custom validation:

import { SyncServer } from '@gluonic/server' const server = SyncServer({ database, auth: requireAuth, // Validate mutations before applying validate: async (mutation, context) => { if (mutation.type === 'post' && mutation.op === 'd') { // Check if user can delete this post const post = await prisma.post.findUnique({ where: { id: mutation.id } }) if (post.authorId !== context.user.id) { throw new Error('Cannot delete other users posts') } } } })

Note: This requires custom /tx endpoint (not using adapter’s built-in one).

4. Row-Level Security (Database)

Use database-level permissions:

-- PostgreSQL RLS ALTER TABLE comments ENABLE ROW LEVEL SECURITY; CREATE POLICY comments_select_policy ON comments FOR SELECT USING (true); -- Everyone can read CREATE POLICY comments_modify_policy ON comments FOR UPDATE USING (author_id = current_setting('app.current_user_id'));

Then in auth:

export async function requireAuth(req: FastifyRequest) { // ... verify token ... // Set session variable for RLS await prisma.$executeRaw` SET LOCAL app.current_user_id = ${(req as any).user.id} ` }

Future: Role-Based Access Control

Example of planned RBAC support:

import { RBACAuthorization } from '@gluonic/auth-rbac' const authz = RBACAuthorization({ roles: { admin: { post: { read: true, write: true, delete: true }, user: { read: true, write: true, delete: true } }, editor: { post: { read: true, write: true, delete: false }, user: { read: true, write: false, delete: false } }, viewer: { post: { read: true, write: false, delete: false }, user: { read: true, write: false, delete: false } } } }) const database = PrismaAdapter({ prisma, authorization: authz, getUserRole: async (userId) => { const user = await prisma.user.findUnique({ where: { id: userId } }) return user.role // 'admin', 'editor', 'viewer' } })

Security Checklist

✅ Required

  • HTTPS in production (never HTTP)
  • Validate tokens before database queries
  • Use strong JWT secrets (32+ characters)
  • Set token expiration
  • Rate limit auth endpoints
  • Log authentication failures
  • Rotate secrets regularly
  • Use refresh tokens for long sessions
  • Implement token revocation
  • Add IP-based rate limiting
  • Monitor for brute force attacks
  • Use secure cookies for web clients
  • Implement MFA for sensitive operations

⚠️ Optional

  • Row-level security (database)
  • API key management
  • OAuth provider integration
  • Single sign-on (SSO)

Troubleshooting

401 errors on valid tokens

Problem: Client sends valid token but gets 401.

Solutions:

  • Check JWT secret matches between issuer and verifier
  • Verify token hasn’t expired
  • Check token is sent in correct header (Authorization: Bearer ...)
  • For WebSocket, check token in query param

WebSocket auth fails

Problem: WebSocket connections immediately close with 401.

Solution:

// Client must send token in query param const ws = new WebSocket(`wss://api.example.com/sync/v1/ws?token=${token}`)

Bootstrap returns empty array

Problem: User is authenticated but bootstrap returns [].

Solutions:

  • Check ownershipFields in adapter config matches your schema
  • Verify data exists with correct ownership field value
  • Check user.id being used as orgId matches data

Next Steps

Last updated on