diff --git a/pkg/user/auth_service.go b/pkg/user/auth_service.go index f876d14..c10c15f 100644 --- a/pkg/user/auth_service.go +++ b/pkg/user/auth_service.go @@ -106,7 +106,7 @@ func (s *userServiceImpl) GenerateJWT(ctx context.Context, user *User) (string, // Use the most recently added secret (last in the list) // This ensures new tokens are signed with the latest secret signingSecret := validSecrets[len(validSecrets)-1].Secret - log.Trace().Ctx(ctx).Str("signing_secret", signingSecret).Bool("is_primary", validSecrets[len(validSecrets)-1].IsPrimary).Msg("Generating JWT with latest secret") + log.Trace().Ctx(ctx).Str("signing_secret_fp", tokenFingerprint(signingSecret)).Bool("is_primary", validSecrets[len(validSecrets)-1].IsPrimary).Msg("Generating JWT with latest secret") // Sign and get the complete encoded token as a string tokenString, err := token.SignedString([]byte(signingSecret)) @@ -114,20 +114,20 @@ func (s *userServiceImpl) GenerateJWT(ctx context.Context, user *User) (string, return "", fmt.Errorf("failed to sign JWT: %w", err) } - log.Trace().Ctx(ctx).Str("token", tokenString).Msg("Generated JWT token") + log.Trace().Ctx(ctx).Str("token_fp", tokenFingerprint(tokenString)).Int("token_len", len(tokenString)).Msg("Generated JWT token") return tokenString, nil } // ValidateJWT validates a JWT token and returns the user func (s *userServiceImpl) ValidateJWT(ctx context.Context, tokenString string) (*User, error) { - log.Trace().Ctx(ctx).Str("token", tokenString).Msg("Validating JWT token") + log.Trace().Ctx(ctx).Str("token_fp", tokenFingerprint(tokenString)).Int("token_len", len(tokenString)).Msg("Validating JWT token") // Get all valid secrets for validation validSecrets := s.secretManager.GetAllValidSecrets() log.Trace().Ctx(ctx).Int("num_secrets", len(validSecrets)).Msg("Validating JWT with multiple secrets") for i, secret := range validSecrets { - log.Trace().Ctx(ctx).Int("secret_index", i).Str("secret", secret.Secret).Bool("is_primary", secret.IsPrimary).Msg("Trying secret") + log.Trace().Ctx(ctx).Int("secret_index", i).Str("secret_fp", tokenFingerprint(secret.Secret)).Bool("is_primary", secret.IsPrimary).Msg("Trying secret") } // Try each valid secret until we find one that works @@ -146,7 +146,7 @@ func (s *userServiceImpl) ValidateJWT(ctx context.Context, tokenString string) ( }) if err == nil && token.Valid { - log.Trace().Ctx(ctx).Int("secret_index", i).Str("secret", secret.Secret).Msg("JWT validation successful") + log.Trace().Ctx(ctx).Int("secret_index", i).Str("secret_fp", tokenFingerprint(secret.Secret)).Msg("JWT validation successful") parsedToken = token break } @@ -154,7 +154,7 @@ func (s *userServiceImpl) ValidateJWT(ctx context.Context, tokenString string) ( // Store the last error for reporting validationError = err if err != nil { - log.Trace().Ctx(ctx).Int("secret_index", i).Str("secret", secret.Secret).Err(err).Msg("JWT validation failed") + log.Trace().Ctx(ctx).Int("secret_index", i).Str("secret_fp", tokenFingerprint(secret.Secret)).Err(err).Msg("JWT validation failed") } } @@ -351,3 +351,13 @@ func (s *PasswordResetServiceImpl) CompletePasswordReset(ctx context.Context, us // Complete the password reset return s.repo.CompletePasswordReset(ctx, username, hashedPassword) } + +// tokenFingerprint returns the first 16 hex chars of SHA-256 hash of a token/secret. +// Used for safe logging correlation without leaking sensitive values. +func tokenFingerprint(tok string) string { + if tok == "" { + return "" + } + sum := sha256.Sum256([]byte(tok)) + return hex.EncodeToString(sum[:8]) // 16 hex chars = 8 bytes +}