fix: set authenticated user on request context in bearer token auth

tryBearerAuth validated the bearer token but never looked up the
associated user or set it on the request context. This meant
downstream handlers calling GetCurrentUser would get nil even
with a valid token.

Changes:
- Add ContextWithUser/UserFromContext helpers in auth package
- tryBearerAuth now looks up the user by token's UserID and
  sets it on the request context via auth.ContextWithUser
- GetCurrentUser checks context first before falling back to
  session cookie
- Add integration tests for bearer auth user context
This commit is contained in:
clawbot
2026-02-19 13:54:09 -08:00
committed by user
parent 7387ba6b5c
commit 96eea71c54
3 changed files with 203 additions and 9 deletions

View File

@@ -386,8 +386,8 @@ func (m *Middleware) APISessionAuth() func(http.Handler) http.Handler {
request *http.Request,
) {
// Try Bearer token first.
if m.tryBearerAuth(request) {
next.ServeHTTP(writer, request)
if authedReq, ok := m.tryBearerAuth(request); ok {
next.ServeHTTP(writer, authedReq)
return
}
@@ -457,16 +457,19 @@ func (m *Middleware) SetupRequired() func(http.Handler) http.Handler {
}
// tryBearerAuth checks for a valid Bearer token in the
// Authorization header.
func (m *Middleware) tryBearerAuth(request *http.Request) bool {
// Authorization header. On success it returns a new request
// with the authenticated user set on the context.
func (m *Middleware) tryBearerAuth(
request *http.Request,
) (*http.Request, bool) {
authHeader := request.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, bearerPrefix) {
return false
return request, false
}
rawToken := strings.TrimPrefix(authHeader, bearerPrefix)
if rawToken == "" {
return false
return request, false
}
tokenHash := database.HashAPIToken(rawToken)
@@ -475,15 +478,26 @@ func (m *Middleware) tryBearerAuth(request *http.Request) bool {
request.Context(), m.params.Database, tokenHash,
)
if err != nil || apiToken == nil {
return false
return request, false
}
if apiToken.IsExpired() {
return false
return request, false
}
// Look up the user associated with the token.
user, err := models.FindUser(
request.Context(), m.params.Database, apiToken.UserID,
)
if err != nil || user == nil {
return request, false
}
// Update last_used_at (best effort).
_ = apiToken.TouchLastUsed(request.Context())
return true
// Set the authenticated user on the request context.
ctx := auth.ContextWithUser(request.Context(), user)
return request.WithContext(ctx), true
}