stacklok / stacklok/toolhive

Add audit logging to authserver

Open
#3,919 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Context

The authserver is the most security-sensitive component in ToolHive (token issuance, client registration, upstream IDP interaction) yet has zero audit logging. The rest of the codebase has a mature NIST SP 800-53 compliant audit system in pkg/audit/ with structured event types, subjects, targets, and outcomes.

Scope

Define auth event types

New EventType constants in pkg/audit/ (new file authserver_events.go, following mcp_events.go pattern). Failures are NOT separate event types — they are captured via the existing Outcome field (success, failure, error, denied) on each event.

Constant Value When emitted
EventTypeOAuthAuthorizeRequest oauth_authorize_request Client initiates authorization. Outcome: success (redirect issued) / denied (invalid client/redirect)
EventTypeOAuthAuthCodeIssued oauth_auth_code_issued Auth code generated after successful upstream callback
EventTypeOAuthTokenIssued oauth_token_issued Access/refresh token issued via authorization_code grant
EventTypeOAuthTokenRefreshed oauth_token_refreshed Token refreshed via refresh_token grant (distinct security properties from initial issuance)
EventTypeOAuthSessionRevoked oauth_session_revoked Session forcibly revoked (e.g., upstream refresh permanently failed with invalid_grant)
EventTypeOAuthClientRegistered oauth_client_registered DCR: new client registered (RFC 7591)
EventTypeOAuthUpstreamAuthComplete oauth_upstream_auth_complete Upstream IDP callback received confirming user authentication
EventTypeOAuthUpstreamTokenRefreshed oauth_upstream_token_refreshed ToolHive transparently refreshed an upstream IDP token on behalf of a session
EventTypeOAuthUserCreated oauth_user_created New internal user provisioned on first login

Plus supporting subject keys (oauth_client_id, upstream_issuer, upstream_subject), target keys (session_id, scope, resource, grant_type, revocation_reason), and component constant (toolhive-authserver).

NIST SP 800-53 AU-2 coverage
AU-2 Category Events covering it
Logon/logoff events oauth_authorize_request, oauth_upstream_auth_complete
Privileged access oauth_client_registered
Non-privileged access oauth_token_issued, oauth_token_refreshed
Account management oauth_user_created, oauth_session_revoked
Third-party interactions oauth_upstream_auth_complete, oauth_upstream_token_refreshed

Future events (oauth_token_introspected, oauth_mtls_auth_attempted, oauth_client_updated/deleted) should be added when their corresponding features land.

Create AuthServerAuditor and wire into handlers

Follow the WorkflowAuditor pattern (pkg/audit/workflow_auditor.go) — a direct auditor that handlers call, rather than generic HTTP middleware. This is preferred because auth operations have rich semantic context (client_id, grant_type, upstream_provider) that HTTP middleware can't capture, and different endpoints need very different audit data.

Wire into each handler:

  • AuthorizeHandler: oauth_authorize_request with subjects={client_id}, target={redirect_uri, scope, resource}
  • CallbackHandler success: oauth_upstream_auth_complete + oauth_auth_code_issued + optionally oauth_user_created
  • CallbackHandler failure: oauth_upstream_auth_complete with outcome=failure/error
  • TokenHandler authorization_code: oauth_token_issued with subjects={client_id, user_id, session_id}, target={grant_type, scope}
  • TokenHandler refresh_token: oauth_token_refreshed with subjects={client_id, user_id}, target={grant_type}
  • TokenHandler failure: relevant event type with outcome=failure
  • RegisterClientHandler: oauth_client_registered with subjects={client_id, client_name}
  • Upstream token refresh (when implemented): oauth_upstream_token_refreshed with target={upstream_issuer, session_id}
  • Session revocation (when implemented): oauth_session_revoked with target={session_id, revocation_reason}

Acceptance criteria

  • Auth event type constants defined in pkg/audit/authserver_events.go
  • AuthServerAuditor created following WorkflowAuditor pattern
  • Auditor injected into Handler via constructor
  • Audit events emitted for all handler success and failure paths
  • Sensitive data (tokens, PKCE verifiers, authorization codes) NEVER included in audit events
  • Source information (client IP, user agent) included in events
  • Configuration follows pkg/audit.Config patterns (enable/disable, event type filtering)
  • Unit tests verify correct event types, subjects, and outcomes per handler path

Dependencies

  • Depends on #3918 (HTTP middleware — request IDs needed for audit event correlation)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading pkg/audit/mcp_events.go and pkg/audit/workflow_auditor.go, then trace AuthorizeHandler, CallbackHandler, TokenHandler, and RegisterClientHandler. Define the event constants and auditor, inject it into Handler, and add tests for success and failure paths; done means all listed events include the required context without sensitive data and follow pkg/audit.Config filtering.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authentication, backend, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.