Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 23, 20264 min read

OAuth2 Security: How to Properly Validate Redirect URIs

Master OAuth2 security by implementing strict redirect URI validation. Prevent authorization code injection and open redirects in your authentication flows.

oauth2securityauthenticationweb-securitypkcenodejsWebBackend

We’ve all been there: staring at a callback handler in a production log, trying to figure out why an authorization code leaked to a third-party domain. It’s usually a small oversight in the configuration of an OAuth2 client, but the security implications are massive.

If you aren't strictly validating your redirect URIs, you aren't just risking a minor bug; you're opening a door for attackers to hijack user sessions. Let's dig into how to lock this down.

The Danger of Loose Redirect URI Validation

When an OAuth2 authorization server receives a request, it checks the redirect_uri parameter against a pre-registered list. If your server is configured to accept partial matches or—worse—doesn't check the URI at all, you're vulnerable to open redirection.

I once worked on a project where we used a wildcard sub-domain configuration to allow "flexibility" for developers during local testing. It was a mistake. An attacker crafted a request that redirected the authorization code to a malicious site they controlled. Because we hadn't hardened our redirect URI validation, the attacker intercepted the code and exchanged it for an access token before the user even realized what happened.

Why Exact Matching is Non-Negotiable

Never rely on regex or partial string matching for redirect URIs. If your registration UI allows wildcards like https://*.example.com, you're effectively asking for trouble.

An attacker can register https://app-login.example.com.attacker.com if your validation logic is flawed. Instead, force an exact, case-sensitive string match.

Here is what a robust validation check looks like in Node.js:

JAVASCRIPT
const VALID_REDIRECT_URIS = [
  CE9178">'https://myapp.com/callback',
  CE9178">'https://api.myapp.com/auth/callback'
];

function isValidRedirectUri(incomingUri) {
  // Use a constant-time comparison if possible to prevent timing attacks
  return VALID_REDIRECT_URIS.includes(incomingUri);
}

This approach is simple, predictable, and doesn't leave room for creative bypasses.

Preventing Authorization Code Injection

Even with perfect URI validation, you need to protect the authorization code itself. Authorization code injection happens when an attacker injects their own code into a victim's session, forcing the user to associate their account with the attacker’s credentials.

To stop this, we use the state parameter and, more importantly, Proof Key for Code Exchange (PKCE).

Implementing PKCE for Every Client

PKCE (RFC 7636) was originally designed for native mobile apps, but it’s now the standard for all OAuth2 clients, including SPAs and server-side web apps. It prevents authorization code injection by ensuring that the client requesting the token is the same one that initiated the authorization request.

  1. Client creates a code_verifier (a random high-entropy string).
  2. Client creates a code_challenge (usually a SHA-256 hash of the verifier).
  3. Authorization Request: Client sends the code_challenge to the server.
  4. Token Request: Client sends the original code_verifier to the server.

The server verifies that the hash of the code_verifier matches the code_challenge provided in the initial step. If an attacker intercepts the authorization code, they still lack the code_verifier, making the intercepted code useless.

Beyond the Basics: Hardening the Flow

Securing your authentication pipeline goes beyond just the redirect. If you're building a modern stack, consider these additional layers:

  • Enforce HTTPS: Never allow plain HTTP for redirect URIs. Even in development, use local HTTPS certificates (like those generated by mkcert).
  • Limit Token Lifetimes: Keep your authorization codes short-lived—think 30 to 60 seconds maximum.
  • Use DTOs: When handling the callback response, use Data Transfer Objects to ensure you aren't accidentally binding malicious request data to your internal user models, similar to preventing mass assignment.

Frequently Asked Questions

Q: Can I use a wildcard for my staging environment? A: Avoid it. It’s better to maintain a separate environment-specific configuration file that lists the exact staging URL. Wildcards are a common source of "it worked in staging, but we got hacked in production" stories.

Q: Is state parameter validation still necessary if I use PKCE? A: Yes. While PKCE handles code injection, the state parameter is your primary defense against Cross-Site Request Forgery (CSRF). Always generate a unique, cryptographically strong state value for every request and verify it upon the callback.

Q: What if I have multiple redirect URIs for different regions? A: Maintain an allow-list array in your environment configuration. If you find yourself needing to add URIs dynamically, you're likely creating a security bottleneck that will bite you later.

Final Thoughts

Security in OAuth2 flows is rarely about a single "magic" setting. It’s about the cumulative effect of strict configuration, like exact redirect URI validation and the universal adoption of PKCE.

If I were refactoring our auth service today, I’d focus heavily on automating the allow-list deployment. Manual entry is where configuration drift happens, and drift is where vulnerabilities live. Keep your validation logic simple, your secrets handled outside the codebase, and your dependencies updated to the latest versions. It’s a constant grind, but it’s the only way to keep the bad actors out of your users' accounts.

Back to Blog

Similar Posts

SecurityJune 23, 20264 min read

Cache poisoning prevention: Secure your CDN and proxy layers

Cache poisoning happens when malicious headers trick your CDN. Learn how to secure your Node.js and PHP apps against header injection and request smuggling.

Read more
SecurityJune 22, 20264 min read

Preventing Open Redirect Vulnerabilities: A Guide for Developers

Learn to stop open redirect vulnerabilities by validating destination URLs. Protect your Node.js and PHP apps from phishing attacks with these practical tips.

Read more
SecurityJune 22, 20264 min read

Preventing Session Fixation: Hardening Authentication Flows in Node.js and Laravel

Learn how to prevent session fixation by properly regenerating session IDs during login. Secure your Node.js and Laravel apps with these battle-tested tips.

Read more