Skip to content

Lint enforcement: ban magic-string "user_profile_id" outside AuthClaimTypes #202

@davidortinau

Description

@davidortinau

Context

Per the AuthClaimTypes decision (2026-05-08), all custom JWT claim names live in SentenceStudio.Contracts.AuthClaimTypes as public const string fields. Producers (Api JwtTokenService / DevAuthHandler, WebApp ServerAuthService) and consumers (every endpoint that scopes by user, plus tests) must reference the constant.

The current sweep is correct on main as of e341484, but nothing prevents a future PR from reintroducing a literal like User.FindFirst("user_profile_id") or new Claim("user_profile_id", ...). A typo (e.g. "user_profileid") silently degrades to anonymous-user behavior with no compile-time warning — a production-only bug.

Desired enforcement

Add a CI gate that fails when the literal "user_profile_id" appears in any .cs file under src/ or tests/ except the constant definition in src/SentenceStudio.Contracts/AuthClaimTypes.cs.

Two acceptable approaches — pick whichever is cheapest to maintain:

Option A — Roslyn analyzer (preferred long-term):

  • New analyzer project (or extend an existing one) that fires a diagnostic on any LiteralExpressionSyntax whose value matches a configured set of banned claim names.
  • Configurable via .editorconfig or analyzer settings so adding new banned literals is a one-line change.
  • Wire as <Analyzer> in Directory.Build.props so every project picks it up.

Option B — Unit test grep gate (cheaper to ship):

  • New xUnit test (in tests/SentenceStudio.Api.Tests/ or a dedicated lint test project) that walks src/ and tests/ .cs files and asserts the literal does not appear outside the allowlisted AuthClaimTypes.cs.
  • Runs as part of the normal dotnet test pass, so CI catches it.
  • Easy to extend to other banned literals later.

Whichever path is chosen, the rule should ideally read its banned set from the AuthClaimTypes constant class (via reflection) rather than a hardcoded copy, so adding a new claim constant automatically protects the new literal.

Acceptance criteria

  • Adding a literal "user_profile_id" to any non-allowlisted .cs file fails the build or dotnet test.
  • The check is mechanical and runs in CI on every PR.
  • False positives are limited to the single allowlisted file (AuthClaimTypes.cs) and any documentation/string-resource use cases (which can be allowlisted explicitly).
  • Adding a new claim constant to AuthClaimTypes automatically protects the new literal — or, at minimum, the registration is one line in the analyzer/test config.

Related

  • Decision: .squad/decisions/processed/2026-05-08/kaylee-authclaimtypes-constants.md
  • Most recent sweep: commit e341484 (refactor(auth): centralize AuthClaimTypes in SentenceStudio.Contracts)
  • Original review that surfaced the issue: commit 398a769 (Wash's api-endpoint-review-checklist SIGNIFICANT Fix ContinueConversation method typo #5)

Out of scope

  • Implementing the analyzer/test in this issue (filing for tracking; not doing the work today).
  • Banning unrelated magic strings — scope is JWT claim names only for now.

Metadata

Metadata

Assignees

No one assigned

    Labels

    go:needs-researchNeeds investigationpriority:p2Next sprintsquadSquad triage inbox — Lead will assign to a membersquad:washAssigned to Wash (Backend Dev)type:choreMaintenance, refactoring, cleanup

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions