# You're building a new app with no login yet

> Your app has no login of its own. People register or log in with their STX account on a page STX hosts, and your app gets back who they are and their tokens.

Source: https://docs.stxapp.io/oauth/sign-in-with-your-stx-account/

> **Availability.** Available on US demo now. Live access is coming soon in the US and Ontario. See [Environments](https://docs.stxapp.io/environments/).

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.

How people register or log in to your app with an STX account:

1. **Your app has no login**: One button: continue with your STX account
2. **They register or log in**: On a page STX hosts, then back to your app (Email, Google, X or Apple)
3. **Your app knows who they are**: Their STX id, from a signed ID token
4. **Your app holds tokens**: And trades for them

## What STX gives you

- **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](/oauth/#start-from-where-you-are). 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](/oauth/sign-in-with-your-stx-account/login-vendor/) instead.

## What the member sees

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](/oauth/members-being-verified/).

## Step by step

## How it works underneath

It is the OAuth 2.0 [authorization code flow](/oauth/authorization-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](/oauth/authorization-flow/) applies. Sign-in adds four things. `$STX` in the examples is the base URL of the exchange you are calling. [Environments](/environments/) lists them.

### 1. Ask for `openid` and send a `nonce`

```
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

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.

```json
{
  "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>`.

```json
{ "alg": "RS256", "kid": "if5GofKAtl2gWmp-99w5d0J3qLaEGcHkcBA4qpzg7NU", "typ": "JWT" }
```

```json
{
  "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.

### 3. Verify it before you trust it

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.

### 4. Discover the endpoints

`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](/oauth/discovery-and-errors/#discovery).

## UserInfo

`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"}`.

```bash
curl -s "$STX/oauth/userinfo" -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json
{ "sub": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "email": "member@example.com", "email_verified": true }
```

## What you need from STX

A registered app of type **Web app**, with your callback URL and the scopes you will request, `openid` among them. See [Get demo access](/apps/overview/#get-demo-access).

## When sign-in does not complete

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](/oauth/members-being-verified/). |

## With the TypeScript SDK

`createConnect` builds the request, exchanges the code and verifies the ID token. See [With the TypeScript SDK](/apps/typescript-sdk/#sign-in-with-your-stx-account).

## Live example

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](https://playbook.sportsxapp.com). It runs against the US demo exchange, so no real money is involved.
