klinux / klinux/gocdnext

design: approval identity + group resolution when gocdnext runs behind an external platform/IdP

Open
#172 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
7
Forks
3
Avg merge
2h 12m
Merged PRs (30d)
30

Description

Context

gocdnext is being embedded into an internal developer platform that fronts it for users. That raises two questions the approval gate depends on: who is the acting identity, and where does group membership come from.

Today (server/internal/store/approvals.go:176-205):

  • approvers is matched by string — the display label (User) or the email. Under OIDC the name claim wins as User, which is the drift the code already documents (#51).
  • approver_groups is matched by UserID against gocdnext's local group_members, and the code correctly calls this "the most robust path, immune to name/email string drift".
  • Quorum accounting only runs when the caller supplied a UserID — the authenticated path. Anonymous callers skip the vote table entirely.

The failure mode to design against

A platform approving on behalf of users with a service token. If the UserID reaching gocdnext is the platform's, the gate becomes theatre: quorum counts the service, the audit records the service, and the question a gate exists to answer — which human authorised this — has no answer. Same class as the webhook-drift path having no actor.

Corollary: no caller-asserted identity (an X-On-Behalf-Of-style header). Anyone holding the platform's token could then approve as anyone.

Proposed direction

  1. Shared OIDC rather than delegation. The platform and gocdnext trust the same issuer; the user's own session reaches gocdnext. This is configuration, not a feature — OIDC login already exists. Approvals keep a real UserID, quorum works, the audit names the human.

    Where the platform genuinely must proxy the API, require token exchange (RFC 8693): it exchanges for a token minted for the user. The identity assertion is then signed by the IdP, not asserted by the proxy.

  2. One source of truth for groups, resolved at vote time. If the platform owns teams, mirroring them into group_members invites silent drift and the gate ends up enforcing the stale copy.

    Worth distinguishing: token claims are fine for UI/navigation; authority should be resolved at the moment of decision. A token minted before someone left a team still carries the group. For the vote — the security-relevant instant — re-resolve membership against the IdP/platform instead of trusting the login-time snapshot.

  3. Match approvers by a stable subject, not display strings. This is the debt the integration amplifies: another source of display names. Referencing the OIDC sub (or the gocdnext UserID) makes the allow-list immune to rename/email changes — which is exactly why the group path is already the robust one.

  4. Keep the local group table as a fallback. Standalone and air-gapped installs still need it; external resolution should be an additional source, not a replacement.

Open questions

  • Does the platform own approver policy (who may approve what) or only identity? If policy, the gate's approvers/approver_groups may need to become a reference to an external decision rather than an inline list.
  • How should a resolution failure behave — fail closed (refuse the vote) or fall back to the local table? Fail-closed matches the project's posture elsewhere, but it couples approvals to the platform's availability.
  • Rollout gates reuse this engine (ADR-0001 Phase 2), and their approve/reject route is deliberately viewer-level, authorised by the allow-list rather than by role. Any change here must preserve that separation: approval authority is independent of operational authority.

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 with server/internal/store/approvals.go:176-205 and trace the approval and quorum paths, including how UserID, approvers, approver_groups, and audit identity are populated. Define the integration behavior for shared OIDC, token exchange, vote-time group resolution, stable subject matching, and local fallback, while resolving the listed failure and rollout questions before implementation.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.