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
| Credential | Where 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 tokens | Encrypted at rest in your DB. The encryption key lives in a secret manager (not next to the ciphertext). |
| Access tokens | Memory 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
Authorizationheaders 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, andrefresh_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_uriyour 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 ininvalid_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
statefrom a CSPRNG (at least 128 bits of entropy). - Bind
stateto 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
stateon every callback. Refuse mismatches.
7. PKCE for public clients
- Generate a fresh
code_verifierfor every authorization request. - Use
S256; we rejectplain. - 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://localhostonly 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_secretbrute-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.