# Race Conditions in OAuth 2 API implementations

OAuth 2 token endpoints race on single-use authorization codes and refresh tokens, minting many valid token pairs so revoking one leaves others active.

- Payout: hidden
- Severity: medium
- Program: Internet Bug Bounty
- Found by: dor1s
- Disclosed: 2017-09-19
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/55140
- Library page: https://bugbountylibrary.com/r/h1%3A55140
- Tags: area:web domain:api domain:web impact:ato impact:integrity kind:bounty platform:hackerone sev:medium tech:oauth technique:race-window vuln:access-control vuln:auth-bypass vuln:race-condition

**Target:** OAuth 2 /oauth/token and /oauth/revoke endpoints
**Root cause:** Token issuance for a single-use code or refresh token is not atomic; concurrent requests each pass the validity check before the code/token is marked consumed, so multiple token pairs are generated.
**Steps:**
1. Register an OAuth app and get client_id/client_secret.
2. Send victim through `/oauth/authorize?client_id=APPLICATION_ID&redirect_uri=...&response_type=code` and capture `code` from callback.
3. Fire ~20 parallel requests: `curl --data "grant_type=authorization_code&code=AUTHORIZATION_CODE_VALUE&client_id=APPLICATION_ID&client_secret=APPLICATION_SECRET&redirect_uri=APPLICATION_REDIRECT_URI" "https://OAUTH_PROVIDER_DOMAIN/oauth/token" &`
4. Collect the 1-20 distinct access_token/refresh_token pairs returned.
5. Validate each with `GET /api/me?access_token=ACCESS_TOKEN_VALUE`.
6. Revoke one token via `/oauth/revoke` or the app settings page, then re-test the others; the rest stay valid.
7. For refresh tokens, repeat parallel `grant_type=refresh_token` requests to grow token pairs exponentially.
**How found:** Hypothesized that single-use code/refresh-token consumption is non-atomic; tested 11 OAuth providers with parallel curl bursts and observed 6 fully vulnerable, 4 partially mitigated, 1 safe.
**Exploitation:** A malicious OAuth app races the token endpoint to accumulate many valid access/refresh token pairs from one authorization. When the victim revokes access, only one pair is invalidated, leaving the attacker with working tokens; refresh-token races compound the count each round.
**Escalation:** Refresh-token race yields at least one new refresh_token per attempt, so token pairs grow exponentially and revocation becomes practically impossible.
**Insight:** Single-use OAuth artifacts (authorization codes, refresh tokens) are classic race targets: fire N parallel token requests and check whether more than one distinct token pair comes back, then test whether revocation invalidates all of them.

