Skip to content

You're building a new app with no login yet

Use this when you do not want to run sign-up, passwords or email verification yourself. People register or log in to your app with their STX account.

  1. 1Your app has no loginOne button: continue with your STX account
  2. 2They register or log inOn a page STX hosts, then back to your appEmail, Google, X or Apple
  3. 3Your app knows who they areTheir STX id, from a signed ID token
  4. 4Your app holds tokensAnd trades for them
  • STX hosts the sign-up and login page. Your app sends people there and gets them back; it never sees a password.
  • A new person creates their STX account there, identity verification included. An existing member logs in.
  • Four ways to sign up or log in: email and password, Google, X and Apple.
  • Your app gets back who the member is, and tokens to act for them, using standard OAuth 2.0 and OpenID Connect.

Not your situation? See the OAuth overview. If your app’s login already runs on a service such as Auth0 or Clerk, see You use a login service like Auth0 or Clerk instead.

  1. In your app they choose Continue with your STX account, or whatever you call the button.
  2. On the STX page they log in, or register if they have no STX account yet, with email and password, Google, X or Apple.
  3. STX shows your app’s name and logo and the access you asked for. They choose Allow.
  4. They are in your app, logged in, with their STX account connected.

A returning member who has already approved your app skips step 3. A new person whose account STX is still verifying does not reach step 3 yet; see Members still being verified.

Registering or logging in to your app with an STX accountThree lifelines: the member's browser, your app's server, and STX. The member chooses to continue with their STX account. Your app redirects them to STX with the openid scope, a nonce and a PKCE challenge. On the STX page the member registers or logs in, with email and password, Google, X or Apple, and approves your app. STX sends them back to your app with a code. Your app exchanges the code and the PKCE verifier for tokens and an ID token, verifies the ID token's signature, issuer, audience and nonce, and logs the member in to your app, keyed on the ID token's sub. It can then call the STX API and the userinfo endpoint with the access token.Member · browserYour app · serverSTX1Chooses to continue with their STX account2Redirect to /oauth/authorize: openid, nonce, PKCERegisters or logs in on STX:email, Google, X or Apple.Then approves your app3Back to your app, with a code4Code + verifier for tokens and an ID tokenVerifies the ID token: signature,issuer, audience, nonce5Logged in to your app, keyed on sub/api/v1/… and /oauth/userinfo (Bearer)member data, within the approved scopesNo login of your own: STX hosts sign-up and login, and your app never sees a password.

It is the OAuth 2.0 authorization code flow with OpenID Connect added: ask for the openid scope and the token response also carries an ID token naming the member. Everything in Authorization code flow applies. Sign-in adds four things. $STX in the examples is the base URL of the exchange you are calling. Environments lists them.

GET $STX/oauth/authorize
?response_type=code
&client_id=<your client_id>
&redirect_uri=<one you registered, exactly>
&scope=openid%20profile.read%20balance.read
&state=<opaque, unguessable, per request>
&nonce=<opaque, unguessable, per request>
&code_challenge=<the challenge>
&code_challenge_method=S256

Keep nonce on your server with state and the PKCE verifier. Two optional parameters shorten the member’s path:

Parameter Effect
connection google, apple or x sends a member who is not signed in straight to that sign-in, skipping the STX login page. Where that sign-in is not enabled, the STX login page is shown as usual.
login_hint The member’s email, if you know it. With connection=google it preselects that Google account.

2. Read the ID token from the token response

Section titled “2. Read the ID token from the token response”

The code exchange is unchanged. Because the grant includes openid, the response has one more field, id_token: a JWT signed with RS256, valid for 10 minutes.

{
"access_token": "stxapp_at_…",
"refresh_token": "stxapp_rt_…",
"id_token": "eyJhbG…",
"token_type": "bearer",
"expires_in": 3600,
"scope": "openid profile.read balance.read"
}

Decoded, the ID token’s header and claims look like this. aud is your client_id, nonce is the one you sent, and iss is the STX host you called, shown here as <exchange>.

{ "alg": "RS256", "kid": "if5GofKAtl2gWmp-99w5d0J3qLaEGcHkcBA4qpzg7NU", "typ": "JWT" }
{
"iss": "https://<exchange>",
"aud": "stxapp_client_2s1p…",
"sub": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"email": "member@example.com",
"email_verified": true,
"iat": 1791660409,
"exp": 1791661009,
"nonce": "QPHuFZ…"
}
Claim Value
iss The STX host you called.
sub The member’s STX id. Stable for the life of the account; key your user record on it.
aud Your client_id.
iat, exp Issued and expiry times, in seconds.
nonce The nonce you sent.
email The member’s email.
email_verified Whether STX has verified that email.

A refresh returns new access and refresh tokens and no ID token. You need the ID token once, at sign-in.

Check all of these, or let an OpenID Connect library do it:

  • the header’s alg is RS256; accept no other algorithm, whatever the token says;
  • the signature, against the key at jwks_uri whose kid matches the header’s;
  • iss is the STX host you are configured for;
  • aud is your client_id;
  • nonce is the one you stored for this sign-in;
  • exp has not passed.

Then find or create your user by sub, start your own session, and store the STX tokens on your server against that user.

GET /.well-known/openid-configuration lists the endpoints, the signing keys and the claims. Point your library at the STX host and it reads the rest. See Discovery.

GET /oauth/userinfo returns the same sub, email and email_verified for a member access token that carries openid. Any other token gets 401 with {"error":"invalid_token"}.

curl -s "$STX/oauth/userinfo" -H "Authorization: Bearer $ACCESS_TOKEN"
{ "sub": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "email": "member@example.com", "email_verified": true }

A registered app of type Web app, with your callback URL and the scopes you will request, openid among them. See Get demo access.

The member is returned to your callback with error instead of code. Leave them signed out and let them try again.

error Meaning
access_denied The member chose Deny, or cannot use STX right now.
account_pending The member has not finished STX verification. Show a “finish verifying” message. See Members still being verified.

createConnect builds the request, exchanges the code and verifies the ID token. See With the TypeScript SDK.

Playbook is a demo app built this way: it has no login of its own, and one button takes you to STX to register or log in. Try it at playbook.sportsxapp.com. It runs against the US demo exchange, so no real money is involved.