From b657dfaff74a6bd7179d9adaa6bfe1d3a9ab2d03 Mon Sep 17 00:00:00 2001 From: Bruno Clermont Date: Sun, 14 Jun 2026 13:32:56 -0400 Subject: [PATCH] Add warn-level logging for API authentication failures When the API returns 401 Unauthorized, the log output at info/debug level gave no indication of why the authentication failed. All detail was buried at trace level. Add log.Warn calls at the key auth failure points so operators can see the reason for authentication failures without enabling trace logging: - routers/api/v1/api.go: log method, path, remote IP and reason before returning 401 - services/auth/oauth2.go: log JWT parse failures, grant-not-found and token expiry at Warn level - services/auth/basic.go: log when no token type matched instead of returning nil silently Assisted-by: claude-sonnet-4-6 --- routers/api/v1/api.go | 1 + services/auth/basic.go | 1 + services/auth/oauth2.go | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 3bac1eac91..6673f88fa6 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -785,6 +785,7 @@ func apiAuth(authMethod auth.Method) func(*context.APIContext) { if err != nil { msg, ok := auth.ErrAsUserAuthMessage(err) msg = util.Iif(ok, msg, "invalid username, password or token") + log.Warn("API auth failure: method=%s path=%s ip=%s reason=%q", ctx.Req.Method, ctx.Req.URL.Path, ctx.RemoteAddr(), msg) ctx.APIError(http.StatusUnauthorized, msg) return } diff --git a/services/auth/basic.go b/services/auth/basic.go index c7db14e6e7..0607480443 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -115,6 +115,7 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store store.GetData()["LoginMethod"] = ActionTokenMethodName return user_model.NewActionsUserWithTaskID(task.ID), nil } + log.Warn("Basic Authorization: token not found for any known token type") return nil, nil //nolint:nilnil // the auth method is not applicable } diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index a2f7d5d1e7..e7bd03e83d 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -39,17 +39,19 @@ func GetOAuthAccessTokenScopeAndUserID(ctx context.Context, accessToken string) token, err := oauth2_provider.ParseToken(accessToken, oauth2_provider.DefaultSigningKey) if err != nil { - log.Trace("oauth2.ParseToken: %v", err) + log.Warn("oauth2.ParseToken: %v", err) return accessTokenScope, 0 } var grant *auth_model.OAuth2Grant if grant, err = auth_model.GetOAuth2GrantByID(ctx, token.GrantID); err != nil || grant == nil { + log.Warn("oauth2: grant not found for token grantID=%d: %v", token.GrantID, err) return accessTokenScope, 0 } if token.Kind != oauth2_provider.KindAccessToken { return accessTokenScope, 0 } if token.ExpiresAt.Before(time.Now()) || token.IssuedAt.After(time.Now()) { + log.Warn("oauth2: token expired or not yet valid, grantID=%d expiresAt=%v issuedAt=%v", token.GrantID, token.ExpiresAt, token.IssuedAt) return accessTokenScope, 0 } accessTokenScope = oauth2_provider.GrantAdditionalScopes(grant.Scope)