Skip to content

Technology

Sergio Hernandez edited this page Jul 24, 2026 · 8 revisions

Technology

TrackHub is built on .NET 10 and React 19 + TypeScript. Backend services expose GraphQL (via HotChocolate) with REST Minimal APIs where file transfer or anonymous access is required. Authentication is the Authorization Code Flow with PKCE via OpenIddict, for both browser and mobile clients.

Related pages: Architecture · Security and Identity · Frontend


Technology Stack

graph TB
    subgraph Frontend["Frontend Layer"]
        react["React 19 + TypeScript 7"]
        vite["Vite 8 + Vitest 4"]
        mui["Material UI (MUI)"]
        maps["Leaflet / Google Maps"]
        tanstack["TanStack Query"]
        codegen["graphql-codegen<br/><i>typed 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 16 (GraphQL)"]
        minapi["Minimal APIs (REST)"]
        cqrs["Custom CQRS Mediator"]
        fv["FluentValidation"]
    end

    subgraph Data["Data Layer"]
        pg["PostgreSQL 14+"]
        postgis["PostGIS Extension"]
        ef["EF Core 10"]
    end

    subgraph Infra["Infrastructure"]
        docker["Docker + Compose"]
        nginx["Nginx Reverse Proxy"]
        polly["Polly (Resilience)"]
    end

    react --> tanstack --> codegen --> hc
    react --> pkce --> oiddict
    oiddict --> jwt --> hc & minapi
    hc --> cqrs --> ef --> pg
    minapi --> cqrs
    pg --- postgis
Loading

Versions

Component Version Notes
.NET / ASP.NET Core / EF Core 10.0 Across every repository, including tools/Tools.McpExtractor
HotChocolate 16.5 Every GraphQL service
PostgreSQL 14+ PostGIS required for the geofencing and trip schemas
React 19
TypeScript 7 strict; tsc is the TypeScript lint gate
Vite / Vitest 8 / 4 Rolldown-based Vite; react-scripts/CRA has been removed

The project convention is prefer latest: a newly introduced component is never pinned back. If a tool lags (as typescript-eslint currently does for TypeScript 7), scope or disable that tool rather than downgrading the stack. When bumping the shared stack, remember that Tools.McpExtractor pins EF Core directly and must move with it.


Authentication Flow (PKCE)

TrackHub uses the Authorization Code Flow with PKCE for all public clients (web, mobile and driver mobile). This removes the need to store a client secret in the browser while keeping the exchange secure 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 }
Loading

OAuth Clients

Client Type Grants Scope Usage
web_client Public authorization_code, refresh_token web_scope React web portal
mobile_client Public authorization_code, refresh_token mobile_scope Mobile application
driver_mobile_client Public authorization_code, refresh_token driver_mobile_scope Driver mobile application
postman_client Public authorization_code, refresh_token web_scope API exploration during development
router_client Confidential client_credentials service_scope Router service identity
syncworker_client Confidential client_credentials service_scope SyncWorker service identity
security_client Confidential client_credentials service_scope Security service identity (audit forwarding)
geofence_client Confidential client_credentials service_scope Geofencing service identity (alerts, job runs)
trip_client Confidential client_credentials service_scope TripManagement service identity

All four scopes resolve to the same resource — the audience trackhub_api, which every backend service validates.

Registering a confidential client is only half the work: a service identity also needs rows in security.service_client_permissions. See Security and Identity.

Key Endpoints

Endpoint Purpose
/Identity/authorize Start the authorization flow
/Identity/token Exchange a code for tokens, refresh tokens, or perform a client-credentials grant
/Identity/revoke Revoke a token
/Identity/logout End the session
/Identity/.well-known/openid-configuration OIDC discovery metadata

Default lifetimes are OpenIddict's: access token 1 hour, refresh token 14 days, authorization code 5 minutes.


API surfaces

Style Where Why
GraphQL Manager, Security, Router, Telemetry, Geofencing, TripManagement The default for both portal and service-to-service traffic
REST (Minimal APIs) Reporting (all endpoints); Manager (documents, announcements); Router; TripManagement (public tracking) File upload/download, and anonymous endpoints that must bypass the mediator pipeline
REST (MVC) AuthorityServer The login UI and OAuth endpoints

GraphQL hardening — max execution depth 15, exception details only in Development — is applied by AddTrackHubGraphQLServer and therefore covers every GraphQL service uniformly.


Frontend

The React portal's internals — data-access layering, code generation against the producers' SDLs, i18n, contextual help and the public status page — are documented on the Frontend page.

Security hardening applied in the browser:

  • Token refresh mutex — a single in-flight refresh, so concurrent 401s cannot race
  • GraphQL injection prevention — values travel as GraphQL variables only; there is no string interpolation of user input into documents
  • Schema-checked operations — every document is validated by codegen against the producers' exported SDLs, so backend drift is a compile error
  • Error boundary and a toast notification system (no raw alert())
  • Security meta headersX-Content-Type-Options, X-Frame-Options
  • Request timeouts — 30 s for API calls, 60 s for file transfers
  • Help content rendering — Markdown is rendered with raw HTML disabled at both the authoring layer (build validation) and the rendering layer (skipHtml)
  • Map popup escaping — Leaflet and Google InfoWindow assign their content via innerHTML, so every interpolated value passes through escapeHtml

Backend

The four-layer structure, the CQRS mediator and its behavior pipeline, the composition root and the middleware order are documented on the Architecture page. Service-to-service calls, resilience policies and identity propagation are on Inter-Service Communication.


References

Clone this wiki locally