-
Notifications
You must be signed in to change notification settings - Fork 1
Technology
TrackHub ensures secure and seamless integration across its ecosystem by implementing the Authorization Code Flow with Proof Key for Code Exchange (PKCE) via OpenIddict as the authorization method for both frontend and backend services. Built with React 19 + TypeScript on Vite, the frontend delivers a responsive user experience, while the backend services leverage GraphQL (via HotChocolate) and REST Minimal APIs to address specific integration needs.
graph TB
subgraph Frontend["Frontend Layer"]
react["React 19 + TypeScript"]
vite["Vite + Vitest"]
mui["Material UI (MUI)"]
maps["Leaflet / Google Maps"]
tanstack["TanStack Query"]
apollo["GraphQL Client<br/><i>codegen'd documents</i>"]
end
subgraph Auth["Authentication"]
pkce["OAuth 2.0 + PKCE"]
oiddict["OpenIddict"]
jwt["JWT Bearer Tokens"]
end
subgraph Backend["Backend Services"]
dotnet[".NET 10"]
hc["HotChocolate (GraphQL)"]
minapi["Minimal APIs (REST)"]
cqrs["Custom CQRS Mediator"]
fv["FluentValidation"]
end
subgraph Data["Data Layer"]
pg["PostgreSQL 14+"]
postgis["PostGIS Extension"]
ef["Entity Framework Core"]
end
subgraph Infra["Infrastructure"]
docker["Docker + Compose"]
nginx["Nginx Reverse Proxy"]
polly["Polly (Resilience)"]
end
react --> tanstack --> apollo --> hc
react --> pkce --> oiddict
oiddict --> jwt --> hc & minapi
hc --> cqrs --> ef --> pg
minapi --> cqrs
pg --- postgis
TrackHub uses the Authorization Code Flow with PKCE for all public clients (web and mobile). This eliminates the need to store client secrets in the browser while maintaining security through a cryptographic code verifier.
sequenceDiagram
participant User as User (Browser)
participant App as TrackHub Web<br/>(React)
participant Auth as AuthorityServer<br/>(OpenIddict)
participant API as Backend Services
Note over App: Generate code_verifier (random)<br/>Compute code_challenge = SHA256(code_verifier)
App->>Auth: GET /Identity/authorize<br/>response_type=code<br/>client_id=web_client<br/>code_challenge=...<br/>code_challenge_method=S256<br/>scope=web_scope<br/>redirect_uri=.../callback
Auth->>User: Login Page
User->>Auth: Username + Password
Auth-->>App: 302 Redirect → /callback?code=AUTH_CODE
App->>Auth: POST /Identity/token<br/>grant_type=authorization_code<br/>code=AUTH_CODE<br/>code_verifier=ORIGINAL_VERIFIER<br/>redirect_uri=.../callback
Note over Auth: Verify: SHA256(code_verifier) == code_challenge
Auth-->>App: { access_token, refresh_token, expires_in }
App->>API: GraphQL Request<br/>Authorization: Bearer {access_token}
API->>Auth: Validate JWT Signature + Audience
API-->>App: Response Data
Note over App: When token expires...
App->>Auth: POST /Identity/token<br/>grant_type=refresh_token<br/>refresh_token=...
Auth-->>App: { new_access_token, new_refresh_token }
| Client | Type | Scope | Usage |
|---|---|---|---|
web_client |
Public | web_scope |
Web browser application |
mobile_client |
Public | mobile_scope |
Mobile application |
sync_worker_client |
Confidential | service_scope |
Backend service-to-service |
| Endpoint | Purpose |
|---|---|
/Identity/authorize |
Start authorization flow |
/Identity/token |
Exchange code for tokens / refresh tokens |
/Identity/revoke |
Revoke a token |
/Identity/logout |
End session |
/Identity/.well-known/openid-configuration |
OIDC discovery metadata |
graph TB
subgraph App["TrackHub Web (React)"]
router["React Router"]
auth_mod["OAuth Module<br/><i>PKCE + Token Refresh Mutex</i>"]
subgraph Pages["Pages"]
dash["Dashboard<br/><i>Live Map + Positions</i>"]
reports["Reports<br/><i>Preview + Excel/PDF Export</i>"]
geofence["Geofence Manager<br/><i>Polygons + Circles</i>"]
admin["Account Management<br/><i>Users, Units, Documents, Alerts</i>"]
sysadmin["System Administration<br/><i>Accounts, Clients, Features</i>"]
gps["GPS Integration<br/><i>Operators, Sync, Health</i>"]
profile["Profile<br/><i>Settings</i>"]
end
help["Contextual Help<br/><i>Help button / F1, EN+ES topics</i>"]
subgraph Services["API Services"]
gql_client["GraphQL Client<br/><i>Manager, Security, Router,<br/>Geofencing, Telemetry</i>"]
rest_client["REST Client<br/><i>Reporting, Documents</i>"]
end
end
router --> Pages
Pages --> Services
Pages --- help
auth_mod -->|Bearer Token| Services
gql_client -->|"GraphQL over HTTPS"| backend["Backend Services"]
rest_client -->|"REST over HTTPS"| reporting["Reporting API"]
- Token refresh mutex — Prevents race conditions during concurrent token refresh
- GraphQL injection prevention — Values travel as GraphQL variables only (no string interpolation of user input)
- Schema-checked operations — Every GraphQL document is validated by codegen against the producers' exported SDLs; drift is a compile error
- Error Boundary — React component-level error handling
-
Notification system — MUI Snackbar replaces raw
alert()calls -
Security headers —
X-Content-Type-Options,X-Frame-Options - Request timeout — 30-second Axios timeout on all requests (60 s for file transfers)
-
Help content rendering — Markdown rendered with raw HTML disabled at both authoring (build validation) and rendering (
skipHtml) layers
User documentation ships inside the portal build: one Markdown topic per screen/feature per language under TrackHub/public/help/{en,es}/, with YAML frontmatter mapping topics to route keys. A build-time tool (scripts/build-help.mjs) validates the content (language parity, screen coverage against routes.tsx in both directions, internal topic: links, no raw HTML) and generates a manifest.json consumed by the Help modal (Help button / F1 on every screen: contextual topic, browsable index, client-side search). See the User Guide page.
Each backend service follows Clean Architecture with four layers:
graph LR
subgraph Layers["Dependency Direction →"]
web["Web<br/><i>Endpoints</i>"]
app["Application<br/><i>Commands / Queries</i>"]
domain["Domain<br/><i>Entities / Interfaces</i>"]
infra["Infrastructure<br/><i>DB / External APIs</i>"]
end
web --> app --> domain
infra --> domain
subgraph Pipeline["CQRS Mediator Pipeline"]
direction TB
b1["1. Logging"]
b2["2. Validation<br/><i>FluentValidation</i>"]
b3["3. Authorization<br/><i>[Authorize] attribute</i>"]
b4["4. Caching<br/><i>[Caching] attribute</i>"]
b5["5. Rate Limiting<br/><i>[RateLimiting] attribute</i>"]
b6["6. Handler Execution"]
b1 --> b2 --> b3 --> b4 --> b5 --> b6
end
All service-to-service calls use GraphQL with:
- Polly resilience — Retry (3×, exponential backoff) + circuit breaker
- Bearer token propagation — User context forwarded on downstream calls
- 30-second timeout — Configurable per client
- Client credentials flow — For background services (SyncWorker)
Platform
- Architecture
- Technology
- Inter-Service Communication
- Database
- Security and Identity
- User Permissions Overview
Services
- Manager
- Telemetry
- Router
- Adding a Provider
- Geofencing
- Trip Management
- Reporting
- Common Library
- Frontend
Engineering
User docs
- User Guide (ships in the app)