Autenticação Service Provider
API Key:
Na comunicação do BR-UTM (imagem abaixo), as requisições devem ser autenticadas e autorizadas. Devido à natureza distribuída da arquitetura, não é viável que cada provedor possua sua lógica de autenticação e autorização.
Portando, a solução proposta pela ASTM é a de um servidor de autenticação central onde os provedores obtém tokens OAuth2 codificados e assinados em JWT. O Validator da documentação abaixo checa a validade da assinatura do token, utilizando a chave pública do Auth Server.
Um exemplo de troca de mensagens autenticadas é:
Uso da API Key
Com sua API Key, você pode realizar ações programaticamente no ECO-UTM:
- Insira a sua API Key no header da requisição;
- Insira o scope nos parametros
- Insira o intended audience
Validator
Implementação
Código de exemplo para início da implementação do Auth Server e do Validator em Go
Código exemplo
package main
import (
"encoding/json"
"flag"
"log"
"net/http"
"os"
"strings"
"github.com/golang-jwt/jwt"
)
var (
keyFile = flag.String("private_key_file", "auth.key", "OAuth private key file")
publicKeyFile = flag.String("public_key_file", "auth.pem", "OAuth public key file")
)
func verifyToken(token string) (bool, error) {
bytes, err := os.ReadFile(*publicKeyFile)
if err != nil {
log.Panic(err)
}
publicKey, err := jwt.ParseRSAPublicKeyFromPEM(bytes)
if err != nil {
log.Panic(err)
}
parts := strings.Split(token, ".")
err = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], "."), parts[2], publicKey)
if err != nil {
return false, nil
}
return true, nil
}
func main() {
http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) {
tokenString := r.URL.Query().Get("token")
valid, err := verifyToken(tokenString)
if err != nil {
log.Panic(err)
}
log.Println(valid)
})
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"aud": "aud",
"scope": "scope",
"iss": "iss",
"exp": "exp",
"sub": "sub",
})
// Read private key
bytes, err := os.ReadFile(*keyFile)
if err != nil {
log.Panic(err)
}
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(bytes)
if err != nil {
log.Panic(err)
}
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(privateKey)
if err != nil {
log.Panic(err)
}
resp := make(map[string]string)
resp["access_token"] = tokenString
jsonResp, err := json.Marshal(resp)
if err != nil {
log.Fatalf("Error happened in JSON marshal. Err: %s", err)
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
w.Write(jsonResp)
return
})
log.Fatal(http.ListenAndServe(":9096", nil))
}
Com o código acima rodando na porta :9096, é necessário criar um Serviço e uma Route no Kong para torná-lo acessível:
Lista de endpoints
A lista completa de endpoints também está disponível neste arquivo OpenAPI e nesta coleção no Insomina.
ECO-UTM
A URL base para os seguintes endpoints é http://montreal.icea.decea.mil.br:64235/
PUT |
/providers/user_token/{token}/assign |
Associar usuário e provedor
Headers
apikey |
API Key disponível no BR-UTM Forms |
Path Param
token
|
SARPAS ID do usuário |
Body
{
"data": {
"provider_secret": "your-provider-api-key",
"provider_id": "kong-consumer-custom-id"
}
}
Campos notáveis
provider_secret |
API Key disponível no BR-UTM Forms
|
provider_id |
[OPCIONAL] Custom ID do Kong associado ao Consumer
|
Response
200
|
Success
|
404
|
User not found |
Gerar token de autenticação
Body
{
"data": {
"provider_secret": "",
"provider_id": "",
"user_id": ""
}
}
Campos notáveis
provider_secret |
API Key disponível no BR-UTM Forms
|
provider_id |
[OPCIONAL] Custom ID do Kong associado ao Consumer
|
user_id
|
SARPAS ID do usuário
|
Response
204
|
Success |
404
|
User not found
|
412
|
Invalid provider and user relationship
|
500
|
Internal server error
|
Aprovar token de autenticação do provedor associado ao usuário
Path Param
user-id |
SARPAR ID do usuário
|
Bearer
token |
Bearer token gerado
|
Response
Cria uma solicitação de voo para um espaço aéreo descrito na requisição
Bearer
token |
Bearer token gerado
|
Body
{
"data": {
"type": "/pilots/flights",
"attributes": {
"airplane_id": "1ed0e6d9-e88e-6812-bd5f-0242ac12001c",
"operation": {
"aircrafts": [
{
"id": 4,
"uuid": "1ed0e6d9-e88e-6812-bd5f-0242ac12001c"
}
],
"flight": {
"pilots": ["QGRK"],
"date": {
"start_day": "2025-12-16",
"start_hour": "08:00",
"finish_day": "2025-12-16",
"finish_hour": "09:00"
},
"type": "VLOS",
"observations": "teste",
"communication": {
"id": "1",
"ats_call_type": "vhf-fm",
"rpa_call_type": "vhf-am",
"rps": [
{
"name": "1",
"lat": "1",
"lng": "1",
"contact_info": "1",
"radius": 100
}
]
},
"sarpas_code": "OJMN",
"area": {
"asa_id": "283797ce-73e9-4395-adcf-6c6119b3f20f",
"takeoff_point": [-34.993236064910896,-8.02209168418088],
"landing_point": [-34.993236064910896,-8.02209168418088],
"required_route": {
"geojson": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-24.080093705511494,
-49.0539316478104
],
[
-24.081153158442177,
-49.05006533240442
],
[
-24.08503406183282,
-49.052976909391695
],
[
-24.082881359600847,
-49.055986687210066
],
[
-24.080093705511494,
-49.0539316478104
]
]
]
}
}
},
"flight_type": "height",
"flight_distance": 100
},
"documents": []
},
"basic_information": {
"name": "teste 2",
"type": "101",
"agree_terms": 1
}
}
}
}
}
Campos notáveis
provider_secret |
API Key disponível no BR-UTM Forms
|
provider_id |
[OPCIONAL] Custom ID do Kong associado ao Consumer
|
user_id
|
SARPAR ID do usuário
|
Response
GET |
/bypass/categorize/{solicitationProtocol} |
Categoriza uma solicitação de voo por número de protocolo
Bearer
token |
Bearer token gerado |
Path Param
solicitationProtocol |
Número de protocolo da solicitação |
Response
200
|
Success |
404
|
Solicitation not found
|
500
|
Error
|
GET |
/bypass/analyze/{solicitationProtocol} |
Analisa uma solicitação de voo por número de protocolo
Bearer
token |
Bearer token gerado |
Path Param
solicitationProtocol |
Número de protocolo da solicitação |
Response
200
|
Success |
404
|
Solicitation protocol not found
|
500
|
Error
|
Busca todas as aeronaves de um usuário identificado pelo token de sessão
Bearer
token |
Bearer token gerado |
Response
GET
|
/shared-aircraft-user
|
Busca todas as aeronaves compartilhadas com um usuário identificado pelo token de sessão
Bearer
token |
Bearer token gerado |
Response
POST |
/user/flight/aircrafts |
Busca todas as aeronaves em voo, opcionalmente incluindo compartilhadas, de um usuário identificado pelo ID enviado na requisição
Bearer
token |
Bearer token gerado |
Body
{
"user_information_id": "1ed4b29e-8fe0-60aa-aefd-0242ac120019",
"shared": true
}
Campos notáveis
user_information_id |
SARPAS ID do usuário
|
shared
|
[OPCIONAL] Incluir aeronaves compartilhadas
|
Response
Lista os status do voo de um usuário identificado pelo token de sessão
Bearer
token |
Bearer token gerado
|
Response
200
ASA
A URL base para os seguintes endpoints é http://kong.icea.decea.mil.br:64236/api/
POST |
/polygon/{polygonId}/{userUuid} |
Cria uma área no ASA dados os identificadores do polígono e usuário
Bearer
token |
Bearer token gerado
|
Path Param
polygonId |
ID (definido pelo solicitante) do polígono criado
|
userUuid
|
SARPAS ID do usuário
|
Body
{
"type": "Feature",
"properties": {
"poligonoId": "283797ce-73e9-4395-adcf-6c6119b3f20f",
"perfil": "1",
"datasHoras": "{\"date1\":\"2023-11-30\",\"date2\":\"2023-11-30\",\"hora1\":\"14:00\",\"hora2\":\"15:00\"}",
"elevacoes": {
"criarAreaMaximo": 10,
"criarAreaMinimo": 0,
"criarAreaMaximoFt": 32.8084,
"criarAreaMinimoFt": 0,
"criarAreaNome": "",
"criarAreaDescricao": "",
"alturaEditavelMetros": "10",
"alturaEditavelPes": 32.8084,
"dataInicio": "2025-12-12",
"dataTermino": "2025-12-12",
"horaInicio": "08:00",
"horaTermino": "09:00",
"update": []
},
"raio": 0,
"altura": "10",
"formato": "poligono",
"contextoId": 1,
"pontoDecolagem": {
"latLng": {
"lat": -24.098672973607677,
"lng": -48.909985432572751,
"valido": true
},
"unidade": "gms",
"elevationM": 10,
"elevationFt": 32.8084,
"valido": true
}
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-24.080093705511494,
-49.0539316478104
],
[
-24.081153158442177,
-49.05006533240442
],
[
-24.08503406183282,
-49.052976909391695
],
[
-24.082881359600847,
-49.055986687210066
],
[
-24.080093705511494,
-49.0539316478104
]
]
]
}
}
Campos notáveis
Response
{
voo_id: string
}
GET |
/polygon/{solicitationProtocol}/result |
Obter resultado da solicitação de área
Bearer
token |
Bearer token gerado
|
Path Param
solicitationProtocol |
Número de protocolo da solicitação de área |
Response
200 OK