Trusting client-side redirect parameters

A callback URL often carries a status flag, an email address or an error message as a query parameter. Reading that value and treating it as fact is the most common mistake in social-login integrations, because any value in a URL is user-controlled.

Treat redirect parameters as a hint to update the UI, and require a server-to-server call to a protected endpoint before accepting the result as true.

Skipping or mishandling the state parameter

The state value protects against cross-site request forgery on the callback. Skipping it, reusing a static value, or failing to check it against the one issued for that attempt lets an attacker link their own authorization result to a victim’s session.

Generate a fresh, unpredictable state or session identifier per attempt, store it server-side or in a short-lived cookie, and reject callbacks that do not match.

Hardcoding a single email field for every provider

Providers differ in how they expose an email address. Some return it directly in an ID token, some require a separate call, and some, like GitHub, can return several addresses with only one marked primary and verified.

An integration that assumes one shape across all providers will intermittently store missing or wrong addresses. Normalize each provider’s response before it reaches shared account logic, and only accept addresses marked verified where a provider offers that flag.

Leaving sessions and codes valid too long

An authorization code or a login-broker session that stays valid for hours is a much larger target than one that expires in minutes. Long-lived intermediate values increase the value of a leaked callback URL, a shared screenshot or an exposed server log.

Match expiry to how long a real user actually needs, poll for completion instead of extending expiry, and stop accepting a value the moment it is consumed.

Putting secrets where a browser or repository can read them

Client secrets and API keys occasionally end up in frontend bundles, mobile binaries, or committed configuration files because it was the fastest way to get a demo working. Once shipped to a browser or app store, a secret is effectively public regardless of intent.

Keep secrets in server-side environment configuration only, add a rotation path before you need one, and scan repositories for accidental commits as a standing habit rather than a one-time cleanup.

Not planning for partial failure

A provider outage, a network timeout mid-verification, or a user closing a popup before the result arrives are normal events, not edge cases. Integrations that only handle the happy path leave users stuck on a spinner with no way to retry.

Design explicit failed and expired states, offer a clear retry action, and make sure a half-finished attempt cannot be replayed to complete a login later.