Skip to main content
reference 3 min read

Security expectations for partners

Required controls — credential storage, logging hygiene, refresh handling, TLS, redirect URIs, state, PKCE, and webhook URLs.

we-fly is GDPR-regulated and serves a community where leaking flight location can put pilots at physical risk. We expect partners to meet the same bar.

Required controls

1. Credential storage

CredentialWhere it may live
client_secret (confidential)Server-side only. Secret manager, env var, or HSM. Never in the browser, mobile bundle, or git repo.
Refresh tokensEncrypted at rest in your DB. The encryption key lives in a secret manager (not next to the ciphertext).
Access tokensMemory or short-lived encrypted store; never logged.
API keys (wf_key_…)Secret manager only. Treat like a password.

If you cannot keep a secret server-side, register a public client with PKCE — that's exactly what PKCE exists for.

2. Logging hygiene

  • Never log Authorization headers in their full form. Truncate to prefix (wf_at_3b8…) before writing to disk, and only when you must.
  • Never log request bodies of token-endpoint calls — they contain client_secret, code_verifier, and refresh_token.
  • If you ship logs to a third-party (Datadog, Honeycomb, etc.), set up scrubbing rules before integration goes live.

3. Refresh-token handling

See the dedicated section in User authorization.

In short: single-use, atomic replace, never retry the old token.

4. TLS

  • All requests must use HTTPS. Plain HTTP is unsupported.
  • Verify certificates. Don't disable verification in production.
  • Use modern ciphers; TLS 1.2+ only.

5. Redirect URIs

  • Register every redirect_uri your app uses. Wildcards aren't supported.
  • Don't use redirect_uris that match dev/staging in production — redirect-URI mismatches at the token endpoint result in invalid_grant.
  • For native/mobile apps, use OS-native deep links or a custom URI scheme; for SPAs, use HTTPS URLs.

6. State parameter

  • Always generate state from a CSPRNG (at least 128 bits of entropy).
  • Bind state to the user's browser session (cookie or session storage), not to a global. Otherwise a malicious user can leak another user's authorization code.
  • Verify state on every callback. Refuse mismatches.

7. PKCE for public clients

  • Generate a fresh code_verifier for every authorization request.
  • Use S256; we reject plain.
  • Verifier length: 43–128 unreserved URL chars. The example in User authorization uses 32 random bytes → 43 base64url chars, which is the minimum acceptable.

8. Webhook URL constraints

If you register a webhook endpoint, the URL you give us must:

  • Use https:// (http://localhost only in dev environments).
  • Resolve to a public IP. We re-check DNS on every delivery attempt and reject any address inside private / loopback / link-local / internal ranges (IPv4 and IPv6). The delivery is pinned to the screened address, so DNS rebinding between the check and the connection can't reach an internal target.
  • Not redirect us anywhere — we don't follow redirects and treat 3xx responses as failures.

See Webhooks for details. These rules protect both we-fly's internal network and your own — a wrongly-pointed webhook is unlikely to leak internal data because the request never lands on the wire.

Reporting a security issue

For our app or for an integration you've discovered behaving suspiciously, reach out through the contact page. We acknowledge within 48 hours.

Please don't disclose credential leaks or exploitable vulnerabilities publicly — coordinate disclosure with us first.

What we monitor on our side

You should know that we:

  • log token issuance and refresh events (with metadata such as IP and User-Agent) and retain them for a limited window before auto-purging;
  • detect refresh-token replay and burn the whole chain;
  • detect authorization-code replay and revoke any tokens minted from it;
  • rate-limit the token endpoint to throttle client_secret brute-forcing;
  • monitor per-application error rates and flag anomalous patterns for review;
  • expose lastUsedAt per token to the dashboard so users can spot abandoned grants.

You should mirror these on your side where applicable — leaked secrets get noticed faster when both ends are watching.