# @better-auth/sso provider registration has server-side request forgery via unvalidated OIDC endpoints

Better Auth SSO plugin persisted unvalidated OIDC endpoint URLs from /sso/register, letting any authenticated user trigger non-blind SSRF and, with trustEmailVerified, account takeover.

- Payout: Research
- Severity: critical
- Program: Better Auth
- Found by: vaadata-poyetont
- Disclosed: 2026-07-07
- CVE: CVE-2026-53513
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-5rr4-8452-hf4v
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5rr4-8452-hf4v
- Tags: area:web domain:web impact:ato impact:data-leak impact:internal-access kind:research platform:ghsa sev:critical tech:nodejs tech:oidc technique:source-review vuln:account-takeover vuln:auth-bypass vuln:ssrf

**Target:** POST /sso/register and /sso/update-provider OIDC endpoint URL fields in @better-auth/sso
**Root cause:** The skipDiscovery branch of provider registration stored attacker-supplied tokenEndpoint/userInfoEndpoint/jwksEndpoint as bare z.string() with no URL or origin validation, unlike the discovery branch which used validateDiscoveryUrl. Stored URLs were later fetched server-side during the OIDC callback.
**Steps:**
1. Authenticate to any Better Auth app with the sso() plugin (default gate accepts any session).
2. Send `POST /sso/register` with `skipDiscovery: true` and attacker-controlled `oidcConfig.userInfoEndpoint`, `tokenEndpoint`, `jwksEndpoint` pointing at internal targets (e.g. `http://169.254.169.254/...`).
3. Trigger the OIDC callback so the server fetches the stored URLs via validateAuthorizationCode, betterFetch, and validateToken.
4. Read the reflected response body through the user profile (non-blind SSRF).
5. If `trustEmailVerified: true`, return a malicious userInfo response with `emailVerified: true` and a victim's email to trigger OAuth auto-link account takeover.
**How found:** Reporter noticed the discovery branch routed URLs through validateDiscoveryUrl while the skipDiscovery branch persisted them as-is, and that schema fields were bare strings; traced the three fetch sites at callback time.
**Exploitation:** Any authenticated session registers a provider with internal endpoint URLs; the callback causes the auth server to fetch them and reflect bodies back, exfiltrating cloud metadata/IAM credentials or internal HTTP responses. With trustEmailVerified enabled, a crafted userInfo response auto-links to a pre-existing account with the chosen email, achieving takeover.
**Escalation:** SSRF compounded into account takeover via OAuth auto-link when trustEmailVerified: true and a victim email overlaps an attacker-chosen domain.
**Insight:** When a codebase has two config paths (discovery vs skipDiscovery), diff their validation: the shortcut path often skips the URL/origin gate the main path enforces, and stored URLs become SSRF sinks at callback time.

