Integration security checklist
Why this page
Most OAuth 2.0 and OpenID Connect security incidents come from the same short list of integration mistakes: a missing state check, an unvalidated ID Token, a token left in a URL or a log. This page is a checklist you can walk before go-live and during security reviews.
It is split into two parts on purpose:
- Part 1 - what you must verify covers the things you are responsible for in your own application code. These are the checks that actually prevent the common attacks.
- Part 2 - what LuxID enforces for you covers the server-side protections LuxID provides. You do not implement these, but knowing they exist tells you what your integration can rely on - and what you should not try to probe against production.
The items below are written as checks on your own integration. Do not run active penetration tests, fuzzing, or bypass attempts against LuxID production - that is indistinguishable from an attack on a shared production identity service used by many organisations, and may get your client blocked. If you want to security-test the flow itself, use the sandbox / UAT environment, and report any suspected LuxID-side weakness through the responsible-disclosure channel on the Contact and support channels page (also published as /.well-known/security.txt).
Before you start - know your flow
A quick orientation, so the rest of the checklist is concrete:
- You use the Authorization Code flow with PKCE (S256). LuxID does not support the deprecated Implicit flow.
- You know whether you consume OpenID Connect (you receive an ID Token and identify the user) or only OAuth 2.0 (you receive an access token to call an API). Most sign-in integrations are OIDC.
- You know your
client_id, your registered exactredirect_uri(s), and where yourclient_secretlives (confidential clients only - never in a mobile app or SPA). - Your integration reads endpoints from the discovery document (
/.well-known/openid-configuration) rather than hardcoding them.
Part 1 - what you must verify in your integration
These are your responsibility. A gap here is a vulnerability in your application.
state - CSRF protection on the callback
- Your app generates a unique, unpredictable
statevalue for every authorization request (cryptographically random, not a counter or timestamp). - Your app stores the value it sent (in the user's session) and rejects the callback if the returned
statedoes not match. - Each
stateis single-use: a replayed or reusedstateis rejected. - A callback that arrives with no
stateis rejected.
nonce - replay protection on the ID Token (OIDC)
- Your app sends a unique, unpredictable
nonceon every authentication request. - Your app validates that the
nonceclaim in the returned ID Token equals the value it sent, and rejects the token otherwise. - Each
nonceis single-use; a replayed ID Token (oldnonce) is rejected.
ID Token validation (OIDC)
Validate every ID Token against all five OpenID Connect Core 1.0 §3.1.3.7 checks - do not trust a token because it merely decodes:
- Signature verified against LuxID's JWKS (RS256). See Key management.
-
issmatches the LuxID issuer exactly. -
audcontains yourclient_id. - If
audcontains more than one value,azpis present and equals yourclient_id. -
expis in the future andiatis sane (apply a small clock-skew tolerance - use the smallest your infrastructure allows). -
noncematches (see above).
See Tokens and claims and Token validation issues.
redirect_uri and scopes
- Every
redirect_uriyour app uses is registered exactly with LuxID and served over HTTPS. LuxID matches redirect URIs exactly; there are no wildcards. - Your app requests only the scopes it actually needs; it does not request extra or speculative scopes "just in case".
- Your app treats the set of claims it receives as the source of truth for what the user consented to - it does not assume it received a scope it did not request.
Token handling and storage
- Access and refresh tokens are never placed in URLs (no tokens in query strings, no tokens in redirects).
- Tokens and the authorization code are never written to logs, analytics, or error trackers.
- Refresh tokens are held in platform secure storage (Keychain / Keystore / encrypted server-side store), never in
localStorageor a plain file. -
client_secret(confidential clients) is held server-side only, never shipped in mobile or SPA code. - Your app validates the access token's
expbefore use and refreshes proactively rather than on error.
Session and logout
- Signing out of your application clears your own application session; you do not assume it ends the LuxID SSO session (it does not - see Session management).
- Where appropriate, you revoke refresh/access tokens on logout via the revocation endpoint.
Part 2 - what LuxID enforces for you
You do not implement these - LuxID does. They are listed so you know the protections your integration relies on. Do not actively test these against production (see the caution above); if you need to see the behaviour, use the sandbox / UAT.
redirect_urivalidation. LuxID rejects anyredirect_urithat does not exactly match a registered value. There is no partial or wildcard matching to bypass.response_typehandling. LuxID accepts only the response types it supports (Authorization Code); it does not silently fall back to weaker modes.- Malformed authorization requests. Missing, duplicated, or empty required parameters are rejected rather than guessed.
- Authorization code single-use. A code can be redeemed once. Reuse, replay, or redemption of an expired code is rejected, and reuse of an already-redeemed code invalidates the associated tokens.
- Code binding. An authorization code is bound to the client and the PKCE
code_verifierthat requested it - a code issued for one client/session cannot be redeemed by another. - Concurrency. Race conditions on code redemption resolve to a single successful exchange.
stateandnonceare echoed, not validated for you. LuxID returns yourstateand reflects yournonceinto the ID Token, but validating them is your job (Part 1) - their security value comes from your check, not from LuxID.
If, while building against the sandbox, you observe behaviour that contradicts this list, treat it as a potential finding and report it through the responsible-disclosure channel - do not keep probing. LuxID commissions its own authenticated penetration tests; coordinated reports are welcome, unsolicited production attacks are not.
How to use this checklist
- Before go-live: walk Part 1 end to end. Every box must be checked for relying-party approval - it maps directly to the security baseline.
- During a security review: use Part 1 as the audit scope for your own code, and Part 2 as the boundary of what belongs to LuxID.
- When something looks wrong on LuxID's side: stop testing and use Contact and support channels.
Related pages
- Protect your Application - the how-to behind each Part 1 item
- Tokens and claims - what to validate and why
- OpenID Connect - the flow these checks apply to
- Sandbox environment - where to test safely
- Contact and support channels - responsible disclosure and support