BlueDesk — System Architecture
Single sign-on portal and application launcher for Blue Car Rental employees
System Overview
BlueDesk connects employees to all internal tools through a single Microsoft login. Three user roles interact with the system through a Next.js frontend backed by a Go API server, a PostgreSQL database, and Microsoft Entra ID for authentication.
Three Domains
BlueDesk is organized into three bounded domains with a strict dependency chain. Each domain has clear responsibilities and defined interfaces.
Authentication
Foundational domain. Establishes user identity via Microsoft Entra ID.
- OAuth 2.0 flow with Microsoft Entra ID
- Session JWT issuance and validation
- Token refresh lifecycle
- Login / logout handling
- Cookie security and session expiry (1h)
- CSRF state validation (oauth_state cookie)
- HTTP-only cookie management (session_token, refresh_token, user_info)
User & Access Management
Administrative domain. Manages who can access what.
- User directory (Microsoft Graph API sync)
- Application registry (register, configure, deactivate)
- Per-user, per-app permission assignment
- Granular roles: View, Edit, Admin, Configure
- User activation / deactivation (soft delete)
App Launcher
Employee-facing domain. The portal that users interact with daily.
- Launcher page with app tiles
- Per-app launch JWT generation
- User redirect to target applications
- Shows only apps assigned to user
- IT Admins get App Admin in all apps
Dependency Chain
Each domain depends on the one before it. Authentication is foundational.
Authentication Flow
The complete OAuth 2.0 authorization code flow with Microsoft Entra ID, from the user clicking "Sign In" through to landing on their dashboard.
sequenceDiagram
autonumber
actor User
participant FE as Next.js Frontend
(Port 3000)
participant BE as Go Backend
(Port 8080)
participant MS as Microsoft Entra ID
participant DB as PostgreSQL
User->>FE: Click "Sign In with Microsoft"
FE->>BE: GET /api/v1/auth/login
BE->>BE: Generate CSRF state + oauth_state cookie
BE-->>FE: auth_url + Set-Cookie: oauth_state
FE->>MS: Redirect to auth_url
User->>MS: Enter credentials (+ 2FA)
MS->>BE: GET /api/v1/auth/callback?code=...&state=...
BE->>BE: Validate CSRF (cookie vs query param)
BE->>MS: Exchange auth code for access token
MS-->>BE: Access token + user info
BE->>DB: Find or create user record
DB-->>BE: User data
BE->>FE: Set-Cookie: session_token, refresh_token, user_info
FE->>BE: GET /api/v1/user/me (verify role)
alt IT Admin
FE->>User: Show Admin Dashboard
else Regular / App Admin
FE->>User: Show App Launcher
end
Note over User,DB: First-time users are auto-created with no app access.
IT admin must assign applications.
App Launch Flow
When a user clicks on an application tile, BlueDesk generates a per-app JWT containing the user's identity and permissions, then redirects to the target app.
sequenceDiagram
autonumber
actor User
participant FE as Next.js Frontend
participant BE as Go Backend
participant DB as PostgreSQL
participant App as Target Application
User->>FE: Click app tile in launcher
FE->>BE: POST /api/v1/launcher/apps/{id}
BE->>DB: Check user_app_permissions
DB-->>BE: Permission record (role, can_view, can_edit, ...)
alt No permission found
BE-->>FE: 403 Forbidden
FE-->>User: "Access denied" message
else Has permission
BE->>BE: Generate launch JWT with
user identity + app permissions
BE-->>FE: Launch JWT + app URL
FE->>App: Redirect with JWT
App->>App: Validate JWT signature
App-->>User: Auto-logged in, permissions applied
end
Note over BE,App: The launch JWT is distinct from the session JWT.
It is scoped to a single application.
Database Schema
Four tables in the blue_desk_admin database.
The junction table user_app_permissions connects users to applications with granular role-based access.
| Column | Type | Key |
|---|---|---|
| id | uuid | PK |
| name | string | |
| display_name | string | |
| access_type | string | |
| app_url | string | |
| public_url | string | |
| is_active | boolean |
| Column | Type | Key |
|---|---|---|
| id | uuid | PK |
| string | ||
| microsoft_id | string | |
| is_it_admin | boolean | |
| is_active | boolean | |
| security_level | string | |
| session_timeout | int |
| Column | Type | Key |
|---|---|---|
| id | uuid | PK |
| user_id | uuid | FK → users |
| app_id | uuid | FK → apps |
| app_role | string | |
| can_view | boolean | |
| can_edit | boolean | |
| can_admin | boolean | |
| can_configure | boolean |
| Column | Type | Key |
|---|---|---|
| id | uuid | PK |
| user_id | uuid | FK → users |
| token_id | string | |
| expires_at | timestamp | |
| is_active | boolean |
Relationships
Application Integration Types
BlueDesk supports two integration models for internal applications, depending on whether the app has a public-facing interface or is admin-only.
Admin-Only Apps
Accessible only through BlueDesk by authenticated administrators. No public URL exists.
- Admin signs into BlueDesk via Microsoft SSO
- Admin sees app tile in launcher
- BlueDesk generates launch JWT with admin permissions
- Admin is redirected to the app's admin panel URL
- App validates JWT and grants access
Examples: Internal reporting tools, configuration dashboards, system monitors
Dual-Access Apps
Two entry points: a public interface with PIN auth, and an admin panel via BlueDesk JWT.
- Public path: Regular users access app at public URL with PIN auth
- Admin path: Admin signs into BlueDesk via Microsoft SSO
- BlueDesk generates launch JWT with admin permissions
- Admin is redirected to the app's admin panel URL
- App validates JWT and grants elevated access
Examples: Vehicle tracking (users log vehicles with PIN, admins configure via BlueDesk), queue management systems
JWT Token Structure
BlueDesk uses two distinct JWT tokens. Understanding the difference is critical: the session JWT is about who you are; the launch token is about what you can do in a specific app.
Session JWT
Authentication DomainProves the user is authenticated with BlueDesk. Issued after successful Microsoft OAuth login. Expires after 1 hour.
Used for: Authenticating API requests to the BlueDesk backend. Stored in an HTTP-only session_token cookie (not localStorage).
Contains: User identity, admin status, session metadata.
Consumed by: BlueDesk backend only.
Launch Token
App Launcher DomainProves the user is authorized for a specific application. Generated on-demand when the user clicks an app tile.
Used for: Authenticating the user at the target application. Passed during redirect.
Payload:
{
"user_id": "uuid",
"email": "user@example.com",
"is_it_admin": false,
"app_role": "user",
"can_view": true,
"can_edit": false,
"can_admin": false,
"can_configure": false,
"app_id": "uuid",
"app_name": "app-slug",
"exp": 1234567890
}
Consumed by: The target integrated application.
Permission Matrix
Permissions are assigned per-user, per-application. Each permission grant specifies a role and four boolean capabilities. Roles are presets; individual flags can be customized.
| Role | View | Edit | Admin | Configure | Typical Use |
|---|---|---|---|---|---|
| User | ✓ | ✕ | ✕ | ✕ | View data only |
| User (Edit) | ✓ | ✓ | ✕ | ✕ | Data entry |
| App Admin | ✓ | ✓ | ✓ | ✓ | Full control |
Design System
Brand guidelines, typography, color palette, localization strategy, and shared UI components used across the BlueDesk admin portal frontend.
Typography
BLUE Display — titles and headings (font-blue-display).
Custom font with 90% size-adjust fallback.
Host Grotesk — body text (default sans).
Variable weight font supporting weights 100–900.
Brand Colors
| Color | Hex | Usage |
|---|---|---|
| Dark Blue | #00143B |
Sidebar background, primary dark surfaces |
| Bright Blue | #1B56FD |
Accent color, buttons, active states |
| Light Blue | #C3DCFF |
Hover states, subtle highlights |
| White | #F8F8F8 |
Page background, card surfaces |
| Warm White | #EDEDED |
Secondary backgrounds, borders |
| Lava | #FD6112 |
Warning accents, attention indicators |
Localization
Icelandic (is) — all UI strings are translated. Application names and company names remain in English.
Components
BlueLogo — SVG component at components/ui/blue-logo.tsx.
Renders the BLUE wordmark with configurable className and fill props.