Skip to content

[duplicate-code] Duplicate Code Pattern: Bearer/Agent Header Prefix Parsing in auth/header.go #3109

@github-actions

Description

@github-actions

Part of duplicate code analysis: #3108

Summary

ParseAuthHeader and ExtractSessionID in internal/auth/header.go both contain near-identical logic for detecting and stripping the "Bearer " and "Agent " scheme prefixes from Authorization header values. This 16-line block is duplicated verbatim, differing only in one strings.TrimSpace call.

Duplication Details

Pattern: Repeated Bearer/Agent Scheme Prefix Detection

  • Severity: High
  • Occurrences: 2 instances in the same file
  • Locations:
    • internal/auth/header.go (lines 75–88, inside ParseAuthHeader)
    • internal/auth/header.go (lines 151–163, inside ExtractSessionID)

Instance 1 — ParseAuthHeader (lines 75–88):

// Handle "Bearer <token>" format (backward compatibility)
if strings.HasPrefix(authHeader, "Bearer ") {
    log.Print("Detected Bearer token format (backward compatibility)")
    token := strings.TrimPrefix(authHeader, "Bearer ")
    return token, token, nil
}

// Handle "Agent <agent-id>" format
if strings.HasPrefix(authHeader, "Agent ") {
    log.Print("Detected Agent ID format")
    agentIDValue := strings.TrimPrefix(authHeader, "Agent ")
    return agentIDValue, agentIDValue, nil
}

Instance 2 — ExtractSessionID (lines 151–163):

// Handle "Bearer <token>" format (backward compatibility)
// Trim spaces for backward compatibility with older clients
if strings.HasPrefix(authHeader, "Bearer ") {
    log.Print("Detected Bearer format, trimming spaces for backward compatibility")
    sessionID := strings.TrimPrefix(authHeader, "Bearer ")
    return strings.TrimSpace(sessionID)
}

// Handle "Agent <agent-id>" format
if strings.HasPrefix(authHeader, "Agent ") {
    log.Print("Detected Agent format")
    return strings.TrimPrefix(authHeader, "Agent ")
}

The only behavioral difference is that ExtractSessionID calls strings.TrimSpace on Bearer tokens.

Impact Analysis

  • Maintainability: If a new header scheme is added (e.g., "Token " for a future spec version), it must be added in two places; a missed update would cause inconsistent behavior between session extraction and full auth parsing
  • Bug Risk: High — the two functions are in a security-critical code path. Discrepancies in scheme handling between them could result in auth bypass or session mismatch vulnerabilities
  • Code Bloat: Minor (~16 lines), but concentrated in a file already well-structured with clear doc comments

Refactoring Recommendations

  1. Extract a private stripAuthScheme helper inside internal/auth/header.go:

    // stripAuthScheme extracts the value from a scheme-prefixed Authorization header.
    // Recognizes "Bearer <value>" and "Agent <value>" formats.
    // Returns (scheme, value, true) on match, or ("", authHeader, false) for plain values.
    func stripAuthScheme(authHeader string) (scheme, value string, matched bool) {
        for _, prefix := range []string{"Bearer ", "Agent "} {
            if strings.HasPrefix(authHeader, prefix) {
                scheme = strings.TrimSuffix(prefix, " ")
                value = strings.TrimPrefix(authHeader, prefix)
                return scheme, value, true
            }
        }
        return "", authHeader, false
    }

    Then both ParseAuthHeader and ExtractSessionID can call stripAuthScheme and apply their own post-processing (e.g., TrimSpace in ExtractSessionID).

  2. Or, have ExtractSessionID delegate to ParseAuthHeader since ExtractAgentID already does this pattern — though the TrimSpace divergence would need to be resolved first.

Implementation Checklist

  • Review both functions to confirm the TrimSpace difference is intentional
  • Extract stripAuthScheme private helper (or equivalent)
  • Update ParseAuthHeader to use the helper
  • Update ExtractSessionID to use the helper
  • Verify all existing tests pass
  • Add a test case for each recognized scheme in the helper

Parent Issue

See parent analysis report: #3108
Related to #3108

Generated by Duplicate Code Detector ·

  • expires on Apr 10, 2026, 6:01 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions