Server Utilities¶
@sentinel-auth/js/server provides JWT verification, permission checks, and RBAC action checks for Node.js and Edge runtimes.
verifyToken¶
Verify a Sentinel JWT against a JWKS endpoint. Uses jose (Edge-compatible).
const payload = await verifyToken(token, {
jwksUrl: 'http://localhost:9003/.well-known/jwks.json',
})
const user = payloadToUser(payload)
// { userId, email, name, workspaceId, workspaceSlug, workspaceRole, groups }
| Option | Type | Default | Description |
|---|---|---|---|
jwksUrl |
string |
required | Sentinel JWKS endpoint |
audience |
string |
"sentinel:access" |
Expected aud claim |
issuer |
string |
-- | Expected iss claim |
JWKS keys are fetched and cached automatically.
PermissionClient¶
Zanzibar-style permission checks. Mirrors the Python SDK.
const permissions = new PermissionClient(
'http://localhost:9003', 'my-service', 'sk_my_service_key',
)
can(token, resourceType, resourceId, action) -- single permission check.
check(token, checks) -- batch check.
const results = await permissions.check(token, [
{ service_name: 'my-service', resource_type: 'document', resource_id: docId, action: 'view' },
{ service_name: 'my-service', resource_type: 'document', resource_id: docId, action: 'edit' },
])
registerResource(request) -- register a resource (service key, no JWT needed).
await permissions.registerResource({
service_name: 'my-service', resource_type: 'document', resource_id: docId,
workspace_id: workspaceId, owner_id: userId, visibility: 'workspace',
})
share(token, resourceType, resourceId, share) -- grant access.
await permissions.share(token, 'document', docId, {
grantee_type: 'user', grantee_id: targetUserId, permission: 'edit',
})
accessible(token, resourceType, action, workspaceId, limit?) -- list accessible resource IDs.
const result = await permissions.accessible(token, 'document', 'view', workspaceId)
// { resource_ids: ['doc1', 'doc2'], has_full_access: false }
RoleClient¶
RBAC action checks. Mirrors the Python SDK.
registerActions(actions) -- register at startup (service key).
await roles.registerActions([
{ action: 'notes:create', description: 'Create notes' },
{ action: 'notes:export', description: 'Export notes' },
])
checkAction(token, action, workspaceId) -- check single action.
getUserActions(token, workspaceId) -- list permitted actions.
Express example¶
import express from 'express'
import { verifyToken, payloadToUser, PermissionClient } from '@sentinel-auth/js/server'
const app = express()
const permissions = new PermissionClient('http://localhost:9003', 'my-service', process.env.SERVICE_KEY)
async function authenticate(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return res.status(401).json({ error: 'Unauthorized' })
try {
req.user = payloadToUser(await verifyToken(token, {
jwksUrl: 'http://localhost:9003/.well-known/jwks.json',
}))
req.token = token
next()
} catch {
res.status(401).json({ error: 'Invalid token' })
}
}
app.get('/api/documents/:id', authenticate, async (req, res) => {
const allowed = await permissions.can(req.token, 'document', req.params.id, 'view')
if (!allowed) return res.status(403).json({ error: 'Forbidden' })
res.json(await getDocument(req.params.id))
})