> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aveid.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Common mistakes and edge cases

> Frequent integration bugs, protocol mismatches, and how to fix them.

<AccordionGroup>
  <Accordion title="Wrong API host" icon="triangle-exclamation">
    **Symptom:** Token requests fail or return an unexpected error page.

    **Cause:** Sending API requests to `aveid.net` instead of `api.aveid.net`.

    **Fix:**

    ```
    ✅ POST https://api.aveid.net/api/oauth/token
    ❌ POST https://aveid.net/api/oauth/token
    ```
  </Accordion>

  <Accordion title="Mixed field casing on the token request" icon="circle-info">
    **Symptom:** Token endpoint returns `400 invalid_request` even though you're sending the right values.

    **Cause:** Ave accepts both OAuth-standard `snake_case` fields and legacy `camelCase` fields. Problems usually come from mixing names incorrectly or misspelling them.

    **Fix:** Prefer the standard OAuth field names in new integrations:

    ```ts theme={null}
    {
      "grant_type": "authorization_code",
      "client_id": "YOUR_CLIENT_ID",
      "redirect_uri": "...",
      "code_verifier": "..."
    }
    ```

    Legacy `grantType`, `clientId`, `redirectUri`, and `codeVerifier` still work for backward compatibility.
  </Accordion>

  <Accordion title="PKCE verifier mismatch" icon="triangle-exclamation">
    **Symptom:** Token exchange returns `invalid_grant`.

    **Causes:**

    * Base64url encoding bug in the verifier
    * Stored verifier was overwritten before exchange (e.g. user opened a second tab)
    * Multiple concurrent login flows racing

    **Fix:** Generate one verifier per login intent, store it in `sessionStorage`, and clear it immediately after a single use.
  </Accordion>

  <Accordion title="Missing id_token in the response" icon="circle-info">
    **Symptom:** Token response has `access_token` but no `id_token`.

    **Cause:** The `openid` scope was not requested or was not granted for your app.

    **Fix:** Add `openid` to your requested scopes:

    ```
    scope=openid profile email
    ```
  </Accordion>

  <Accordion title="Refresh token suddenly returns invalid_grant" icon="triangle-exclamation">
    **Symptom:** A refresh request that previously worked now returns `invalid_grant`.

    **Possible causes:**

    * Token expired (refresh tokens have a maximum lifetime)
    * Token was revoked by the user or by the server
    * Reuse detection triggered — you sent the same refresh token twice (rotation means the old one is immediately invalidated after first use)

    **Fix:** Treat `invalid_grant` on a refresh as a terminal session loss for that session and re-authenticate the user. Store the **new** refresh token returned by every successful refresh immediately. Ave only invalidates the rotated token lineage that was reused, so unrelated sessions on other devices should stay signed in.
  </Accordion>

  <Accordion title="Connector token exchange denied" icon="triangle-exclamation">
    **Symptom:** Token exchange with `grantType: urn:ietf:params:oauth:grant-type:token-exchange` returns `access_denied` or `invalid_target`.

    **Possible causes:**

    * No active connector grant for this user
    * The target resource is disabled
    * Requested scope exceeds what the grant allows
    * `subjectToken` was issued to a different client than expected

    **Fix:** Re-run the connector authorization flow (`https://aveid.net/connect`) with the correct `resource` and `scope`, making sure the source app's token is used as `subjectToken`.
  </Accordion>

  <Accordion title="E2EE app key fails to decode" icon="circle-info">
    **Symptom:** `atob()` throws, or the decrypted key is garbage.

    **Cause:** The app key is delivered via URL fragment (`#app_key=...`). Browsers silently replace `+` with a space in fragment parameters, breaking base64 decoding.

    **Fix:**

    ```ts theme={null}
    const fixed = rawKey.replace(/ /g, "+");
    const bytes = atob(fixed);
    ```

    Always normalize base64 strings from URL fragments before decoding.
  </Accordion>

  <Accordion title="User profile data is stale" icon="circle-info">
    **Symptom:** Display name, avatar, or email shown in your app is outdated.

    **Cause:** Profile data cached from the initial token exchange is never refreshed.

    **Fix:** Re-fetch the profile from `/api/oauth/userinfo` on each login, or when you need fresh data.
  </Accordion>

  <Accordion title="Data showing up under the wrong user" icon="triangle-exclamation">
    **Symptom:** One user can see another user's data, or data is missing after a user changes identity.

    **Cause:** App is storing data keyed by `handle` (username) instead of `identityId`. Handles can change; `identityId` is stable.

    **Fix:** Always key user data by the `sub` claim from the JWT (which is the `identityId`). Never rely on `handle` as a stable identifier.
  </Accordion>
</AccordionGroup>
