Week 4 Review & Best Practices
Consolidate your integration knowledge, review security best practices, learn telemetry patterns, and complete the Week 4 assessment.
๐Learning Objectives
- Recap all Week 4 integration topics
- Implement telemetry with sdk.telemetry
- Apply security best practices
- Complete the Week 4 assessment
- Preview Week 5 advanced topics
๐Week 4 Recap
- Workflow YAML definitions
- Step types (connector, approval, notification)
sdk.crud.run()to trigger workflows- SSE subscription for progress
- CrudGrid component
- Token flow architecture
- User context via
/api/idp/userinfo - Token exchange (RFC 8693)
- On-Behalf-Of (OBO) flow
- DPoP proof-of-possession
- AuthZEN evaluation API
sdk.authz.evaluate()- Batch evaluation for performance
- IfCan component pattern
- Decision context & explanations
- Proxy routing configuration
- Session management (cookies + Redis)
- CSRF protection
- Error handling patterns
- LLM proxy for AI features
๐Telemetry & Observability
Production plugins need observability. The SDK provides sdk.telemetry for structured logging, metrics, and tracing.
Using sdk.telemetry
// src/utils/telemetry.ts
const sdk = (globalThis as any).pluginSdk;
// Structured logging
export function logInfo(message: string, data?: Record<string, any>) {
sdk.telemetry.log('info', message, {
plugin: 'role-viewer',
...data
});
}
export function logError(message: string, error: Error, data?: Record<string, any>) {
sdk.telemetry.log('error', message, {
plugin: 'role-viewer',
error: error.message,
stack: error.stack,
...data
});
}
// Track user actions
export function trackEvent(event: string, properties?: Record<string, any>) {
sdk.telemetry.track(event, {
plugin: 'role-viewer',
timestamp: new Date().toISOString(),
...properties
});
}
// Measure performance
export function measureTiming(name: string, durationMs: number) {
sdk.telemetry.timing(name, durationMs, {
plugin: 'role-viewer'
});
}
Usage in Components
// Track API call timing
const fetchRoles = async () => {
const startTime = performance.now();
try {
logInfo('Fetching user roles');
const response = await sdk.api.fetch('/api/membership/users/me/roles');
const data = await response.json();
const duration = performance.now() - startTime;
measureTiming('fetch_roles', duration);
trackEvent('roles_loaded', {
count: data.roles?.length || 0,
duration_ms: Math.round(duration)
});
return data;
} catch (err: any) {
logError('Failed to fetch roles', err);
trackEvent('roles_load_error', { error: err.message });
throw err;
}
};
// Track user interactions
const handleRoleClick = (roleId: string) => {
trackEvent('role_clicked', { role_id: roleId });
navigateToDetail(roleId);
};
- Always include
pluginname for filtering - Use structured data, not string concatenation
- Track both success and failure paths
- Measure timing for performance-critical operations
- Don't log sensitive data (PII, tokens, passwords)
๐Security Best Practices
1. Always Use sdk.api.fetch()
Never use raw fetch() for API calls. The SDK handles CSRF, session cookies, and error normalization.
// โ Wrong - missing CSRF, session cookie issues
const response = await fetch('/api/crud/workflows/run', {
method: 'POST',
body: JSON.stringify(data)
});
// โ
Correct - SDK handles security automatically
const response = await sdk.api.fetch('/api/crud/workflows/run', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
2. Validate All User Input
// Use zod for runtime validation
import { z } from 'zod';
const AccessRequestSchema = z.object({
resourceId: z.string().uuid(),
justification: z.string().min(20).max(1000),
duration: z.enum(['24h', '7d', '30d', 'permanent'])
});
const handleSubmit = (formData: unknown) => {
// Throws if invalid
const validated = AccessRequestSchema.parse(formData);
// Safe to use
return sdk.crud.run('access-request', validated);
};
3. Fail Closed on Auth Errors
// Authorization check with fail-closed pattern
const canPerformAction = async (resource: string, action: string): Promise<boolean> => {
try {
const result = await sdk.authz.evaluate({
resource: { type: resource },
action: { name: action }
});
return result.decision === true;
} catch (err) {
logError('Auth check failed', err as Error);
// Fail closed - deny on error
return false;
}
};
4. Sanitize User-Generated Content
// Never render untrusted HTML directly
// โ XSS vulnerability
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// โ
Use text content (auto-escaped)
<div>{userComment}</div>
// โ
Or sanitize if HTML is required
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userComment) }} />
5. Protect Secrets
// โ Never hardcode secrets
const API_KEY = 'sk-abc123secret';
// โ Never log tokens/secrets
console.log('Token:', accessToken);
// โ
Secrets should only be in backend environment variables
// Plugins get tokens through secure httpOnly cookies managed by BFF
Security Checklist
| Check | Why It Matters |
|---|---|
โ
Use sdk.api.fetch() |
Automatic CSRF, session, error handling |
| โ Validate all input | Prevent injection and data corruption |
| โ Check permissions before actions | Defense in depth with PDP |
| โ Fail closed on auth errors | Deny access when uncertain |
| โ Don't log sensitive data | Prevent credential leakage |
| โ Sanitize user content | Prevent XSS attacks |
| โ Use HTTPS only | Encrypt data in transit |
๐Production Deployment Checklist
- Build: Production bundle passes without errors
- Integrity: SHA-256 hash updated in plugins.yaml
- Permissions: All required API endpoints in permissions.api
- PDP Application: pdp_application matches policy configuration
- Telemetry: Logging and event tracking implemented
- Error handling: All API calls have proper error handlers
- Loading states: Skeleton UI for async operations
- Empty states: Meaningful UI when no data
- Authorization: All sensitive actions gated with PDP checks
- Documentation: README with setup and usage instructions
โKnowledge Check
const result = await sdk.crud.run('workflow-name', {
input_field: 'value'
});
console.log(result.workflow_run_id);
Token Exchange: Trade your token for one with different audience/scopes. Used when you need a different token format.
OBO (On-Behalf-Of): A service acts as the user when calling another service. Preserves user identity in the call chain. The act claim tracks the acting party.
Use batch evaluation with sdk.authz.batchEvaluate():
const results = await sdk.authz.batchEvaluate([
{ resource: { type: 'doc', id: '1' }, action: { name: 'edit' } },
{ resource: { type: 'doc', id: '2' }, action: { name: 'edit' } },
// ... more
]);
// 1 API call instead of 10!
Tokens are stored in httpOnly cookies, which JavaScript cannot access. This protects against XSS attacksโeven if malicious script runs, it can't steal the token. The BFF retrieves tokens from Redis using the session ID.
The BFF returns 403 Forbidden. The plugin allowlist acts as a security boundaryโplugins can only call explicitly permitted endpoints. Always add new endpoints to permissions.api in plugins.yaml before using them.
๐Week 4 Assessment
Before moving to Week 5, verify you can complete these tasks:
โ Trigger Workflow
Use sdk.crud.run() with workflow name and input
โ Monitor Progress
Subscribe to workflow SSE events
โ Get User Info
Fetch /api/idp/userinfo for user context
โ Check Permission
Use sdk.authz.evaluate() with fail-closed
โ Batch Permissions
Use sdk.authz.batchEvaluate() for lists
โ Handle Errors
Status-specific handling for 401, 403, 429
You've mastered integration patterns for workflows, identity, authorization, and the BFF. Your plugins can now orchestrate business processes, enforce fine-grained permissions, and provide real-time feedback.
๐ฎWeek 5 Preview: Advanced Topics
Week 5 covers advanced platform capabilities:
| Day | Topic | What You'll Learn |
|---|---|---|
| 21 | MCP Integration | Model Context Protocol for AI tool integration |
| 22 | LLM Proxy & AI | Multi-provider AI, budget controls, cost tracking |
| 23 | ARIA Framework | AI Agent authorization, delegations, chain validation |
| 24 | NowConnect | Hybrid connectivity for on-premises systems |
| 25 | Receipts & Audit | Tamper-evident audit trails, compliance |
Week 5 teaches you how to build plugins that leverage AI capabilities while maintaining security and cost control. Learn how to authorize AI agents, track LLM costs, and create audit trails for AI-assisted operations.
๐Day 20 Checkpoint
- Reviewed all Week 4 integration topics
- Implemented telemetry with sdk.telemetry
- Applied security best practices
- Completed Week 4 assessment
- Ready for Week 5 advanced topics