Chapter 4 — Architecture and Authentication
This chapter describes the authentication and authorization model used across the entire BR-UTM ecosystem, including how your USS obtains tokens, uses them to call DECEA's APIs, validates tokens received from other USSs, and authenticates peer-to-peer calls.
4.1 Authentication Architecture Overview
BR-UTM uses OAuth2 Client Credentials flow with JWT (JSON Web Token) access tokens, signed using RS256 (RSA SHA-256). This is a stateless, distributed authentication model where:
- Your USS presents its API Key to DECEA's Auth Server to obtain a signed JWT.
- The JWT is included as a Bearer token in every API request (to the DSS, to DECEA's USS, or to other USSs).
- The receiving server validates the JWT locally using DECEA's public key — it does not need to call the Auth Server again.
This architecture is critical for the distributed, peer-to-peer nature of the system: there is no central session state, and any server can independently validate any token.
┌──────────────────┐ 1. POST /token?apikey=...&scope=...&intended_audience=...
│ Your USS │──────────────────────────────────────────────────────────────►│ Auth Server │
│ │◄──────────────────────────────── 2. JWT (signed RS256) ───────│ │
│ │
│ │ 3. Authorization: Bearer <JWT>
│ │──────────────────────────────────────────────────────────────►│ DSS / USS │
│ │◄──────────────────────────────── 4. Response ────────────────│ │
└──────────────────┘
4.2 Obtaining a JWT Token
To obtain a JWT, send an HTTP GET or POST request to the Auth Server token endpoint:
Endpoint: http://api.sandbox.brutm.dcta.mil.br/token
Query parameters (or request body):
| Parameter | Description | Example |
|---|---|---|
apikey |
Your company's API Key, obtained from the Portal UTM. Can also be sent as an HTTP header. | abc123... |
scope |
The OAuth2 scope(s) you need for the operation. Space-separated for multiple scopes. | utm.strategic_coordination utm.constraint_processing |
intended_audience |
The FQDN (domain name only, no path) of the server this token will be sent to. | api.sandbox.brutm.dcta.mil.br or uss-b.yourpartner.com |
Example request:
GET /token?scope=utm.strategic_coordination&intended_audience=api.sandbox.brutm.dcta.mil.br
Authorization: ApiKey abc123yourapikey
Example response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
4.3 JWT Token Structure
Every token issued by DECEA's Auth Server is a signed JWT containing the following claims:
| Claim | Description | Example |
|---|---|---|
iss |
The URL of the Auth Server that issued the token. | http://api.sandbox.brutm.dcta.mil.br/token |
exp |
Token expiration timestamp (Unix epoch). Maximum 1 hour from issuance. | 1751327400 |
sub |
Your USS's unique identifier — the manager identifier in DSS records. |
"my-company-uss" |
scope |
Granted scopes, space-separated. | "utm.strategic_coordination utm.constraint_processing" |
jti |
Unique token ID for replay protection (RFC 7519). | "d3e8f921-..." |
aud |
The FQDN of the intended recipient server. | "api.sandbox.brutm.dcta.mil.br" |
Token Validity
- Tokens are valid for up to 1 hour (inspect the
expclaim for the exact expiry time). - Cache and reuse tokens until they are near expiry. Requesting a new token on every API call is wasteful and may cause rate limiting.
- A single token can carry multiple scopes (space-separated in the
scopeclaim), so you can request all the scopes you need for a workflow in one token request.
4.4 Available Scopes
UTM API (utm.yaml)
| Scope | Purpose |
|---|---|
utm.strategic_coordination |
Create, update, delete, and query OIRs in the DSS. Notify subscriber USSs. Fetch OIR details from other USSs. Required for all standard flight operations. |
utm.constraint_processing |
Query constraint references in the DSS and fetch constraint details from DECEA's USS. Required if your area may have constraints. |
utm.conformance_monitoring_sa |
Query OIRs and fetch telemetry for off-nominal situations. Used by DECEA's monitoring systems. |
utm.constraint_management |
Create, update, and delete constraints. DECEA only. |
utm.availability_arbitration |
Set USS availability state in the DSS. Not required in Phase 1. |
utm.aviation_authority |
Access flight authorization details. DECEA only. |
Remote ID API (remoteid.yaml)
| Scope | Purpose |
|---|---|
rid.service_provider |
Create, update, and delete ISAs in the Remote ID DSS. Required when activating/deactivating flights. |
rid.display_provider |
Query ISAs and telemetry from other USSs. Required for situational awareness. |
Automated Testing APIs
| Scope | Purpose |
|---|---|
interuss.flight_planning.direct_automated_test |
Used by DECEA's test framework when calling your flights.yaml endpoints during homologation. |
interuss.flight_planning.plan |
Used by DECEA's test framework to simulate user flight plan actions. |
rid.inject_test_data |
Used by DECEA's test framework when calling your injection.yaml endpoints during homologation. |
interuss.versioning.read_system_versions |
Used by DECEA's test framework when calling your versioning.yaml endpoint. |
Day-to-Day Scope Requirements
For normal production operations, your USS will primarily need:
utm.strategic_coordination
utm.constraint_processing
rid.service_provider
rid.display_provider
4.5 The intended_audience Parameter — Peer-to-Peer Calls
The intended_audience parameter (which becomes the aud claim in the JWT) is critical for peer-to-peer security. It binds a token to a specific recipient, preventing token replay attacks.
Rules:
- When calling DECEA's DSS or Auth Server: use the domain of the DSS (e.g.,
api.sandbox.brutm.dcta.mil.br). - When calling another USS (e.g., to fetch OIR details): use only the domain of that USS's
uss_base_url— no path, no port (unless non-standard).
Example:
If USS B's uss_base_url in the DSS is https://uss-b.partnercompany.com/api/utm, then when USS A wants to call USS B:
intended_audience = "uss-b.partnercompany.com"
USS A requests a fresh token with this audience and includes it in the Authorization header when calling GET https://uss-b.partnercompany.com/api/utm/uss/v1/operational_intents/{id}.
USS B, upon receiving this request, validates that the token's aud claim matches its own domain (uss-b.partnercompany.com). If it doesn't match, it rejects the request with HTTP 401.
4.6 Using the Token
Include the token in every outgoing HTTP request:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
This applies to calls to:
- DECEA's DSS (
/dss/v1/...) - DECEA's Remote ID DSS (
/dss/identification_service_areas/...) - Any peer USS's endpoints (
/uss/v1/...)
4.7 Validating Incoming Tokens (Inbound Requests)
Your USS must validate every inbound request — whether from DECEA's systems or from other USSs. Failing to do so is a security vulnerability and a homologation failure.
The validation steps are:
Step 1 — Extract the Token
Extract the JWT from the Authorization header:
Authorization: Bearer <token>
Step 2 — Verify the RS256 Signature
Verify the token's digital signature using DECEA's public RSA key (obtained from the Portal UTM during onboarding). The signing algorithm is RS256.
If the signature is invalid → reject with HTTP 401.
Step 3 — Verify Token Expiry
Check that the exp claim is greater than the current UTC timestamp.
If the token is expired → reject with HTTP 401.
Step 4 — Verify the Audience
Check that the aud claim matches your own server's FQDN. This prevents a token intended for another USS from being replayed against your server.
If aud doesn't match your domain → reject with HTTP 401.
Step 5 — Verify the Scope
Check that the scope claim contains the scope required by the specific endpoint being called. Different endpoints require different scopes (see the OpenAPI specifications for each endpoint's required scope).
If the scope is insufficient → reject with HTTP 403.
Go implementation reference (from the BR-UTM workshop):
func verifyToken(token string, publicKeyFile string) (bool, error) {
bytes, _ := os.ReadFile(publicKeyFile)
publicKey, _ := jwt.ParseRSAPublicKeyFromPEM(bytes)
parts := strings.Split(token, ".")
err := jwt.SigningMethodRS256.Verify(
strings.Join(parts[0:2], "."), parts[2], publicKey,
)
return err == nil, err
}
4.8 Token Caching Strategy
For optimal performance:
- Cache the token after obtaining it.
-
Reuse it for all requests to the same audience with the same scopes, until it's within a safety margin of expiry (e.g., 60 seconds before
exp). - Request a new token when the cached one is near expiry or when you need a different scope/audience combination.
You may maintain multiple cached tokens simultaneously — one per (scope, audience) combination that your USS needs.
4.9 Architecture Diagram — Full Authentication Flow
┌─────────────────────────────────────────────┐
│ DECEA Infrastructure │
┌──────────────┐ │ ┌──────────────┐ ┌────────────────────┐ │
│ │──────► │ │ Auth Server │ │ DSS │ │
│ Your USS │ token │ │ /token │ │ /dss/v1/... │ │
│ │◄───── │ └──────────────┘ └────────────────────┘ │
│ │ │ │
│ │─────────────────── Bearer JWT ──────────────────────►│
│ │◄──────────────────── Response ────────────────────── │
└──────┬───────┘ └─────────────────────────────────────────────┘
│
│ Peer-to-Peer (with audience-specific token)
│
▼
┌──────────────┐
│ Partner USS │ validates JWT: sig + exp + aud + scope
│ /uss/v1/... │
└──────────────┘
4.10 Security Principles
- Principle of least privilege: Request only the scopes your operation actually needs. Avoid requesting all scopes in every token.
-
No token sharing: Tokens are bound to a specific audience (
aud). A token obtained to talk to the DSS cannot be used to call another USS, and vice versa. -
Time synchronization: Your system clock must be synchronized with DECEA's NTP server (
ntp.decea.gov.br) to ensure token expiry calculations are accurate. See Chapter 7 — Non-Functional Requirements.
No Comments