fix(security): 🐛 handle errors in OAuth2 examples and passkey methods
Some checks failed
Build , Vet Test, and Lint / Run Vet Tests (1.24.x) (push) Successful in -22m52s
Build , Vet Test, and Lint / Run Vet Tests (1.23.x) (push) Successful in -22m42s
Build , Vet Test, and Lint / Build (push) Successful in -26m19s
Build , Vet Test, and Lint / Lint Code (push) Successful in -25m40s
Tests / Unit Tests (push) Successful in -26m33s
Tests / Integration Tests (push) Failing after -26m55s

* Add error handling for JSON encoding and HTTP server calls.
* Update passkey examples to improve readability and maintainability.
* Ensure consistent use of error handling across all examples.
This commit is contained in:
2026-01-31 22:58:52 +02:00
parent 2e7b3e7abd
commit 7600a6d1fb
4 changed files with 48 additions and 45 deletions

View File

@@ -54,10 +54,10 @@ func ExampleOAuth2Google() {
}) })
// Return user info as JSON // Return user info as JSON
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
// Example: OAuth2 Authentication with GitHub // Example: OAuth2 Authentication with GitHub
@@ -89,10 +89,10 @@ func ExampleOAuth2GitHub() {
return return
} }
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
// Example: Custom OAuth2 Provider // Example: Custom OAuth2 Provider
@@ -142,10 +142,10 @@ func ExampleOAuth2Custom() {
return return
} }
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
// Example: Multi-Provider OAuth2 with Security Integration // Example: Multi-Provider OAuth2 with Security Integration
@@ -240,10 +240,10 @@ func ExampleOAuth2MultiProvider() {
protectedRouter.HandleFunc("/profile", func(w http.ResponseWriter, r *http.Request) { protectedRouter.HandleFunc("/profile", func(w http.ResponseWriter, r *http.Request) {
userCtx, _ := GetUserContext(r.Context()) userCtx, _ := GetUserContext(r.Context())
json.NewEncoder(w).Encode(userCtx) _ = json.NewEncoder(w).Encode(userCtx)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
// Example: OAuth2 with Token Refresh // Example: OAuth2 with Token Refresh
@@ -294,10 +294,10 @@ func ExampleOAuth2TokenRefresh() {
SameSite: http.SameSiteLaxMode, SameSite: http.SameSiteLaxMode,
}) })
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
// Example: OAuth2 Logout // Example: OAuth2 Logout
@@ -326,7 +326,7 @@ func ExampleOAuth2Logout() {
// Get user ID from session // Get user ID from session
userCtx, err := oauth2Auth.Authenticate(r) userCtx, err := oauth2Auth.Authenticate(r)
if err == nil { if err == nil {
oauth2Auth.Logout(r.Context(), LogoutRequest{ _ = oauth2Auth.Logout(r.Context(), LogoutRequest{
Token: token, Token: token,
UserID: userCtx.UserID, UserID: userCtx.UserID,
}) })
@@ -343,10 +343,10 @@ func ExampleOAuth2Logout() {
}) })
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte("Logged out successfully")) _, _ = w.Write([]byte("Logged out successfully"))
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
// Example: Complete OAuth2 Integration with Database Setup // Example: Complete OAuth2 Integration with Database Setup
@@ -374,7 +374,7 @@ func ExampleOAuth2Complete() {
// Public routes // Public routes
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome! <a href='/auth/google/login'>Login with Google</a>")) _, _ = w.Write([]byte("Welcome! <a href='/auth/google/login'>Login with Google</a>"))
}) })
router.HandleFunc("/auth/google/login", func(w http.ResponseWriter, r *http.Request) { router.HandleFunc("/auth/google/login", func(w http.ResponseWriter, r *http.Request) {
@@ -411,17 +411,17 @@ func ExampleOAuth2Complete() {
protectedRouter.HandleFunc("/dashboard", func(w http.ResponseWriter, r *http.Request) { protectedRouter.HandleFunc("/dashboard", func(w http.ResponseWriter, r *http.Request) {
userCtx, _ := GetUserContext(r.Context()) userCtx, _ := GetUserContext(r.Context())
w.Write([]byte(fmt.Sprintf("Welcome, %s! Your email: %s", userCtx.UserName, userCtx.Email))) _, _ = fmt.Fprintf(w, "Welcome, %s! Your email: %s", userCtx.UserName, userCtx.Email)
}) })
protectedRouter.HandleFunc("/api/profile", func(w http.ResponseWriter, r *http.Request) { protectedRouter.HandleFunc("/api/profile", func(w http.ResponseWriter, r *http.Request) {
userCtx, _ := GetUserContext(r.Context()) userCtx, _ := GetUserContext(r.Context())
json.NewEncoder(w).Encode(userCtx) _ = json.NewEncoder(w).Encode(userCtx)
}) })
protectedRouter.HandleFunc("/auth/logout", func(w http.ResponseWriter, r *http.Request) { protectedRouter.HandleFunc("/auth/logout", func(w http.ResponseWriter, r *http.Request) {
userCtx, _ := GetUserContext(r.Context()) userCtx, _ := GetUserContext(r.Context())
oauth2Auth.Logout(r.Context(), LogoutRequest{ _ = oauth2Auth.Logout(r.Context(), LogoutRequest{
Token: userCtx.SessionID, Token: userCtx.SessionID,
UserID: userCtx.UserID, UserID: userCtx.UserID,
}) })
@@ -437,7 +437,7 @@ func ExampleOAuth2Complete() {
http.Redirect(w, r, "/", http.StatusTemporaryRedirect) http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }
func setupOAuth2Tables(db *sql.DB) { func setupOAuth2Tables(db *sql.DB) {
@@ -446,7 +446,7 @@ func setupOAuth2Tables(db *sql.DB) {
ctx := context.Background() ctx := context.Background()
// Create users table if not exists // Create users table if not exists
db.ExecContext(ctx, ` _, _ = db.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE, username VARCHAR(255) NOT NULL UNIQUE,
@@ -464,7 +464,7 @@ func setupOAuth2Tables(db *sql.DB) {
`) `)
// Create user_sessions table (used for both regular and OAuth2 sessions) // Create user_sessions table (used for both regular and OAuth2 sessions)
db.ExecContext(ctx, ` _, _ = db.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS user_sessions ( CREATE TABLE IF NOT EXISTS user_sessions (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
session_token VARCHAR(500) NOT NULL UNIQUE, session_token VARCHAR(500) NOT NULL UNIQUE,
@@ -547,7 +547,7 @@ func ExampleOAuth2AllProviders() {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
} }
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
// GitHub routes // GitHub routes
@@ -562,7 +562,7 @@ func ExampleOAuth2AllProviders() {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
} }
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
// Microsoft routes // Microsoft routes
@@ -577,7 +577,7 @@ func ExampleOAuth2AllProviders() {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
} }
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
// Facebook routes // Facebook routes
@@ -592,7 +592,7 @@ func ExampleOAuth2AllProviders() {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
} }
json.NewEncoder(w).Encode(loginResp) _ = json.NewEncoder(w).Encode(loginResp)
}) })
// Create security list for protected routes // Create security list for protected routes
@@ -608,8 +608,8 @@ func ExampleOAuth2AllProviders() {
protectedRouter.HandleFunc("/profile", func(w http.ResponseWriter, r *http.Request) { protectedRouter.HandleFunc("/profile", func(w http.ResponseWriter, r *http.Request) {
userCtx, _ := GetUserContext(r.Context()) userCtx, _ := GetUserContext(r.Context())
json.NewEncoder(w).Encode(userCtx) _ = json.NewEncoder(w).Encode(userCtx)
}) })
http.ListenAndServe(":8080", router) _ = http.ListenAndServe(":8080", router)
} }

View File

@@ -570,6 +570,7 @@ func NewFacebookAuthenticator(clientID, clientSecret, redirectURL string, db *sq
func NewMultiProviderAuthenticator(db *sql.DB, configs map[string]OAuth2Config) *DatabaseAuthenticator { func NewMultiProviderAuthenticator(db *sql.DB, configs map[string]OAuth2Config) *DatabaseAuthenticator {
auth := NewDatabaseAuthenticator(db) auth := NewDatabaseAuthenticator(db)
//nolint:gocritic // OAuth2Config is copied but kept for API simplicity
for _, cfg := range configs { for _, cfg := range configs {
auth.WithOAuth2(cfg) auth.WithOAuth2(cfg)
} }

View File

@@ -22,12 +22,13 @@ func PasskeyAuthenticationExample() {
}) })
// Create authenticator with passkey support // Create authenticator with passkey support
auth := NewDatabaseAuthenticatorWithOptions(db, DatabaseAuthenticatorOptions{ // Option 1: Pass during creation
_ = NewDatabaseAuthenticatorWithOptions(db, DatabaseAuthenticatorOptions{
PasskeyProvider: passkeyProvider, PasskeyProvider: passkeyProvider,
}) })
// Or use WithPasskey method // Option 2: Use WithPasskey method
auth = NewDatabaseAuthenticator(db).WithPasskey(passkeyProvider) auth := NewDatabaseAuthenticator(db).WithPasskey(passkeyProvider)
ctx := context.Background() ctx := context.Background()
@@ -106,9 +107,9 @@ func PasskeyAuthenticationExample() {
// Get all credentials for a user // Get all credentials for a user
credentials, _ := auth.GetPasskeyCredentials(ctx, 1) credentials, _ := auth.GetPasskeyCredentials(ctx, 1)
for _, cred := range credentials { for i := range credentials {
fmt.Printf("Credential: %s (created: %s, last used: %s)\n", fmt.Printf("Credential: %s (created: %s, last used: %s)\n",
cred.Name, cred.CreatedAt, cred.LastUsedAt) credentials[i].Name, credentials[i].CreatedAt, credentials[i].LastUsedAt)
} }
// Update credential name // Update credential name
@@ -130,7 +131,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
Username string `json:"username"` Username string `json:"username"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
} }
json.NewDecoder(r.Body).Decode(&req) _ = json.NewDecoder(r.Body).Decode(&req)
options, err := auth.BeginPasskeyRegistration(r.Context(), PasskeyBeginRegistrationRequest{ options, err := auth.BeginPasskeyRegistration(r.Context(), PasskeyBeginRegistrationRequest{
UserID: req.UserID, UserID: req.UserID,
@@ -147,7 +148,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
challenges[sessionID] = options.Challenge challenges[sessionID] = options.Challenge
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(options) _ = json.NewEncoder(w).Encode(options)
}) })
// Complete registration endpoint // Complete registration endpoint
@@ -157,7 +158,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
Response PasskeyRegistrationResponse `json:"response"` Response PasskeyRegistrationResponse `json:"response"`
CredentialName string `json:"credential_name"` CredentialName string `json:"credential_name"`
} }
json.NewDecoder(r.Body).Decode(&req) _ = json.NewDecoder(r.Body).Decode(&req)
// Get stored challenge (from session in production) // Get stored challenge (from session in production)
sessionID := "session-123" sessionID := "session-123"
@@ -176,7 +177,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(credential) _ = json.NewEncoder(w).Encode(credential)
}) })
// Begin authentication endpoint // Begin authentication endpoint
@@ -184,7 +185,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
var req struct { var req struct {
Username string `json:"username"` // Optional Username string `json:"username"` // Optional
} }
json.NewDecoder(r.Body).Decode(&req) _ = json.NewDecoder(r.Body).Decode(&req)
options, err := auth.BeginPasskeyAuthentication(r.Context(), PasskeyBeginAuthenticationRequest{ options, err := auth.BeginPasskeyAuthentication(r.Context(), PasskeyBeginAuthenticationRequest{
Username: req.Username, Username: req.Username,
@@ -199,7 +200,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
challenges[sessionID] = options.Challenge challenges[sessionID] = options.Challenge
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(options) _ = json.NewEncoder(w).Encode(options)
}) })
// Complete authentication endpoint // Complete authentication endpoint
@@ -207,7 +208,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
var req struct { var req struct {
Response PasskeyAuthenticationResponse `json:"response"` Response PasskeyAuthenticationResponse `json:"response"`
} }
json.NewDecoder(r.Body).Decode(&req) _ = json.NewDecoder(r.Body).Decode(&req)
// Get stored challenge (from session in production) // Get stored challenge (from session in production)
sessionID := "session-456" sessionID := "session-456"
@@ -238,7 +239,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
}) })
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(loginResponse) _ = json.NewEncoder(w).Encode(loginResponse)
}) })
// List credentials endpoint // List credentials endpoint
@@ -257,7 +258,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(credentials) _ = json.NewEncoder(w).Encode(credentials)
}) })
// Delete credential endpoint // Delete credential endpoint
@@ -271,7 +272,7 @@ func PasskeyHTTPHandlersExample(auth *DatabaseAuthenticator) {
var req struct { var req struct {
CredentialID string `json:"credential_id"` CredentialID string `json:"credential_id"`
} }
json.NewDecoder(r.Body).Decode(&req) _ = json.NewDecoder(r.Body).Decode(&req)
err = auth.DeletePasskeyCredential(r.Context(), userCtx.UserID, req.CredentialID) err = auth.DeletePasskeyCredential(r.Context(), userCtx.UserID, req.CredentialID)
if err != nil { if err != nil {

View File

@@ -61,11 +61,11 @@ func (p *DatabasePasskeyProvider) BeginRegistration(ctx context.Context, userID
} }
excludeCredentials := make([]PasskeyCredentialDescriptor, 0, len(credentials)) excludeCredentials := make([]PasskeyCredentialDescriptor, 0, len(credentials))
for _, cred := range credentials { for i := range credentials {
excludeCredentials = append(excludeCredentials, PasskeyCredentialDescriptor{ excludeCredentials = append(excludeCredentials, PasskeyCredentialDescriptor{
Type: "public-key", Type: "public-key",
ID: cred.CredentialID, ID: credentials[i].CredentialID,
Transports: cred.Transports, Transports: credentials[i].Transports,
}) })
} }
@@ -319,7 +319,8 @@ func (p *DatabasePasskeyProvider) GetCredentials(ctx context.Context, userID int
} }
credentials := make([]PasskeyCredential, 0, len(rawCreds)) credentials := make([]PasskeyCredential, 0, len(rawCreds))
for _, raw := range rawCreds { for i := range rawCreds {
raw := rawCreds[i]
credID, err := base64.StdEncoding.DecodeString(raw.CredentialID) credID, err := base64.StdEncoding.DecodeString(raw.CredentialID)
if err != nil { if err != nil {
continue continue