Skip to content

Add GitHub as OAuth Identity Provider #73

@BorisTyshkevich

Description

@BorisTyshkevich

Problem

GitHub OAuth is not standard OIDC — it lacks .well-known/openid-configuration discovery, issues opaque access tokens (not JWTs), never returns an id_token, and uses non-standard user API claims (id/login instead of sub, no email_verified). The current OAuth implementation nearly supports GitHub via the fetchUserInfo fallback path, but four gaps prevent it from working end-to-end.

Why GitHub?

GitHub is one of the most widely used identity providers for developer tooling. Supporting it broadens the MCP server's audience, especially for teams already using GitHub for authentication across their infrastructure.

What doesn't work today

Gap Detail
Claim mapping GitHub's /user returns id (int) and login instead of OIDC-standard sub. oauthClaimsFromUserInfo produces an empty Subject.
Email verification GitHub's /user endpoint has no email_verified field. require_email_verified: true rejects all GitHub users. Verified email status requires a separate GET /user/emails call.
Private emails GitHub may return email: null when the user's email is private — again requiring /user/emails.
Accept headers Some GitHub API endpoints benefit from explicit Accept: application/json headers.

Proposed approach: generic enhancements (no GitHub-specific code)

All changes are provider-agnostic — no if provider == "github" anywhere. This benefits any future non-OIDC OAuth2 provider (Bitbucket, GitLab self-hosted, custom APIs).

1. New config fields in OAuthConfig (pkg/config/config.go)

Field Type Purpose
userinfo_claims_mapping map[string]string Maps upstream fields to standard OIDC claims (e.g., {"id": "sub", "login": "preferred_username"})
userinfo_email_url string Secondary endpoint for verified email (e.g., https://api.github.com/user/emails)
userinfo_accept_header string Accept header for userinfo requests
token_request_accept_header string Accept header for token exchange requests

2. Claim mapping in oauthClaimsFromUserInfo (cmd/altinity-mcp/oauth_server.go)

  • Add claimsMapping map[string]string parameter
  • Before extracting standard fields, apply mapping: copy raw[srcKey]raw[dstKey] if target not already set
  • Handle numeric id (float64) → string conversion for sub field

3. New helper: fetchVerifiedEmail

  • GET to userinfo_email_url with Authorization: Bearer {token}
  • Parses JSON array of {email, primary, verified} objects (GitHub's /user/emails format)
  • Returns first primary=true && verified=true email

4. Enhance fetchUserInfo

  • Add Accept header when userinfo_accept_header is configured
  • Pass userinfo_claims_mapping to claim extraction
  • After claim extraction, if email missing/unverified and userinfo_email_url configured, call fetchVerifiedEmail

5. Token exchange Accept header (handleOAuthCallback)

  • Replace PostForm with manual http.NewRequest to support configurable Accept header

GitHub config example

server:
  oauth:
    enabled: true
    mode: "gating"
    gating_secret_key: "<RANDOM_SECRET>"
    issuer: "https://github.com"
    client_id: "<GITHUB_CLIENT_ID>"
    client_secret: "<GITHUB_CLIENT_SECRET>"
    auth_url: "https://github.com/login/oauth/authorize"
    token_url: "https://github.com/login/oauth/access_token"
    userinfo_url: "https://api.github.com/user"
    userinfo_email_url: "https://api.github.com/user/emails"
    userinfo_accept_header: "application/json"
    token_request_accept_header: "application/json"
    scopes: ["user:email"]
    userinfo_claims_mapping:
      id: "sub"
      login: "preferred_username"
    require_email_verified: true

Note: Gating mode is required because GitHub issues opaque tokens that ClickHouse cannot validate directly.

Files to modify

  • pkg/config/config.go — Add 4 new OAuthConfig fields
  • cmd/altinity-mcp/oauth_server.go — Core logic (claim mapping, email fetch, Accept headers)
  • cmd/altinity-mcp/oauth_server_test.go — Unit + integration tests
  • docs/oauth_authorization.md — GitHub provider docs + general non-OIDC section
  • helm/altinity-mcp/values_examples/mcp-oauth-github.yaml — New Helm example

Test plan

  • Unit: claim mapping with/without config, numeric ID conversion, mapping doesn't overwrite existing fields, email fetch success/failure
  • Integration: mock GitHub-like upstream (opaque token, /user with id/login, /user/emails with verified array) — full callback flow
  • Regression: all existing OAuth tests pass unchanged (nil mapping = backward compatible)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions