-
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.js, 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.js 18+"]
mui["Material UI (MUI)"]
maps["Leaflet / Google Maps"]
apollo["GraphQL Client"]
end
subgraph Auth["Authentication"]
pkce["OAuth 2.0 + PKCE"]
oiddict["OpenIddict 4.x"]
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 --> 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>Excel Export</i>"]
geofence["Geofence Manager<br/><i>Draw + Monitor</i>"]
admin["Administration<br/><i>Users, Groups, Operators</i>"]
profile["Profile<br/><i>Settings</i>"]
end
subgraph Services["API Services"]
gql_client["GraphQL Client<br/><i>Manager, Security,<br/>Router, Geofencing</i>"]
rest_client["REST Client<br/><i>Reporting</i>"]
end
end
router --> Pages
Pages --> Services
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 — Input sanitization via
formatValue - 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
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)