Learn how to implement user authentication and authorization with the Asterisms JS SDK Backend.
The authorization service provides user authentication, permission checking, and role-based access control for your Asterisms applications.
const auth = sdk.authorization();try {
const user = await auth.getCurrentUser();
if (user) {
console.log('User:', user.name, user.email);
} else {
console.log('No user authenticated');
}
} catch (error) {
console.error('Authentication error:', error);
}const canRead = await auth.hasPermission('resource.read');
const canWrite = await auth.hasPermission('resource.write');
const canDelete = await auth.hasPermission('resource.delete');
if (canRead) {
// User can read the resource
}// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
const sdk = locals.sdk;
if (!sdk) {
return { user: null };
}
try {
const auth = sdk.authorization();
const user = await auth.getCurrentUser();
return {
user: user
? {
id: user.id,
name: user.name,
email: user.email,
roles: user.roles
}
: null
};
} catch (error) {
console.error('Auth error in layout:', error);
return { user: null };
}
};// src/routes/admin/+page.server.ts
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
const sdk = locals.sdk;
if (!sdk) {
throw redirect(302, '/login');
}
try {
const auth = sdk.authorization();
const user = await auth.getCurrentUser();
if (!user) {
throw redirect(302, '/login');
}
// Check admin permission
const isAdmin = await auth.hasPermission('admin.access');
if (!isAdmin) {
throw redirect(302, '/unauthorized');
}
return {
user: {
id: user.id,
name: user.name,
email: user.email
}
};
} catch (error) {
console.error('Admin page auth error:', error);
throw redirect(302, '/login');
}
};<!-- src/lib/components/AuthGuard.svelte -->
<script lang="ts">
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
export let requiredPermission: string | null = null;
export let requireAuth = true;
$: user = $page.data.user;
$: isAuthenticated = !!user;
let hasPermission = false;
onMount(async () => {
if (requireAuth && !isAuthenticated) {
goto('/login');
return;
}
if (requiredPermission && isAuthenticated) {
try {
const response = await fetch('/api/auth/check-permission', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ permission: requiredPermission })
});
const result = await response.json();
hasPermission = result.hasPermission;
if (!hasPermission) {
goto('/unauthorized');
}
} catch (error) {
console.error('Permission check error:', error);
goto('/unauthorized');
}
} else {
hasPermission = true;
}
});
</script>
{#if isAuthenticated && hasPermission}
<slot />
{:else if requireAuth && !isAuthenticated}
<div class="auth-required">
<p>Authentication required</p>
<a href="/login">Login</a>
</div>
{:else if requiredPermission && !hasPermission}
<div class="permission-required">
<p>Insufficient permissions</p>
<p>Required: {requiredPermission}</p>
</div>
{/if}// src/routes/api/auth/check-permission/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const POST: RequestHandler = async ({ request, locals }) => {
try {
const sdk = locals.sdk;
if (!sdk) {
return json({ hasPermission: false }, { status: 503 });
}
const { permission } = await request.json();
if (!permission) {
return json({ hasPermission: false }, { status: 400 });
}
const auth = sdk.authorization();
const hasPermission = await auth.hasPermission(permission);
return json({ hasPermission });
} catch (error) {
console.error('Permission check error:', error);
return json({ hasPermission: false }, { status: 500 });
}
};// src/routes/api/admin/users/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ locals }) => {
try {
const sdk = locals.sdk;
if (!sdk) {
return json({ error: 'SDK not available' }, { status: 503 });
}
const auth = sdk.authorization();
// Check authentication
const user = await auth.getCurrentUser();
if (!user) {
return json({ error: 'Authentication required' }, { status: 401 });
}
// Check admin permission
const isAdmin = await auth.hasPermission('admin.users.read');
if (!isAdmin) {
return json({ error: 'Insufficient permissions' }, { status: 403 });
}
// Fetch users (example)
const users = await getUserList(); // Your implementation
return json({ users });
} catch (error) {
console.error('Admin users API error:', error);
return json({ error: 'Internal server error' }, { status: 500 });
}
};import { createAuthorizerMiddleware } from '@asterisms/sdk-backend';
const app = express();
const sdk = getAsterismsBackendSDK(props);
// Create authorization middleware
const authMiddleware = createAuthorizerMiddleware(sdk.authorization());
// Use middleware
app.use('/api/protected', authMiddleware);
// Protected routes
app.get('/api/protected/data', (req, res) => {
// User is authenticated here
const user = req.user; // Added by middleware
res.json({ user });
});function requirePermission(permission: string) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const sdk = req.locals?.sdk;
if (!sdk) {
return res.status(503).json({ error: 'SDK not available' });
}
const auth = sdk.authorization();
const hasPermission = await auth.hasPermission(permission);
if (!hasPermission) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
} catch (error) {
console.error('Permission middleware error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
}
// Usage
app.get('/api/admin/settings', requirePermission('admin.settings.read'), (req, res) => {
// User has admin.settings.read permission
res.json({ settings: {} });
});const auth = sdk.authorization();
const user = await auth.getCurrentUser();
if (user) {
console.log('User ID:', user.id);
console.log('Name:', user.name);
console.log('Email:', user.email);
console.log('Roles:', user.roles);
console.log('Permissions:', user.permissions);
console.log('Created:', user.createdAt);
console.log('Last Login:', user.lastLoginAt);
}async function checkUserRole(requiredRole: string): Promise<boolean> {
try {
const auth = sdk.authorization();
const user = await auth.getCurrentUser();
if (!user) {
return false;
}
return user.roles.includes(requiredRole);
} catch (error) {
console.error('Role check error:', error);
return false;
}
}
// Usage
const isAdmin = await checkUserRole('admin');
const isModerator = await checkUserRole('moderator');// Permission examples
const permissions = [
'resource.read', // Read access
'resource.write', // Write access
'resource.delete', // Delete access
'admin.users.read', // Read users
'admin.users.write', // Modify users
'admin.system.config', // System configuration
'workspace.create', // Create workspaces
'workspace.manage' // Manage workspaces
];async function checkMultiplePermissions(permissions: string[]): Promise<Record<string, boolean>> {
const auth = sdk.authorization();
const results: Record<string, boolean> = {};
for (const permission of permissions) {
try {
results[permission] = await auth.hasPermission(permission);
} catch (error) {
console.error(`Permission check failed for ${permission}:`, error);
results[permission] = false;
}
}
return results;
}
// Usage
const permissions = await checkMultiplePermissions([
'resource.read',
'resource.write',
'admin.access'
]);
console.log('User permissions:', permissions);import { AsterismsSDKError } from '@asterisms/sdk-backend';
try {
const auth = sdk.authorization();
const user = await auth.getCurrentUser();
} catch (error) {
if (error instanceof AsterismsSDKError) {
switch (error.code) {
case 'AUTH_TOKEN_EXPIRED':
// Handle token expiration
break;
case 'AUTH_TOKEN_INVALID':
// Handle invalid token
break;
case 'AUTH_USER_NOT_FOUND':
// Handle user not found
break;
default:
console.error('Authentication error:', error.message);
}
} else {
console.error('Unknown auth error:', error);
}
}try {
const hasPermission = await auth.hasPermission('admin.access');
} catch (error) {
if (error instanceof AsterismsSDKError) {
switch (error.code) {
case 'PERMISSION_NOT_FOUND':
// Permission doesn't exist
break;
case 'USER_NOT_AUTHENTICATED':
// User not logged in
break;
default:
console.error('Permission error:', error.message);
}
}
}import { describe, it, expect, beforeEach } from 'vitest';
import { getAsterismsBackendSDK } from '@asterisms/sdk-backend';
describe('Authentication Service', () => {
let sdk: AsterismsBackendSDK;
beforeEach(async () => {
sdk = getAsterismsBackendSDK(testProps);
await sdk.boot();
});
it('should return current user when authenticated', async () => {
const auth = sdk.authorization();
const user = await auth.getCurrentUser();
expect(user).toBeDefined();
expect(user.id).toBeDefined();
expect(user.email).toBeDefined();
});
it('should check permissions correctly', async () => {
const auth = sdk.authorization();
const hasPermission = await auth.hasPermission('resource.read');
expect(typeof hasPermission).toBe('boolean');
});
});import { test, expect } from '@playwright/test';
test('protected page redirects when not authenticated', async ({ page }) => {
await page.goto('/admin');
await expect(page).toHaveURL('/login');
});
test('authenticated user can access protected page', async ({ page }) => {
// Login first
await page.goto('/login');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'password');
await page.click('[type="submit"]');
// Access protected page
await page.goto('/admin');
await expect(page).toHaveURL('/admin');
await expect(page.locator('h1')).toContainText('Admin Panel');
});// ❌ Bad: Trust client-side checks
if (user.roles.includes('admin')) {
// Dangerous - client can modify this
}
// ✅ Good: Server-side validation
const isAdmin = await auth.hasPermission('admin.access');
if (isAdmin) {
// Safe - validated on server
}// ❌ Bad: Broad permissions
await auth.hasPermission('admin');
// ✅ Good: Specific permissions
await auth.hasPermission('admin.users.delete');// ❌ Bad: Expose error details
catch (error) {
res.status(500).json({ error: error.message });
}
// ✅ Good: Generic error messages
catch (error) {
console.error('Auth error:', error);
res.status(401).json({ error: 'Authentication required' });
}// Example rate limiting for auth endpoints
const rateLimit = require('express-rate-limit');
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many authentication attempts'
});
app.use('/api/auth', authLimiter);