Skip to content

Add support for OIDC subject claim customization on organizations and repositories #562

@MariusStorhaug

Description

Context

GitHub Actions workflows can use OpenID Connect (OIDC) to exchange short-lived tokens directly with cloud providers (AWS, Azure, GCP, HashiCorp Vault, etc.) instead of storing long-lived credentials as secrets. The OIDC token issued by GitHub contains a sub (subject) claim that cloud providers use to authorize access.

By default, the subject claim follows the format repo:/:, but GitHub provides REST API endpoints to customize which claim keys are included in the subject. This customization can be configured at both the organization and repository level, allowing fine-grained control over the identity claims presented to cloud providers.

The GitHub PowerShell module currently has no coverage of the Actions OIDC API endpoints.

Request

The module should provide commands to get and set the OIDC subject claim customization template at both the organization and repository levels. This allows automation of OIDC trust configuration across multiple repositories and organizations — a common need when managing cloud provider integrations at scale.

Additionally, the module should provide a way to discover which claim keys are available on a given GitHub instance, so that argument completion can guide the user when specifying claim keys.

What is expected

  • A command to retrieve the current OIDC subject claim customization template for an organization or repository
  • A command to set (create or update) the OIDC subject claim customization template for an organization or repository
  • Support for the include_claim_keys array and the repository-level use_default flag
  • A command to discover the available claim keys for a given GitHub instance via the OpenID Connect Discovery endpoint
  • Argument completion for the IncludeClaimKeys parameter, powered by the discovery function

API endpoints to cover

Method Endpoint Synopsis
GET /orgs/{org}/actions/oidc/customization/sub Get the customization template for an OIDC subject claim for an organization
PUT /orgs/{org}/actions/oidc/customization/sub Set the customization template for an OIDC subject claim for an organization
GET /repos/{owner}/{repo}/actions/oidc/customization/sub Get the customization template for an OIDC subject claim for a repository
PUT /repos/{owner}/{repo}/actions/oidc/customization/sub Set the customization template for an OIDC subject claim for a repository

OIDC discovery endpoint

The list of available claim keys is not hardcoded — it is dynamically discoverable via the OpenID Connect Discovery endpoint exposed by each GitHub instance's OIDC token issuer:

Instance Discovery URL
github.com https://token.actions.githubusercontent.com/.well-known/openid-configuration
GHE.com ({subdomain}.ghe.com) https://token.actions.{subdomain}.ghe.com/.well-known/openid-configuration

The discovery response contains a claims_supported array listing all valid claim keys for that instance. This is used by Get-GitHubOidcClaim and powers argument completion for the IncludeClaimKeys parameter.

Note

The OIDC discovery endpoint is public and requires no authentication.

API response shapes

Organization GET returns:

{
  "include_claim_keys": [
    "repo",
    "context"
  ]
}

Repository GET returns (includes use_default):

{
  "use_default": false,
  "include_claim_keys": [
    "repo",
    "context"
  ]
}

Repository PUT body requires:

{
  "use_default": false,
  "include_claim_keys": [
    "repo",
    "context"
  ]
}

Note

When use_default is true, the include_claim_keys field is ignored by the API.

Available claim keys

The following claim keys are returned by the github.com discovery endpoint (as of writing). The actual list is dynamic and instance-specific — always use the discovery endpoint to get the current set:

sub, aud, exp, iat, iss, jti, nbf, ref, sha, repository, repository_id, repository_owner, repository_owner_id, enterprise, enterprise_id, run_id, run_number, run_attempt, actor, actor_id, workflow, workflow_ref, workflow_sha, head_ref, base_ref, event_name, ref_type, ref_protected, environment, environment_node_id, job_workflow_ref, job_workflow_sha, repository_visibility, runner_environment, issuer_scope, check_run_id

Required permissions

Endpoint Fine-grained token permission Classic scope
Org GET Administration org (read) read:org
Org PUT Administration org (write) write:org
Repo GET Actions repo (read) repo
Repo PUT Actions repo (write) repo

Acceptance criteria

  • OIDC subject claim templates can be retrieved for organizations and repositories
  • OIDC subject claim templates can be set for organizations and repositories
  • The use_default flag is supported for repository-level configuration
  • Available claim keys are discoverable via Get-GitHubOidcClaim for any GitHub instance (github.com and GHE.com)
  • Get-GitHubOidcClaim is accessible without authentication and works across all context types
  • The IncludeClaimKeys parameter on Set-GitHubOidcSubjectClaim has argument completion powered by the discovery endpoint
  • The commands follow existing module conventions for parameter naming and context resolution

Technical decisions

Object type grouping: The new functions belong under the Actions object type folder, since the OIDC API is part of the GitHub Actions REST API. Create an OIDC subfolder under Actions for both public and private functions.

Public function naming:

  • Get-GitHubOidcClaim — retrieves the list of supported OIDC claim keys from the discovery endpoint
  • Get-GitHubOidcSubjectClaim — retrieves the OIDC subject claim template
  • Set-GitHubOidcSubjectClaim — creates or updates the OIDC subject claim template

Private function naming (one function per API call):

  • Get-GitHubOidcSubjectClaimForOrganizationGET /orgs/{org}/actions/oidc/customization/sub
  • Set-GitHubOidcSubjectClaimForOrganizationPUT /orgs/{org}/actions/oidc/customization/sub
  • Get-GitHubOidcSubjectClaimForRepositoryGET /repos/{owner}/{repo}/actions/oidc/customization/sub
  • Set-GitHubOidcSubjectClaimForRepositoryPUT /repos/{owner}/{repo}/actions/oidc/customization/sub

OIDC claims discovery function (Get-GitHubOidcClaim): This function follows the Get-GitHubStatus pattern — a public function that calls Invoke-RestMethod directly on a non-API endpoint (the OIDC discovery URL). No private function counterpart is needed because: (1) the endpoint is public and requires no authentication, (2) there is only one operation (get the claims list), and (3) the Status/ functions set the precedent for this pattern. The function accepts an optional Context parameter to determine the hostname; when no context is provided, it defaults to github.com. URL derivation: github.comtoken.actions.githubusercontent.com; any other hostname → token.actions.{hostname}.

Argument completer for IncludeClaimKeys: A completers.ps1 file in src/functions/public/Actions/OIDC/ registers an argument completer for the IncludeClaimKeys parameter on Set-GitHubOidcSubjectClaim. The completer calls Get-GitHubOidcClaim with the context from $fakeBoundParameters and filters results using the configured CompletionMode (following the pattern in Gitignore/completers.ps1).

Parameter set naming (public functions): Use the synopsis of the corresponding private function:

  • Get-GitHubOidcSubjectClaim:
    • 'Get the customization template for an OIDC subject claim for an organization'
    • 'Get the customization template for an OIDC subject claim for a repository'
  • Set-GitHubOidcSubjectClaim:
    • 'Set the customization template for an OIDC subject claim for an organization'
    • 'Set the customization template for an OIDC subject claim for a repository'

Parameter naming:

  • Owner — with aliases Organization and User (standard convention)
  • Repository — with alias Repo if needed
  • IncludeClaimKeys[string[]] array of claim keys to include in the subject
  • UseDefault[switch] for the repository-level use_default flag
  • Context — standard context parameter

API call pattern: Use REST API via Invoke-GitHubAPI (not GraphQL), since this is a simple REST-only endpoint. Follow the splat pattern from existing private functions: Method, APIEndpoint, Body, Context.

No class needed: The API response is a simple object with 1–2 properties (include_claim_keys and optionally use_default). Return the response directly as a PSCustomObject rather than creating a dedicated class. The output is small and flat, and a class would add complexity without meaningful benefit.

ShouldProcess: Apply [CmdletBinding(SupportsShouldProcess)] only to Set-GitHubOidcSubjectClaim and the Set- private functions. The Get- functions must not use it.

Breaking changes: None — this is entirely new functionality.

Test approach: Unit tests with mocked API responses covering:

  • Get template for organization
  • Get template for repository
  • Set template for organization
  • Set template for repository with UseDefault = $false and custom claim keys
  • Set template for repository with UseDefault = $true (claim keys ignored)
  • Get-GitHubOidcClaim accessibility — verify that the discovery endpoint is reachable and returns claim keys across all auth context types (no context, PAT, UAT, IAT, App), confirming the public endpoint works regardless of authentication state

Implementation plan

Claims discovery

  • Create src/functions/public/Actions/OIDC/Get-GitHubOidcClaim.ps1 — public function that retrieves supported claim keys from the OIDC discovery endpoint, following the Get-GitHubStatus pattern (direct Invoke-RestMethod, optional Context for hostname derivation)
  • Create src/functions/public/Actions/OIDC/completers.ps1 — argument completer for IncludeClaimKeys on Set-GitHubOidcSubjectClaim, powered by Get-GitHubOidcClaim

Private functions

  • Create src/functions/private/Actions/OIDC/Get-GitHubOidcSubjectClaimForOrganization.ps1GET /orgs/{org}/actions/oidc/customization/sub
  • Create src/functions/private/Actions/OIDC/Set-GitHubOidcSubjectClaimForOrganization.ps1PUT /orgs/{org}/actions/oidc/customization/sub
  • Create src/functions/private/Actions/OIDC/Get-GitHubOidcSubjectClaimForRepository.ps1GET /repos/{owner}/{repo}/actions/oidc/customization/sub
  • Create src/functions/private/Actions/OIDC/Set-GitHubOidcSubjectClaimForRepository.ps1PUT /repos/{owner}/{repo}/actions/oidc/customization/sub

Public functions

  • Create src/functions/public/Actions/OIDC/Get-GitHubOidcSubjectClaim.ps1 with parameter sets dispatching to the two Get- private functions
  • Create src/functions/public/Actions/OIDC/Set-GitHubOidcSubjectClaim.ps1 with parameter sets dispatching to the two Set- private functions, using SupportsShouldProcess

Tests

  • Add tests for Get-GitHubOidcClaim — verify the discovery endpoint is accessible and returns a non-empty claims_supported array. Test without context (defaults to github.com), and with each auth context type (PAT, UAT, IAT, App) to confirm all users can access and use the function regardless of authentication state
  • Add unit tests for Get-GitHubOidcSubjectClaimForOrganization
  • Add unit tests for Set-GitHubOidcSubjectClaimForOrganization
  • Add unit tests for Get-GitHubOidcSubjectClaimForRepository
  • Add unit tests for Set-GitHubOidcSubjectClaimForRepository
  • Add integration-level tests for Get-GitHubOidcSubjectClaim (parameter set dispatch)
  • Add integration-level tests for Set-GitHubOidcSubjectClaim (parameter set dispatch, ShouldProcess)

Documentation

  • Add .SYNOPSIS, .DESCRIPTION, .NOTES with API doc links, and .LINK to all functions
  • Add usage examples to all public functions

Metadata

Metadata

Labels

Type

No type

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions