> ## 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.

# Security best practices

> Hardening checklist for OAuth, OIDC token validation, Connector delegation, and signing workflows.

<AccordionGroup>
  <Accordion title="OAuth request hardening" icon="shield-halved" defaultOpen>
    <Check>Always generate and verify `state` to prevent CSRF attacks</Check>
    <Check>Always generate and verify `nonce` when requesting `id_token`</Check>
    <Check>Use PKCE (`S256`) for every public client (SPA, mobile, desktop)</Check>
    <Check>Register exact redirect URIs — no wildcards, no trailing-slash variations</Check>
  </Accordion>

  <Accordion title="Token storage and handling" icon="vault">
    <Check>Treat `access_token`, `access_token_jwt`, and `refresh_token` as secrets</Check>
    <Check>Prefer server-side token storage (HTTP-only cookies, encrypted session)</Check>
    <Check>If browser storage is necessary, use `sessionStorage` over `localStorage` — never `localStorage` for refresh tokens</Check>
    <Check>Rotate app sessions after a refresh or privilege change</Check>

    <Warning>
      `refresh_token` grants long-lived access. A leaked refresh token lets an attacker impersonate the user until it's revoked. Store it server-side whenever possible.
    </Warning>
  </Accordion>

  <Accordion title="ID token validation" icon="badge-check">
    When validating an `id_token` JWT received from Ave, check all of the following:

    <Check>Signature valid against JWKS from the Ave OIDC discovery document (`{issuer}/.well-known/jwks.json`, where `issuer` is the `issuer` value from `/.well-known/openid-configuration`)</Check>
    <Check>`iss` equals the `issuer` value from the Ave OIDC discovery document (e.g. `https://aveid.net` on the hosted service)</Check>
    <Check>`aud` equals your `clientId` (or `origin:https://yourapp.com` for Quick Ave)</Check>
    <Check>`exp` is in the future</Check>
    <Check>`iat` is recent (within an acceptable clock skew)</Check>
    <Check>`nonce` matches the value you sent at authorization time</Check>
    <Check>Reject tokens with missing or unexpected values for any of the above</Check>

    <Note>
      Keep your service clocks synchronized (NTP). JWT validation is time-sensitive — a clock that's off by more than a few minutes will cause false rejections or accept expired tokens.
    </Note>
  </Accordion>

  <Accordion title="Connector (delegation) safety" icon="link">
    <Check>Request the minimum set of connector scopes your app actually needs</Check>
    <Check>Pin to known `requestedResource` values — don't accept arbitrary resource keys from user input</Check>
    <Check>Handle `access_denied` on token exchange as a normal user action (grant was revoked), not a system error</Check>
    <Check>Never cache delegated tokens past their `expires_in` time</Check>
  </Accordion>

  <Accordion title="Signing safety" icon="pen-nib">
    <Check>Include an anti-replay nonce or a unique operation ID in every signed payload</Check>
    <Check>Set short expiration times for signing requests (default 300 s, max 3600 s)</Check>
    <Check>Verify signatures server-side before performing any irreversible action</Check>
    <Check>Use canonical, deterministic payload strings to avoid signature-mismatch bugs</Check>

    <Warning>
      Never perform side effects (transfers, permission changes, deletions) based solely on a pending signing request. Wait for `status: "signed"` **and** verify the Ed25519 signature before proceeding.
    </Warning>
  </Accordion>

  <Accordion title="E2EE key safety" icon="lock">
    <Check>Normalize base64 fragments before decoding — replace ` ` (space) with `+`</Check>
    <Check>Remove key-bearing URL fragments from browser history immediately after parsing</Check>
    <Check>Keep per-identity encrypted data partitions — never mix data across identities</Check>
    <Check>Never log raw key material</Check>
    <Check>Use the [embed SDK](/sdk/embed-sdk) for iframe flows; it only accepts `postMessage` from the Ave issuer</Check>
  </Accordion>

  <Accordion title="Operational monitoring" icon="chart-line">
    <Check>Monitor spikes in `invalid_grant` errors — may indicate token replay or reuse attacks</Check>
    <Check>Monitor spikes in `invalid_scope` errors — may indicate misconfigured clients or probing</Check>
    <Check>Track refresh token failure patterns to detect stolen tokens</Check>
    <Check>Alert on unusual signing denial/approval anomalies</Check>
    <Check>Rotate app credentials (`clientSecret`) periodically</Check>
  </Accordion>
</AccordionGroup>
