Middleware returns an Echo middleware that handles authentication. Resolution order: 1. If auth not enabled AND no legacy API keys → pass through 2. Skip auth for exempt paths (PathWithoutAuth + /api/auth/) 3. If auth enabled (db != nil): a. Try "session" cookie → DB lookup b. Try Authorization: Be
(db *gorm.DB, appConfig *config.ApplicationConfig)
| 37 | // 5. If no auth found for /api/ or /v1/ paths → 401 |
| 38 | // 6. Otherwise pass through (static assets, UI pages, etc.) |
| 39 | func Middleware(db *gorm.DB, appConfig *config.ApplicationConfig) echo.MiddlewareFunc { |
| 40 | return func(next echo.HandlerFunc) echo.HandlerFunc { |
| 41 | return func(c echo.Context) error { |
| 42 | authEnabled := db != nil |
| 43 | hasLegacyKeys := len(appConfig.ApiKeys) > 0 |
| 44 | |
| 45 | // 1. No auth at all |
| 46 | if !authEnabled && !hasLegacyKeys { |
| 47 | return next(c) |
| 48 | } |
| 49 | |
| 50 | path := c.Request().URL.Path |
| 51 | exempt := isExemptPath(path, appConfig) |
| 52 | authenticated := false |
| 53 | |
| 54 | // 2. Try to authenticate (populates user in context if possible) |
| 55 | if authEnabled { |
| 56 | user := tryAuthenticate(c, db, appConfig) |
| 57 | if user != nil { |
| 58 | c.Set(contextKeyUser, user) |
| 59 | c.Set(contextKeyRole, user.Role) |
| 60 | authenticated = true |
| 61 | |
| 62 | // Session rotation for cookie-based sessions |
| 63 | if session, ok := c.Get("_auth_session").(*Session); ok { |
| 64 | MaybeRotateSession(c, db, session, appConfig.Auth.APIKeyHMACSecret) |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | // 3. Legacy API key validation (works whether auth is enabled or not) |
| 70 | if !authenticated && hasLegacyKeys { |
| 71 | key := extractKey(c) |
| 72 | if key != "" && isValidLegacyKey(key, appConfig) { |
| 73 | syntheticUser := &User{ |
| 74 | ID: "legacy-api-key", |
| 75 | Name: "API Key User", |
| 76 | Role: RoleAdmin, |
| 77 | } |
| 78 | c.Set(contextKeyUser, syntheticUser) |
| 79 | c.Set(contextKeyRole, RoleAdmin) |
| 80 | c.Set(contextKeySource, UsageSourceLegacy) |
| 81 | authenticated = true |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | // 4. If authenticated or exempt path, proceed |
| 86 | if authenticated || exempt { |
| 87 | return next(c) |
| 88 | } |
| 89 | |
| 90 | // 5. Require auth for API paths |
| 91 | if isAPIPath(path) { |
| 92 | // Check GET exemptions for legacy keys |
| 93 | if hasLegacyKeys && appConfig.DisableApiKeyRequirementForHttpGet && c.Request().Method == http.MethodGet { |
| 94 | for _, rx := range appConfig.HttpGetExemptedEndpoints { |
| 95 | if rx.MatchString(c.Path()) { |
| 96 | return next(c) |