Skip to content

For discussion : solution direction for automatic user attribution for Copilot coding agent sessions in reusable workflows #20413

@mvdbos

Description

@mvdbos

For discussion : solution direction for automatic user attribution for Copilot coding agent sessions in reusable workflows

Summary

I would like to discuss a mechanism for engine: copilot (gh-aw) workflows to attribute Copilot coding agent premium requests to the user who triggered the workflow — without requiring a user-specific COPILOT_GITHUB_TOKEN PAT to be stored as a repository secret.

One possible approach I've explored is leveraging the OIDC actor / actor_id claims already present in the GitHub Actions runtime. I describe this in detail below as a starting point for discussion, while recognising that GitHub may have better approaches to solve this problem. This solution would also require changes beyond gh-aw, but it feels like this is the right repo to start the discussion from.


Context: platform-orchestrated coding agent sessions

An internal developer platform that provides standardised coding agent workflows to application teams across the organisation. The platform is hosted in a central repository and exposes reusable workflows that consumer teams invoke via workflow_call with secrets: inherit.

The flow:

  1. A developer posts a slash command on an issue in their team's repository.
  2. A thin caller workflow in the consumer repo triggers a reusable workflow in the platform repo via workflow_call.
  3. The reusable workflow runs an engine: copilot (gh-aw) coding agent session — the agent reads the issue, writes code, and creates a pull request.
  4. The developer reviews and merges.

Multiple teams across the organisation use the same platform workflows.


The problem: cost attribution for platform-orchestrated sessions

When a developer uses Copilot directly — assigning @copilot to an issue, using Copilot Chat, or accepting completions — premium requests are automatically attributed to that developer. No configuration is needed.

Platform-orchestrated sessions break this model. The engine: copilot runtime authenticates the coding agent session using COPILOT_GITHUB_TOKEN, a secret that must be present in the repository where the workflow executes. This token is a user-specific PAT — premium requests are attributed to whoever owns the token, not to the developer who triggered the run.

In a reusable workflow (workflow_call) scenario:

  • If the consumer repo provides the token via secrets: inherit, the session bills to one team member whose PAT is stored as the secret — not the triggering user.
  • If the platform repo provides the token, all sessions across all teams bill to a single platform account.

Neither option provides accurate per-user attribution.

How the current authentication chain works

The chain from a gh-aw workflow to a billed Copilot session involves three distinct systems:

gh-aw (compiler + runtime)  →  Copilot CLI  →  GitHub Copilot API (backend)
Layer What it does with COPILOT_GITHUB_TOKEN
gh-aw compiler Emits the validate_multi_secret.sh step into the compiled .lock.yml. Hard-fails if the secret is absent.
gh-aw runtime Passes COPILOT_GITHUB_TOKEN to the Copilot CLI as the auth credential when invoking engine: copilot.
Copilot CLI Uses the token as a Bearer token to authenticate against the GitHub API (api.github.com/copilot/...).
Copilot API backend Validates the token, resolves the token owner's user identity, checks Copilot license + premium request quota, and attributes billing to that user.

The billing decision — who pays — is made at the last layer (Copilot API backend), based on token ownership. Every layer upstream simply passes the token through. This means any solution will require changes across multiple layers of this chain.


Current workaround (functional but not ideal)

Our current approach requires each consumer team to store a COPILOT_GITHUB_TOKEN in their repository secrets. This is a user-specific fine-grained PAT with the Copilot Requests permission, passed to the platform workflow via secrets: inherit.

# Consumer repo: .github/workflows/platform-caller.yml
name: Platform Agent
on:
  issue_comment:
    types: [created]

jobs:
  agent:
    if: contains(github.event.comment.body, '/run-agent')
    uses: org/platform-repo/.github/workflows/agent.lock.yml@main
    secrets: inherit

Why this is not ideal

Concern Detail
Org policy friction Many enterprise organisations restrict PAT creation. Requiring every consumer team to create and manage a PAT conflicts with these policies.
Attribution is per-team, not per-user One team member's PAT absorbs all sessions for the entire team. GitHub's billing reports show that person as the consumer, not the actual triggering developer.
Active rotation required Teams must rotate the PAT not only when someone leaves, but also when that person's premium request allowance is exhausted while other team members still have capacity.
Alternative: service accounts Dedicated service accounts per team eliminate key-person risk, but require an additional Copilot license for every consuming team plus governance overhead to provision and maintain them.

None of these concerns exist for normal @copilot usage, where GitHub attributes sessions to the triggering user automatically.


What we're aiming for

A way for engine: copilot sessions to automatically attribute premium requests to the user who triggered the workflow, without requiring that user's PAT to be stored as a repository secret. The desired end state:

  • Per-user attribution — identical to how native @copilot usage is billed today.
  • Zero secret management — no PATs, no service accounts, no rotation.
  • No consumer repo configuration — beyond the caller workflow file itself.

We don't prescribe a specific implementation. GitHub may have approaches we haven't considered. Below we sketch one possible solution based on OIDC as a starting point for discussion.


One possible approach: OIDC-based user attribution

The insight

The GitHub Actions OIDC provider (token.actions.githubusercontent.com) already issues tokens that contain the triggering user's identity:

{
  "actor": "octocat",
  "actor_id": "583231",
  "repository": "org/consumer-repo",
  "repository_id": "123456789",
  "job_workflow_ref": "org/platform-repo/.github/workflows/agent.lock.yml@refs/heads/main",
  ...
}

The actor and actor_id claims identify the developer who triggered the workflow — the person who posted the slash command. This information is already available to the GitHub Actions runtime at the point where the engine: copilot session is initiated.

How this could work

Allow the engine: copilot runtime to attribute the coding agent session's premium requests to the user identified by actor / actor_id, instead of requiring a user-specific COPILOT_GITHUB_TOKEN.

Conceptually, the flow could look like:

sequenceDiagram
    participant Dev as Developer (actor)
    participant Consumer as Consumer Repo
    participant Platform as Platform Workflow (workflow_call)
    participant Runtime as engine: copilot runtime
    participant OIDC as GitHub OIDC Provider
    participant CLI as Copilot CLI
    participant API as Copilot API Backend

    Dev->>Consumer: Posts slash command on issue
    Consumer->>Platform: workflow_call (secrets: inherit)
    Platform->>Runtime: engine: copilot step begins
    Runtime->>OIDC: Request OIDC token (id-token: write)
    OIDC-->>Runtime: JWT with actor=octocat, actor_id=583231
    Runtime->>CLI: Invoke with OIDC token (no COPILOT_GITHUB_TOKEN)
    CLI->>API: Initiate coding session (present OIDC token as user attestation)
    API->>API: Verify: actor has Copilot license? Verify: actor has premium request capacity?
    API-->>CLI: Session authorised, attributed to octocat
    CLI-->>Runtime: Session complete
    Runtime->>Consumer: Creates PR (attributed to octocat)
Loading

What this approach would touch

Based on our understanding of the authentication chain, this approach would require changes at multiple layers. The GitHub Actions OIDC provider already emits the required claims — no changes needed there.

Component Change required
gh-aw compiler Stop hard-failing when COPILOT_GITHUB_TOKEN is absent. Support an OIDC fallback mode: when the secret is missing but permissions: id-token: write is set, compile the workflow to request an OIDC token instead.
gh-aw runtime When in OIDC fallback mode, request an OIDC token from the Actions runtime and pass it (or the actor/actor_id claims) to the Copilot CLI via a new flag or env var.
Copilot CLI Accept an OIDC JWT as an alternative authentication path (e.g., --oidc-token flag or COPILOT_OIDC_TOKEN env var). Present the token to the Copilot API backend for session initiation.
Copilot API backend Accept an OIDC-attested identity as a valid billing source. Validate the JWT signature (issuer: token.actions.githubusercontent.com), extract actor_id, verify the actor has an active Copilot license and premium request capacity, and attribute the session to that user.
GitHub Actions OIDC provider No change needed. Already emits actor, actor_id, and all required claims.

What would NOT need to change

  • Consumer repos: No secrets, no PATs, no service accounts. The triggering user's identity is carried by the OIDC token automatically.
  • Platform workflows: No changes to workflow logic. The engine: copilot frontmatter remains the same.
  • User consent model: The user already consented to the workflow running by posting the slash command. The OIDC token is an attestation of who they are, not a delegation of credentials.

Security considerations for the OIDC approach

Concern Mitigation
Can a workflow impersonate a different user? No. The OIDC actor / actor_id claims are set by the GitHub Actions runtime based on the actual event trigger. They cannot be overridden by workflow code.
Can a malicious workflow drain a user's premium requests? The user must have write access to the repository and must have explicitly triggered the workflow (e.g., posted a comment). This is the same trust model as native @copilot usage: the user initiates the action.
Does this expose the user's credentials? No. The OIDC token is an attestation of identity, not a credential. It cannot be used to access the user's repositories, secrets, or other resources. It only asserts "this workflow was triggered by user X."
What if the actor doesn't have a Copilot license? The Copilot API should reject the session with a clear error, just as it does today when COPILOT_GITHUB_TOKEN belongs to a user without a license.
workflow_call chains In a workflow_call chain, github.actor reflects the original triggering user, not the workflow author. The OIDC actor claim follows the same semantics.
OIDC token trust boundary The Copilot API backend would only need to trust tokens from token.actions.githubusercontent.com — GitHub's own OIDC provider. This is the same trust model used by Azure, AWS, and GCP for workload identity federation.

Why this matters at scale

Aspect Current (PAT-based) Desired end state
Attribution granularity Per-team (one PAT per repo) Per-user (automatic, identical to native @copilot)
Consumer onboarding Create PAT, store as secret, add caller workflow Add caller workflow only (no secrets)
Token rotation Active management — person leaves, quota exhausted, org policy changes None — no tokens to manage
Service account overhead Additional Copilot license per team + provisioning None
Org policy compatibility May conflict with PAT restrictions No PATs required
Billing accuracy One person absorbs entire team's usage Actual user is billed, identical to all other Copilot features

Alternatives we considered (and why they don't work)

Alternative Why it doesn't solve the problem
GITHUB_TOKEN Installation access token (ghs_), no user identity, no Copilot entitlement.
GitHub App user-to-server token (ghu_) Requires interactive OAuth consent flow — cannot be obtained in a headless Actions workflow.
github.actor context variable Username string only, not a credential or attestation. Cannot be cryptographically verified by the Copilot API.
Custom cost attribution dashboard Informational only — no billing separation, no per-user caps, duplicates what GitHub billing already provides.

Next steps

We'd welcome a conversation to validate this problem statement and explore feasible solutions — whether OIDC-based or otherwise. We are happy to participate in a preview programme and provide feedback from our production platform.


Impact

We believe the use case of centralised agent platforms calling engine: copilot via workflow_call will become increasingly common as organisations adopt agentic workflows. Solving the attribution problem now prevents every platform team from independently building workarounds around PAT management and token rotation.

This feature would make cost attribution for platform-orchestrated Copilot sessions identical to native @copilot usage — per-user, automatic, and zero-configuration.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions