Jump to content

SimTX API/Device login

From SimTX Wiki
Revision as of 17:16, 24 October 2026 by Clanker (talk | contribs) (Remove development history and server internals)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Device login is the sign-in flow for software that cannot, or should not, handle a SimTX password itself: desktop applications, command-line tools and headless bridges. The software shows the user a short code, the user approves it in a browser while signed in to SimTX, and the software receives an access and refresh credential.

The flow is modelled on the OAuth 2.0 device authorization grant (RFC 8628), with two differences: requests and responses are JSON rather than form-encoded, and the endpoint and field names are SimTX's own. Generic OAuth libraries will therefore not work without adaptation.

Software that only receives at an ad-hoc site does not need to sign in at all. Bots that run unattended should normally use a personal credential instead (see Authentication).

Sequence[edit | edit source]

 Client                          auth.simtx.net                    User's browser
   |  POST /device/authorizations      |                                  |
   |---------------------------------->|                                  |
   |  201 deviceCode, userCode, URI    |                                  |
   |<----------------------------------|                                  |
   |  show userCode, open URI ------------------------------------------->|
   |                                   |   user signs in and approves     |
   |  POST /device/token  (every 5 s)  |<---------------------------------|
   |---------------------------------->|                                  |
   |  400 authorization_pending        |                                  |
   |<----------------------------------|                                  |
   |  POST /device/token               |                                  |
   |---------------------------------->|                                  |
   |  200 access + refresh credential  |                                  |
   |<----------------------------------|                                  |

Starting a login[edit | edit source]

POST /api/v1/device/authorizations. No authentication.

Request[edit | edit source]

{ "clientName": "Example SDR Client", "scope": "game.transmit station.manage" }
Field Type Required Description
clientName string No Name shown to the user on the approval page and in their device list. Truncated to 80 characters. Defaults to SimTX client when blank.
scope string No Space-separated list of permission identifiers the credential may use. An empty or missing scope requests every permission the account holds at the moment of approval. Unknown identifiers are rejected with 400 scope_invalid.

Request the smallest scope that does the job. A transmitter needs game.transmit. A client that creates or edits stations, launches satellites or deorbits them also needs station.manage. A tool that publishes extensions needs catalog.publish. Reading the user's stations and receiving through them need no particular scope.

Response[edit | edit source]

201 Created:

{
  "deviceCode": "stx_dc1_9a1f...",
  "userCode": "QZ7K-M4TP",
  "verificationUri": "https://auth.simtx.net/device",
  "verificationUriComplete": "https://auth.simtx.net/device?code=QZ7K-M4TP",
  "expiresIn": 600,
  "interval": 5,
  "expiresAt": "2026-10-24T12:10:00Z"
}
Field Type Description
deviceCode string Secret used to poll for the result. Keep it private; do not show it to the user.
userCode string Code the user confirms in the browser.
verificationUri string Page where the user types the code.
verificationUriComplete string Same page with the code pre-filled. Open this one if the client can launch a browser.
expiresIn integer Seconds until the device code expires (600).
interval integer Minimum seconds between polls (5).
expiresAt string Absolute expiry time.

Display userCode prominently and open verificationUriComplete.

Polling for credentials[edit | edit source]

POST /api/v1/device/token. No authentication.

{ "deviceCode": "stx_dc1_9a1f..." }

Success[edit | edit source]

200 OK with a credential pair:

{
  "accessCredential": "stx_at1_5c0e...",
  "refreshCredential": "stx_rt1_b77d...",
  "credentialType": "Bearer",
  "expiresIn": 600,
  "scope": "game.transmit station.manage"
}
Field Type Description
accessCredential string Bearer credential, valid for expiresIn seconds.
refreshCredential string Used once to obtain the next pair.
credentialType string Always Bearer.
expiresIn integer Access credential lifetime in seconds (600).
scope string Space-separated scopes actually granted.

A device code can be exchanged exactly once. Polling again after success returns access_denied.

Not ready yet[edit | edit source]

Until the user acts, the service answers 400 with one of the following codes. Every one of these responses includes the interval the client must now use, as a string, in fieldErrors.interval.

code Meaning Client action
authorization_pending The user has not approved or denied yet. Wait interval seconds and poll again.
slow_down The poll arrived sooner than interval seconds after the previous one. The interval has been increased by 5 seconds. Adopt fieldErrors.interval for all later polls.
expired_token Ten minutes have passed. Start a new login.
access_denied The user denied the request, or the code was already exchanged. Stop.
invalid_grant The device code is unknown. Start a new login.

A client that ignores slow_down and keeps its old pace is told to slow down on every single poll, and each warning adds another five seconds. Such a client never receives credentials. Read the interval from the response.

Refreshing[edit | edit source]

POST /api/v1/device/refresh. No authentication.

{ "refreshCredential": "stx_rt1_b77d..." }

On success the response is 200 OK with a new credential pair in the same format as above. The refresh credential rotates on every use. Store the new one immediately and discard the old one.

HTTP code Meaning
401 invalid_grant The refresh credential is unknown, expired, or its device session was revoked. Sign in again.
401 refresh_reuse_detected A refresh credential that had already been used was presented again. The service treats this as theft and revokes the whole device session. Sign in again.

Refresh credentials expire after 30 days without use and 180 days after the original sign-in, whichever comes first.

Recommended client behaviour[edit | edit source]

  1. On start-up, load the stored refresh credential. If there is none, run device login.
  2. Refresh to obtain an access credential. Keep the access credential in memory.
  3. Refresh again when fewer than about 30 seconds of the access credential remain.
  4. When any endpoint answers 401 credential_invalid, refresh once and repeat the request. If the refresh fails, delete the stored credential and run device login.
  5. Serialise refreshes. Two threads refreshing with the same refresh credential at once will trigger refresh_reuse_detected, and the user will have to sign in again.

Example[edit | edit source]

A complete login with curl and jq:

AUTH=https://auth.simtx.net

start=$(curl -s -X POST "$AUTH/api/v1/device/authorizations" \
  -H 'Content-Type: application/json' \
  -d '{"clientName":"curl demo","scope":"game.transmit"}')
device=$(jq -r .deviceCode <<<"$start")
interval=$(jq -r .interval <<<"$start")
echo "Open $(jq -r .verificationUriComplete <<<"$start") and confirm $(jq -r .userCode <<<"$start")"

while sleep "$interval"; do
  reply=$(curl -s -X POST "$AUTH/api/v1/device/token" \
    -H 'Content-Type: application/json' -d "{\"deviceCode\":\"$device\"}")
  case $(jq -r '.code // "ok"' <<<"$reply") in
    ok) jq . <<<"$reply"; break ;;
    authorization_pending) ;;
    slow_down) interval=$(jq -r .fieldErrors.interval <<<"$reply") ;;
    *) echo "login failed: $reply"; exit 1 ;;
  esac
done

See also[edit | edit source]

SimTX API reference (protocol v25)
Start OverviewQuick startFramingData typesPacket listSchemaLimits and errorsCompatibility and versioning
Account service OverviewDevice loginServer directoryStation endpointsGame ticketsPermissionsPublic endpoints
Game protocol Connection and handshakeSession statusReceiver sessionTransmitter sessionControl sessionCatalogModeration
Stations StationsSatellitesTracking and pointing
Connection ClientHelloPingServerHelloPongErrorBanner
Session AntennaPointingUpdateSessionStatus
Receiver ForwardedClientAddressReceiverStartReceiverRetuneIqStreamDescriptorIqBlock
Transmitter TransmitterStartSampleBlockTxUpdateTxModeInfoTxQueueTxAcceptTxTelemetryTxUpdateAck
Control ListStationsStartMonitorStopMonitorWeatherMapRequestServerHealthRequestCoverageMapRequestEphemerisRequestPassPredictionRequestStationListMonitorBlockMonitorEndWeatherMapResponseServerHealthReportCoverageMapResponseEphemerisResponsePassPredictionResponseMonitorAck
Catalog CatalogListRequestCatalogFetchCatalogUploadCatalogDeleteCatalogListCatalogEntryCatalogUploadResultCatalogDeleteResult
Moderation KickRequestIpModerationRequestBroadcastBannerKickResultIpModerationResultStationImpactList
Archive Protocol v24 reference