Application Security Training Guide for Bike4Mind
1. Architecture Overview
The Bike4Mind platform is built as a monorepo with a robust security architecture that comprises:
- Frontend: Next.js-based client application
- Backend: API routes implemented with Next.js API routes and AWS Lambda functions
- Core Business Logic: Housed in
b4m-core
packages - Infrastructure: Managed through AWS services via SST (Serverless Stack)
2. Authentication System
2.1 Authentication Strategies
The platform supports multiple authentication methods:
export enum AuthStrategy {
Google = 'google',
Facebook = 'facebook', // Currently commented out in implementation
Github = 'github',
Okta = 'okta',
}
Additionally, the system supports:
- Username/password local authentication
- JWT token-based authentication for API access
2.2 JWT Implementation
JWT tokens are used for stateless authentication across the application:
- Access Tokens: Short-lived tokens (1 day expiry) for API authentication
- Refresh Tokens: Longer-lived tokens (2 days expiry) to renew access without re-authentication
- Token Storage: Client-side storage with appropriate security measures
- Token Verification: Server-side verification using the JWT secret
2.3 Authentication Flow
- User logs in via a strategy (local, OAuth providers)
- System generates JWT tokens (access + refresh)
- Client includes access token in the
Authorization
header - Server validates token and attaches user to request
- When token expires, refresh token is used to obtain a new access token
3. Authorization System (CASL)
The application uses CASL for fine-grained access control:
3.1 Permission Levels
export enum Permission {
read = 'read',
create = 'create',
update = 'update',
delete = 'delete',
share = 'share',
}
3.2 CASL Implementation
- Permissions are defined in the
ability.ts
file - Each request has an attached ability object via
req.ability
- Permission checks use the pattern:
req.ability.can(action, subject, conditions)
- Special permissions exist for admin users and role-based permissions
3.3 Permission Scope Patterns
The system implements several permission patterns:
-
Owner Permissions: Users can manage their own resources
const ownDocumentPermission = { userId: user.id };
allow(Permission.read, Resource, ownDocumentPermission); -
Shared Permissions: Resources can be shared with specific users with specific permissions
const userWithPermissions = {
'users.userId': user.id,
'users.permissions': permission,
}; -
Group Permissions: Users inherit permissions from groups they belong to
const groupWithPermissions = {
'groups.groupId': { $in: user.groups },
'groups.permissions': permission,
}; -
Global Permissions: Some resources can be globally readable/writable
allow(Permission.read, resource, { isGlobalRead: true });
4. API Security
4.1 BaseApi Middleware
All API routes utilize the baseApi
middleware, which:
- Establishes database connection
- Applies authentication middleware
- Attaches abilities to requests
- Handles errors consistently
- Provides logging
Example usage:
const handler = baseApi().get(
asyncHandler(async (req, res) => {
// Authentication is automatically handled
// req.ability is available for permission checks
// req.user contains the authenticated user
})
);