From 0d8da84f79bb4616d91dc23aca6ec4fd1c29ebc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Fri, 13 Mar 2026 18:22:55 +0100 Subject: [PATCH 1/4] Add custom AuthErrorHandler tests for error responses --- auth_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/auth_test.go b/auth_test.go index e72020a..4520527 100644 --- a/auth_test.go +++ b/auth_test.go @@ -692,6 +692,73 @@ func TestRequiredRoles(t *testing.T) { }) } +// TestCustomAuthErrorHandler tests that a custom AuthErrorHandler overrides the default error response +func TestCustomAuthErrorHandler(t *testing.T) { + mockAuth := NewMockAuthService() + + app := fiber.New() + oapi := New(app, Config{ + EnableOpenAPIDocs: true, + EnableValidation: true, + EnableAuthorization: true, + AuthService: mockAuth, + SecuritySchemes: map[string]SecurityScheme{ + "bearerAuth": {Type: "http", Scheme: "bearer", BearerFormat: "JWT"}, + }, + DefaultSecurity: []map[string][]string{ + {"bearerAuth": {}}, + }, + AuthErrorHandler: func(c *fiber.Ctx, err *AuthError) error { + return c.Status(err.StatusCode).JSON(fiber.Map{ + "custom": true, + "message": err.Message, + "status": err.StatusCode, + }) + }, + }) + + Get(oapi, "/protected", func(c *fiber.Ctx, input struct{}) (fiber.Map, *ErrorResponse) { + return fiber.Map{"ok": true}, nil + }, OpenAPIOptions{Summary: "Protected"}) + + t.Run("custom handler for 401", func(t *testing.T) { + req := httptest.NewRequest("GET", "/protected", nil) + // No auth header -> 401 + resp, _ := app.Test(req) + if resp.StatusCode != 401 { + t.Fatalf("Expected 401, got %d", resp.StatusCode) + } + var body map[string]any + json.NewDecoder(resp.Body).Decode(&body) + resp.Body.Close() + if body["custom"] != true { + t.Error("Expected custom error handler to be invoked") + } + }) + + t.Run("custom handler for 403", func(t *testing.T) { + Get(oapi, "/admin-only", func(c *fiber.Ctx, input struct{}) (fiber.Map, *ErrorResponse) { + return fiber.Map{"ok": true}, nil + }, WithRoles(OpenAPIOptions{Summary: "Admin only"}, "admin")) + + req := httptest.NewRequest("GET", "/admin-only", nil) + req.Header.Set("Authorization", "Bearer valid-token") // has "user" role, not "admin" + resp, _ := app.Test(req) + if resp.StatusCode != 403 { + t.Fatalf("Expected 403, got %d", resp.StatusCode) + } + var body map[string]any + json.NewDecoder(resp.Body).Decode(&body) + resp.Body.Close() + if body["custom"] != true { + t.Error("Expected custom error handler to be invoked for 403") + } + if body["status"] != float64(403) { + t.Errorf("Expected status 403 in body, got %v", body["status"]) + } + }) +} + // TestWithRolesHelper tests the WithRoles helper function func TestWithRolesHelper(t *testing.T) { opts := WithRoles(OpenAPIOptions{Summary: "test"}, "admin", "editor") From 2c8b8e45363b97526f035939c5208785be0f10fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Fri, 13 Mar 2026 18:23:00 +0100 Subject: [PATCH 2/4] Add support for custom AuthErrorHandler in OApiApp configuration --- fiberoapi.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fiberoapi.go b/fiberoapi.go index df24a37..80d88dc 100644 --- a/fiberoapi.go +++ b/fiberoapi.go @@ -42,7 +42,8 @@ func New(app *fiber.App, config ...Config) *OApiApp { provided.OpenAPIDocsPath != "" || provided.OpenAPIJSONPath != "" || provided.OpenAPIYamlPath != "" || - provided.ValidationErrorHandler != nil + provided.ValidationErrorHandler != nil || + provided.AuthErrorHandler != nil // Only override boolean defaults if the config appears to be explicitly set if hasExplicitConfig { @@ -96,6 +97,9 @@ func New(app *fiber.App, config ...Config) *OApiApp { if provided.ValidationErrorHandler != nil { cfg.ValidationErrorHandler = provided.ValidationErrorHandler } + if provided.AuthErrorHandler != nil { + cfg.AuthErrorHandler = provided.AuthErrorHandler + } } oapi := &OApiApp{ @@ -860,6 +864,9 @@ func Method[TInput any, TOutput any, TError any]( // Check for authentication/authorization errors first var authErr *AuthError if errors.As(err, &authErr) { + if app.config.AuthErrorHandler != nil { + return app.config.AuthErrorHandler(c, authErr) + } errType := "authentication_error" if authErr.StatusCode == 403 { errType = "authorization_error" From fd2312383896b7b39e092927647d68abdda3594f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Fri, 13 Mar 2026 18:23:05 +0100 Subject: [PATCH 3/4] Add AuthErrorHandler to Config for custom authentication error handling --- types.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/types.go b/types.go index 1cc6826..99045a8 100644 --- a/types.go +++ b/types.go @@ -57,6 +57,10 @@ type PathInfo struct { // It receives the fiber context and the validation error, and returns a fiber error response type ValidationErrorHandler func(c *fiber.Ctx, err error) error +// AuthErrorHandler is a function type for handling authentication/authorization errors +// It receives the fiber context and the AuthError, and returns a fiber error response +type AuthErrorHandler func(c *fiber.Ctx, err *AuthError) error + // Config represents configuration for the OApi wrapper type Config struct { EnableValidation bool // Enable request validation (default: true) @@ -69,6 +73,7 @@ type Config struct { SecuritySchemes map[string]SecurityScheme // OpenAPI security schemes DefaultSecurity []map[string][]string // Default security requirements ValidationErrorHandler ValidationErrorHandler // Custom handler for validation errors + AuthErrorHandler AuthErrorHandler // Custom handler for auth errors (401/403) } // OpenAPIOptions represents options for OpenAPI operations From 3aa3358ee57b9f5aaa3d459e222ea4366340f9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Fri, 13 Mar 2026 18:23:14 +0100 Subject: [PATCH 4/4] Add custom AuthErrorHandler for improved error responses in authentication --- _examples/auth/main.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/_examples/auth/main.go b/_examples/auth/main.go index 34dee9f..6760cc6 100644 --- a/_examples/auth/main.go +++ b/_examples/auth/main.go @@ -240,6 +240,19 @@ func main() { EnableOpenAPIDocs: true, EnableAuthorization: true, AuthService: authService, + // Custom error handler for authentication/authorization errors + AuthErrorHandler: func(c *fiber.Ctx, err *fiberoapi.AuthError) error { + errType := "authentication_error" + if err.StatusCode == 403 { + errType = "authorization_error" + } + return c.Status(err.StatusCode).JSON(fiber.Map{ + "error": errType, + "message": err.Message, + "status": err.StatusCode, + "service": "fiber-oapi auth example", + }) + }, SecuritySchemes: map[string]fiberoapi.SecurityScheme{ "bearerAuth": { Type: "http",