Thread ctx through auth middleware DB lookups
The auth middleware's m.db.Preload + m.db.First calls were running without ctx, so on cache miss the resulting SQL queries appeared as orphan gorm.Query / gorm.Row spans in Jaeger. Now they nest under the parent HTTP request span like every other repo call. This was the last orphaned-SQL source on the request hot path. Combined with the seven service migrations, every authenticated API call now produces a fully-nested flame graph: HTTP → auth-token-lookup (cache hit) or HTTP → auth-token-SQL (cache miss) → service → service-SQL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -104,7 +104,7 @@ func (m *AuthMiddleware) TokenAuth() echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cache miss - look up token in database
|
// Cache miss - look up token in database
|
||||||
user, authToken, err := m.getUserFromDatabaseWithToken(token)
|
user, authToken, err := m.getUserFromDatabaseWithToken(c.Request().Context(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Err(err).Str("token", truncateToken(token)).Msg("Token authentication failed")
|
log.Debug().Err(err).Str("token", truncateToken(token)).Msg("Token authentication failed")
|
||||||
return apperrors.Unauthorized("error.invalid_token")
|
return apperrors.Unauthorized("error.invalid_token")
|
||||||
@@ -148,7 +148,7 @@ func (m *AuthMiddleware) OptionalTokenAuth() echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try database
|
// Try database
|
||||||
user, authToken, err := m.getUserFromDatabaseWithToken(token)
|
user, authToken, err := m.getUserFromDatabaseWithToken(c.Request().Context(), token)
|
||||||
if err == nil && !m.isTokenExpired(authToken.Created) {
|
if err == nil && !m.isTokenExpired(authToken.Created) {
|
||||||
m.cacheTokenInfo(c.Request().Context(), token, user.ID, authToken.Created)
|
m.cacheTokenInfo(c.Request().Context(), token, user.ID, authToken.Created)
|
||||||
c.Set(AuthUserKey, user)
|
c.Set(AuthUserKey, user)
|
||||||
@@ -224,7 +224,7 @@ func (m *AuthMiddleware) getUserFromCache(ctx context.Context, token string) (*m
|
|||||||
|
|
||||||
// In-memory cache miss — fetch from database
|
// In-memory cache miss — fetch from database
|
||||||
var user models.User
|
var user models.User
|
||||||
if err := m.db.First(&user, userID).Error; err != nil {
|
if err := m.db.WithContext(ctx).First(&user, userID).Error; err != nil {
|
||||||
// User was deleted - invalidate caches
|
// User was deleted - invalidate caches
|
||||||
m.cache.InvalidateAuthToken(ctx, token)
|
m.cache.InvalidateAuthToken(ctx, token)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -242,10 +242,11 @@ func (m *AuthMiddleware) getUserFromCache(ctx context.Context, token string) (*m
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getUserFromDatabaseWithToken looks up the token in the database and returns
|
// getUserFromDatabaseWithToken looks up the token in the database and returns
|
||||||
// both the user and the auth token record (for expiry checking).
|
// both the user and the auth token record (for expiry checking). The ctx is
|
||||||
func (m *AuthMiddleware) getUserFromDatabaseWithToken(token string) (*models.User, *models.AuthToken, error) {
|
// threaded into the GORM session so the SQL span attaches to the request trace.
|
||||||
|
func (m *AuthMiddleware) getUserFromDatabaseWithToken(ctx context.Context, token string) (*models.User, *models.AuthToken, error) {
|
||||||
var authToken models.AuthToken
|
var authToken models.AuthToken
|
||||||
if err := m.db.Preload("User").Where("key = ?", token).First(&authToken).Error; err != nil {
|
if err := m.db.WithContext(ctx).Preload("User").Where("key = ?", token).First(&authToken).Error; err != nil {
|
||||||
return nil, nil, fmt.Errorf("token not found")
|
return nil, nil, fmt.Errorf("token not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,8 +263,8 @@ func (m *AuthMiddleware) getUserFromDatabaseWithToken(token string) (*models.Use
|
|||||||
// getUserFromDatabase looks up the token in the database and caches the
|
// getUserFromDatabase looks up the token in the database and caches the
|
||||||
// resulting user record in memory.
|
// resulting user record in memory.
|
||||||
// Deprecated: Use getUserFromDatabaseWithToken for new code paths that need expiry checking.
|
// Deprecated: Use getUserFromDatabaseWithToken for new code paths that need expiry checking.
|
||||||
func (m *AuthMiddleware) getUserFromDatabase(token string) (*models.User, error) {
|
func (m *AuthMiddleware) getUserFromDatabase(ctx context.Context, token string) (*models.User, error) {
|
||||||
user, _, err := m.getUserFromDatabaseWithToken(token)
|
user, _, err := m.getUserFromDatabaseWithToken(ctx, token)
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user