Authentication and Authorization System β
Overview β
MeshMonitor implements a comprehensive authentication and authorization system supporting both local (username/password) and OpenID Connect (OIDC) authentication providers. The system uses session-based authentication with granular role-based access control (RBAC).
Table of Contents β
- Authentication Methods
- User Management
- Permission System
- API Endpoints
- Frontend Integration
- Security Considerations
- OIDC Configuration
- Testing
Authentication Methods β
Local Authentication β
Local authentication uses bcrypt for password hashing with a salt round of 12.
Features:
- Secure password hashing using bcrypt
- Password change functionality (available in user menu for local auth users)
- Admin password reset capability
- Session-based authentication
- Can be disabled when using OIDC exclusively
Login Flow:
- User submits username/password to
/api/auth/login - System validates credentials using bcrypt.compare()
- On success, creates session with user ID and role information
- Returns user object (without password hash) and permissions
OIDC Authentication β
OpenID Connect (OIDC) integration using the openid-client library.
Supported Features:
- Authorization Code Flow with PKCE
- State and nonce validation
- ID token verification
- Auto-user creation (configurable)
- Multiple identity providers
OIDC Flow:
- User initiates OIDC login via
/api/auth/oidc/login - System generates PKCE parameters (code_verifier, code_challenge)
- User redirects to identity provider
- Identity provider redirects back to
/api/auth/oidc/callback - System exchanges authorization code for ID token
- System validates ID token and creates/updates user
- Session is created and user is logged in
User Management β
User Schema β
interface User {
id: number;
username: string;
email: string | null;
displayName: string | null;
authProvider: 'local' | 'oidc';
oidcSubject: string | null; // OIDC subject identifier
isAdmin: boolean;
isActive: boolean;
createdAt: number;
lastLoginAt: number | null;
createdBy: number | null;
}Admin Operations β
Admins can perform the following operations via /api/users:
- List Users:
GET /api/users - Get User:
GET /api/users/:id - Create User:
POST /api/users(local auth only) - Update User:
PUT /api/users/:id - Deactivate User:
DELETE /api/users/:id - Toggle Admin Status:
PUT /api/users/:id/admin - Reset Password:
POST /api/users/:id/reset-password(local auth only) - View Permissions:
GET /api/users/:id/permissions - Update Permissions:
PUT /api/users/:id/permissions
Self-Service Operations β
All authenticated users can:
- View Auth Status:
GET /api/auth/status - Change Password:
POST /api/auth/change-password(local auth only) - Logout:
POST /api/auth/logout
Permission System β
Resource Types β
The system defines the following resources:
dashboard- View statistics and system infonodes- View and manage mesh nodesmessages- Send and receive mesh messagessettings- Application settingsconfiguration- Device configurationinfo- Telemetry and network informationautomation- Automated tasks and announcements
Permission Actions β
Each resource supports two actions:
read- View/access the resourcewrite- Modify the resource
Permission Schema β
interface Permission {
id: number;
userId: number;
resource: ResourceType;
canRead: boolean;
canWrite: boolean;
grantedAt: number;
grantedBy: number | null;
}Default Permissions β
Regular Users:
- Dashboard: read-only
- Nodes: read-only
- Messages: read-only
- Info: read-only
- Settings: no access
- Configuration: no access
- Automation: no access
Admin Users:
- All resources: read + write access
Permission Middleware β
Three middleware functions enforce permissions:
- optionalAuth(): Attaches user to request if authenticated (no enforcement)
- requireAuth(): Requires valid authenticated session
- requirePermission(resource, action): Requires specific resource permission
- requireAdmin(): Requires admin role
Example Usage:
// Public endpoint
app.get('/api/public', optionalAuth(), handler);
// Requires authentication
app.get('/api/profile', requireAuth(), handler);
// Requires specific permission
app.get('/api/nodes', requirePermission('nodes', 'read'), handler);
app.post('/api/messages', requirePermission('messages', 'write'), handler);
// Requires admin
app.get('/api/users', requireAdmin(), handler);API Endpoints β
Authentication Endpoints β
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/auth/status | GET | None | Get current auth status |
/api/auth/login | POST | None | Local authentication login |
/api/auth/logout | POST | None | Logout and destroy session |
/api/auth/change-password | POST | User | Change own password |
/api/auth/oidc/login | GET | None | Initiate OIDC flow |
/api/auth/oidc/callback | GET | None | OIDC callback handler |
User Management Endpoints β
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/users | GET | Admin | List all users |
/api/users/:id | GET | Admin | Get user by ID |
/api/users | POST | Admin | Create new user |
/api/users/:id | PUT | Admin | Update user |
/api/users/:id | DELETE | Admin | Deactivate user |
/api/users/:id/admin | PUT | Admin | Toggle admin status |
/api/users/:id/reset-password | POST | Admin | Reset user password |
/api/users/:id/permissions | GET | Admin | Get user permissions |
/api/users/:id/permissions | PUT | Admin | Update user permissions |
Frontend Integration β
AuthContext β
The AuthContext provides authentication state management:
interface AuthContextType {
authStatus: AuthStatus | null;
login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>;
checkAuth: () => Promise<void>;
oidcLogin: () => Promise<void>;
}Components β
UserMenu: Displays user info and logout button UsersTab: Admin-only user management interface
Permission Checking β
Frontend components can check permissions:
const { authStatus } = useAuth();
// Check if user is admin
if (authStatus?.user?.isAdmin) {
// Show admin features
}
// Check specific permission
if (authStatus?.permissions?.messages?.write) {
// Show send message button
}Security Considerations β
Password Security β
- Passwords hashed using bcrypt with 12 salt rounds
- Password hashes never sent to client
- Minimum password requirements should be enforced in production
Session Security β
- Sessions stored server-side (express-session)
- Session cookies use httpOnly flag (recommended)
- Session cookies use secure flag in production (HTTPS)
- Sessions invalidated on user deactivation
- Session timeout configurable via express-session
OIDC Security β
- PKCE (Proof Key for Code Exchange) required
- State parameter validated to prevent CSRF
- Nonce validated in ID token
- ID token signature verification
- Token endpoint uses client secret authentication
API Security β
- All sensitive endpoints require authentication
- Admin endpoints require admin role
- Users cannot modify their own admin status
- Users cannot delete their own account
- Password reset generates secure random passwords
- Audit logging for security events
Input Validation β
- All inputs validated and sanitized
- SQL injection prevented via parameterized queries
- XSS prevention via React's built-in escaping
- CSRF protection via same-site cookies
OIDC Configuration β
Quick Start β
To enable OIDC authentication with MeshMonitor:
- Register MeshMonitor in your identity provider
- Configure environment variables
- Restart MeshMonitor
- Login and configure user permissions
Environment Variables β
Required environment variables for OIDC:
# OIDC Configuration
OIDC_ENABLED=true # Required to enable OIDC
OIDC_ISSUER=https://your-identity-provider.com
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=https://your-app.com/api/auth/oidc/callback
OIDC_SCOPES="openid profile email" # Optional, defaults to this
OIDC_AUTO_CREATE_USERS=true # Optional, defaults to true
# Local Authentication Control
DISABLE_LOCAL_AUTH=false # Optional, set to true to disable local auth (OIDC only)Docker Compose Example β
version: '3.8'
services:
meshmonitor:
image: ghcr.io/yeraze/meshmonitor:latest
container_name: meshmonitor
ports:
- "8080:3001"
volumes:
- meshmonitor-data:/data
environment:
# Meshtastic connection
- MESHTASTIC_NODE_IP=192.168.1.100
- MESHTASTIC_TCP_PORT=4403
# OIDC Configuration
- OIDC_ENABLED=true
- OIDC_ISSUER=https://auth.example.com/application/o/meshmonitor/
- OIDC_CLIENT_ID=your-client-id-here
- OIDC_CLIENT_SECRET=your-client-secret-here
- OIDC_REDIRECT_URI=https://meshmonitor.example.com/api/auth/oidc/callback
# Optional: Disable local auth for OIDC-only mode
# - DISABLE_LOCAL_AUTH=true
# Session security
- SESSION_SECRET=your-random-secret-here
restart: unless-stopped
volumes:
meshmonitor-data:Dual Authentication Mode β
MeshMonitor supports both local and OIDC authentication simultaneously:
- Both Enabled (default): Users can login with either local credentials or OIDC
- OIDC Only: Set
DISABLE_LOCAL_AUTH=trueto force all users to use OIDC- Local login form will be hidden
/api/auth/loginendpoint will return 403- Useful for enterprise SSO deployments
- Change Password option hidden (managed by identity provider)
Supported Identity Providers β
The OIDC implementation uses OpenID Connect Discovery and supports any compliant provider:
- Authentik - Open-source Identity Provider
- Keycloak - Red Hat's open-source IAM
- Auth0 - Cloud identity platform
- Okta - Enterprise identity service
- Azure AD - Microsoft identity platform
- Google - Google Workspace
- GitHub (via OIDC endpoint)
- Any OpenID Connect 1.0 compliant provider
Provider Setup Examples β
Authentik β
1. Create a new Provider:
- Navigate to Applications β Providers
- Click Create
- Provider Type: OAuth2/OpenID Provider
- Name: MeshMonitor
- Client Type: Confidential
- Redirect URIs:
https://meshmonitor.example.com/api/auth/oidc/callback - Signing Key: Select your certificate
- Scopes: Ensure
openid,email,profileare selected - Click Finish
2. Create an Application:
- Navigate to Applications β Applications
- Click Create
- Name: MeshMonitor
- Slug: meshmonitor
- Provider: Select the provider created above
- Launch URL:
https://meshmonitor.example.com - Click Create
3. Copy Client Credentials:
- Go back to your Provider settings
- Copy the Client ID and Client Secret
4. Configure MeshMonitor:
environment:
- OIDC_ENABLED=true
- OIDC_ISSUER=https://auth.example.com/application/o/meshmonitor/
- OIDC_CLIENT_ID=<client-id-from-authentik>
- OIDC_CLIENT_SECRET=<client-secret-from-authentik>
- OIDC_REDIRECT_URI=https://meshmonitor.example.com/api/auth/oidc/callbackNote: The OIDC_ISSUER URL can be found in your Authentik provider settings under "OpenID Configuration Issuer".
Keycloak β
1. Create a new Client:
- Navigate to your Realm β Clients
- Click Create
- Client Type: OpenID Connect
- Client ID:
meshmonitor - Click Next
2. Configure Client Settings:
- Client Authentication: ON (for confidential client)
- Authorization: OFF (not needed)
- Standard Flow: ON
- Direct Access Grants: OFF
- Click Next
3. Configure Redirect URIs:
- Valid Redirect URIs:
https://meshmonitor.example.com/api/auth/oidc/callback - Web Origins:
https://meshmonitor.example.com - Click Save
4. Get Client Secret:
- Go to the Credentials tab
- Copy the Client Secret
5. Configure MeshMonitor:
environment:
- OIDC_ENABLED=true
- OIDC_ISSUER=https://keycloak.example.com/realms/myrealm
- OIDC_CLIENT_ID=meshmonitor
- OIDC_CLIENT_SECRET=<client-secret-from-keycloak>
- OIDC_REDIRECT_URI=https://meshmonitor.example.com/api/auth/oidc/callbackNote: Replace myrealm with your actual Keycloak realm name.
Google Workspace β
1. Create OAuth 2.0 Credentials:
- Go to Google Cloud Console
- Create a new project or select existing
- Navigate to APIs & Services β Credentials
- Click Create Credentials β OAuth Client ID
- Application Type: Web application
- Name: MeshMonitor
2. Configure Authorized Redirect URIs:
- Authorized Redirect URIs:
https://meshmonitor.example.com/api/auth/oidc/callback - Click Create
3. Copy Credentials:
- Copy the Client ID and Client Secret
4. Configure MeshMonitor:
environment:
- OIDC_ENABLED=true
- OIDC_ISSUER=https://accounts.google.com
- OIDC_CLIENT_ID=<client-id>.apps.googleusercontent.com
- OIDC_CLIENT_SECRET=<client-secret-from-google>
- OIDC_REDIRECT_URI=https://meshmonitor.example.com/api/auth/oidc/callbackAzure AD (Microsoft Entra ID) β
1. Register Application:
- Go to Azure Portal
- Navigate to Azure Active Directory β App Registrations
- Click New registration
- Name: MeshMonitor
- Supported Account Types: Choose appropriate option
- Redirect URI: Web -
https://meshmonitor.example.com/api/auth/oidc/callback - Click Register
2. Create Client Secret:
- Navigate to Certificates & secrets
- Click New client secret
- Description: MeshMonitor
- Expires: Choose appropriate duration
- Click Add
- Copy the Value (client secret) immediately
3. Configure API Permissions:
- Navigate to API permissions
- Ensure these are granted:
openidprofileemailUser.Read
4. Configure MeshMonitor:
environment:
- OIDC_ENABLED=true
- OIDC_ISSUER=https://login.microsoftonline.com/<tenant-id>/v2.0
- OIDC_CLIENT_ID=<application-id>
- OIDC_CLIENT_SECRET=<client-secret-from-azure>
- OIDC_REDIRECT_URI=https://meshmonitor.example.com/api/auth/oidc/callbackNote: Replace <tenant-id> with your Azure AD tenant ID (found in Azure AD overview).
First-Time Setup β
- Configure environment variables
- Restart application to initialize OIDC
- Login button will show "Login with OIDC" option
- First user to login via OIDC will be created automatically
- Promote first user to admin if needed via database or another admin
Testing β
Unit Tests β
- User Model Tests:
src/server/models/User.test.ts - Permission Model Tests:
src/server/models/Permission.test.ts
Integration Tests β
- Auth Routes Tests:
src/server/routes/authRoutes.test.ts - User Routes Tests:
src/server/routes/userRoutes.test.ts
Running Tests β
npm testTest Coverage β
Tests cover:
- Local authentication flow
- Password hashing and verification
- Session management
- Permission checking
- Permission granting and revoking
- Default permissions
- Admin operations
- User management CRUD
- Permission boundaries
- Security constraints
Audit Logging β
All security-relevant events are logged to the audit log:
login_success- Successful loginlogin_failed- Failed login attemptlogout- User logoutuser_created- New user createdoidc_user_created- OIDC user auto-createduser_updated- User information updateduser_deleted- User deactivatedadmin_status_changed- Admin status modifiedpermissions_updated- Permissions modifiedpassword_reset- Admin password resetpassword_changed- User password change
Audit logs include:
- User ID (if authenticated)
- Action type
- Resource type
- Action details (JSON)
- IP address
- Timestamp
Best Practices β
For Administrators β
Initial Setup:
- Create admin user immediately
- Change default admin password
- Configure OIDC if using external identity provider
User Management:
- Grant minimum necessary permissions
- Review permissions regularly
- Deactivate users instead of deleting (maintains audit trail)
- Monitor audit logs for suspicious activity
Production Deployment:
- Enable HTTPS (required for secure cookies)
- Use strong session secret
- Configure session timeout appropriately
- Enable OIDC for enterprise SSO
For Developers β
Adding New Protected Endpoints:
typescript// Public data app.get('/api/public', optionalAuth(), handler); // Requires authentication app.get('/api/profile', requireAuth(), handler); // Requires specific permission app.get('/api/resource', requirePermission('resource', 'read'), handler); // Requires admin app.get('/api/admin', requireAdmin(), handler);Adding New Resources:
- Update
ResourceTypeinsrc/types/permission.ts - Add to
RESOURCESarray with description - Update default permissions if needed
- Update frontend permission checks
- Update
Never:
- Store passwords in plain text
- Send password hashes to client
- Skip authentication on sensitive endpoints
- Allow users to escalate their own privileges
Troubleshooting β
Common Issues β
OIDC Login Fails:
- Verify OIDC_ISSUER is correct and accessible
- Check OIDC_REDIRECT_URI matches registered redirect URI
- Ensure OIDC_CLIENT_SECRET is correct
- Check browser console for errors
- Review server logs for OIDC errors
Session Not Persisting:
- Verify session middleware is configured
- Check cookie settings (secure, sameSite)
- Ensure session store is working
- Check for CORS issues
Permission Denied:
- Verify user has correct permissions
- Check if user is active
- Confirm endpoint requires correct permission level
- Review audit logs
Password Reset Not Working:
- Only works for local auth users
- User must exist and be active
- Admin privilege required
Migration Guide β
Upgrading from No Auth β
If migrating from an unauthenticated version:
- Run database migration:
001_add_auth_tables.ts - Create initial admin user via CLI tool
- Configure frontend to show login page
- Update API calls to use authenticated fetch
- Add permission checks to frontend components
- Test thoroughly before deployment
Adding OIDC to Existing Local Auth β
- Configure OIDC environment variables
- Restart application
- OIDC login option appears automatically
- Existing local users continue working
- New users can authenticate via either method
- Consider migrating users to OIDC over time
Support β
For issues or questions:
- Check server logs for detailed error messages
- Review audit logs for security events
- Consult API documentation
- Open GitHub issue with reproduction steps