Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
SimTX Wiki
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
SimTX API/Device login
Page
Discussion
English
Read
Edit
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
Edit source
View history
General
What links here
Related changes
Special pages
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
{{SimTX API navigation}} '''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 [[SimTX API/Account service#Credential formats|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 [[SimTX API/Account service#Authentication|Authentication]]). == Sequence == <syntaxhighlight lang="text"> 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 | | |<----------------------------------| | </syntaxhighlight> == Starting a login == <code>POST /api/v1/device/authorizations</code>. No authentication. === Request === <syntaxhighlight lang="json"> { "clientName": "Example SDR Client", "scope": "game.transmit station.manage" } </syntaxhighlight> {| class="wikitable" ! Field !! Type !! Required !! Description |- | <code>clientName</code> || string || No || Name shown to the user on the approval page and in their device list. Truncated to 80 characters. Defaults to <code>SimTX client</code> when blank. |- | <code>scope</code> || string || No || Space-separated list of [[SimTX API/Permissions|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 <code>400 scope_invalid</code>. |} Request the smallest scope that does the job. A transmitter needs <code>game.transmit</code>. A client that creates or edits stations, launches satellites or deorbits them also needs <code>station.manage</code>. A tool that publishes extensions needs <code>catalog.publish</code>. Reading the user's stations and receiving through them need no particular scope. === Response === <code>201 Created</code>: <syntaxhighlight lang="json"> { "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" } </syntaxhighlight> {| class="wikitable" ! Field !! Type !! Description |- | <code>deviceCode</code> || string || Secret used to poll for the result. Keep it private; do not show it to the user. |- | <code>userCode</code> || string || Code the user confirms in the browser. |- | <code>verificationUri</code> || string || Page where the user types the code. |- | <code>verificationUriComplete</code> || string || Same page with the code pre-filled. Open this one if the client can launch a browser. |- | <code>expiresIn</code> || integer || Seconds until the device code expires (600). |- | <code>interval</code> || integer || Minimum seconds between polls (5). |- | <code>expiresAt</code> || string || Absolute expiry time. |} Display <code>userCode</code> prominently and open <code>verificationUriComplete</code>. == Polling for credentials == <code>POST /api/v1/device/token</code>. No authentication. <syntaxhighlight lang="json"> { "deviceCode": "stx_dc1_9a1f..." } </syntaxhighlight> === Success === <code>200 OK</code> with a credential pair: <syntaxhighlight lang="json"> { "accessCredential": "stx_at1_5c0e...", "refreshCredential": "stx_rt1_b77d...", "credentialType": "Bearer", "expiresIn": 600, "scope": "game.transmit station.manage" } </syntaxhighlight> {| class="wikitable" ! Field !! Type !! Description |- | <code>accessCredential</code> || string || Bearer credential, valid for <code>expiresIn</code> seconds. |- | <code>refreshCredential</code> || string || Used once to obtain the next pair. |- | <code>credentialType</code> || string || Always <code>Bearer</code>. |- | <code>expiresIn</code> || integer || Access credential lifetime in seconds (600). |- | <code>scope</code> || string || Space-separated scopes actually granted. |} A device code can be exchanged exactly once. Polling again after success returns <code>access_denied</code>. === Not ready yet === Until the user acts, the service answers <code>400</code> with one of the following codes. Every one of these responses includes the interval the client must now use, as a string, in <code>fieldErrors.interval</code>. {| class="wikitable" ! <code>code</code> !! Meaning !! Client action |- | <code>authorization_pending</code> || The user has not approved or denied yet. || Wait <code>interval</code> seconds and poll again. |- | <code>slow_down</code> || The poll arrived sooner than <code>interval</code> seconds after the previous one. The interval has been increased by 5 seconds. || Adopt <code>fieldErrors.interval</code> for all later polls. |- | <code>expired_token</code> || Ten minutes have passed. || Start a new login. |- | <code>access_denied</code> || The user denied the request, or the code was already exchanged. || Stop. |- | <code>invalid_grant</code> || The device code is unknown. || Start a new login. |} A client that ignores <code>slow_down</code> 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 == <code>POST /api/v1/device/refresh</code>. No authentication. <syntaxhighlight lang="json"> { "refreshCredential": "stx_rt1_b77d..." } </syntaxhighlight> On success the response is <code>200 OK</code> 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. {| class="wikitable" ! HTTP !! <code>code</code> !! Meaning |- | 401 || <code>invalid_grant</code> || The refresh credential is unknown, expired, or its device session was revoked. Sign in again. |- | 401 || <code>refresh_reuse_detected</code> || 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 == # On start-up, load the stored refresh credential. If there is none, run device login. # Refresh to obtain an access credential. Keep the access credential in memory. # Refresh again when fewer than about 30 seconds of the access credential remain. # When any endpoint answers <code>401 credential_invalid</code>, refresh once and repeat the request. If the refresh fails, delete the stored credential and run device login. # Serialise refreshes. Two threads refreshing with the same refresh credential at once will trigger <code>refresh_reuse_detected</code>, and the user will have to sign in again. == Example == A complete login with <code>curl</code> and <code>jq</code>: <syntaxhighlight lang="bash"> 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 </syntaxhighlight> == See also == * [[SimTX API/Account service]]: errors, credential formats and rate limits. * [[SimTX API/Station endpoints]]: the user's stations. * [[SimTX API/Game tickets]]: the next step for a client that wants to transmit. {{SimTX API navbox}} [[Category:SimTX API]] [[Category:SimTX API account service]]
Summary:
Please note that all contributions to SimTX Wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
SimTX Wiki:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Templates used on this page:
Template:SimTX API navbox
(
edit
)
Template:SimTX API navigation
(
edit
)