Overview
CVE-2026-42560 describes a Broken Authentication flaw in the Patreon OAuth provider used with Go applications (including those built with Gin). When the provider maps every Patreon account to the same local user.ID instead of deriving a unique ID from the Patreon account data, all Patreon-authenticated users collapse into one identity. This enables cross-account access, privilege confusion, and potential leakage of subscription state in apps that rely on the token.User.ID as the stable key. CWE-287 (Improper Authentication) is the underlying risk: attackers can masquerade as other patrons if the local identity is not uniquely tied to the provider account.
Exploitation occurs when a Go (Gin) app trusts a single, provider-derived key to gate access or customize per-user state. If the mapping layer uses a constant or insufficiently distinct key (e.g., a fixed local user ID) for all Patreon logins, different Patreon accounts end up sharing the same local identity. Attackers could gain access to resources and subscription data that should be isolated per Patreon account, leading to privilege escalation, cross-account leakage, or subscription state confusion.
The issue affected Patreon OAuth provider versions 1.18.0 through 1.25.1 and 2.0.0 through 2.1.1; it was patched in 1.25.2 and 2.1.2. This remediation guide focuses on Go (Gin) implementations and demonstrates how to derive a unique local identity per Patreon account. It also provides a concrete code pattern showing the vulnerable approach and the corrected approach to prevent similar breakages in the future.
Remediation in Go (Gin) should ensure the local identity key is derived from provider data (for example, using a provider prefix plus the Patreon user ID) rather than a constant. The following sample shows a side-by-side comparison of the vulnerable pattern and the fixed pattern, illustrating how to prevent cross-account identity collisions.
Affected Versions
1.18.0 - 1.25.1; 2.0.0 - 2.1.1
Code Fix Example
Go (Gin) API Security Remediation
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type PatreonUser struct {
ID string
Email string
}
type User struct {
ID string
Name string
}
var userStore = map[string]*User{}
// Vulnerable mapping: all Patreon logins map to a single local user
func vulnerableMapPatreonUser(p PatreonUser) *User {
localKey := "PATREON:LOCAL:USER" // constant key
if u, ok := userStore[localKey]; ok {
return u
}
u := &User{ID: localKey, Name: "Patreon User"}
userStore[localKey] = u
return u
}
// Fixed mapping: derive unique local user key per Patreon account
func fixedMapPatreonUser(p PatreonUser) *User {
localKey := fmt.Sprintf("PATREON:%s", p.ID) // provider-prefixed, per-account key
if u, ok := userStore[localKey]; ok {
return u
}
u := &User{ID: localKey, Name: "Patreon User"}
userStore[localKey] = u
return u
}
func main() {
r := gin.Default()
r.GET("/vuln/callback", func(c *gin.Context) {
p := PatreonUser{ID: c.Query("id"), Email: c.Query("email")}
u := vulnerableMapPatreonUser(p)
c.String(http.StatusOK, "Vulnerable user ID: %s\n", u.ID)
})
r.GET("/fix/callback", func(c *gin.Context) {
p := PatreonUser{ID: c.Query("id"), Email: c.Query("email")}
u := fixedMapPatreonUser(p)
c.String(http.StatusOK, "Fixed user ID: %s\n", u.ID)
})
r.Run(":8080")
}