From 1d7a017f3767f912aac31569fe199d4b06c1f035 Mon Sep 17 00:00:00 2001 From: Patrick Nikoletich Date: Thu, 16 Jul 2026 10:57:05 -0700 Subject: [PATCH] docs: add server-to-server token guide Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2a84bd21-23e5-4358-96f1-f343030bb869 --- docs/README.md | 3 +- docs/auth/README.md | 3 +- docs/auth/authenticate.md | 3 + docs/auth/server-to-server-tokens.md | 301 +++++++++++++++++++++++++++ 4 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 docs/auth/server-to-server-tokens.md diff --git a/docs/README.md b/docs/README.md index 9e0d8fddff..ed3370f882 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,7 +8,7 @@ Welcome to the GitHub Copilot SDK docs. Whether you're building your first Copil |---|---| | **Build my first app** | [Getting Started](./getting-started.md)—end-to-end tutorial with streaming & custom tools | | **Set up for production** | [Setup Guides](./setup/README.md)—architecture, deployment patterns, scaling | -| **Configure authentication** | [Authentication](./auth/README.md)—GitHub OAuth, environment variables, BYOK | +| **Configure authentication** | [Authentication](./auth/README.md)—GitHub OAuth, server-to-server tokens, environment variables, BYOK | | **Add features to my app** | [Features](./features/README.md)—hooks, custom agents, MCP, skills, and more | | **Debug an issue** | [Troubleshooting](./troubleshooting/debugging.md)—common problems and solutions | @@ -35,6 +35,7 @@ How to configure and deploy the SDK for your use case. Configuring how users and services authenticate with Copilot. * [Authentication Overview](./auth/README.md): methods, priority order, and examples +* [Server-to-server tokens](./auth/server-to-server-tokens.md): use GitHub App installation tokens for organization-attributed automation * [Bring Your Own Key (BYOK)](./auth/byok.md): use your own API keys from OpenAI, Azure, Anthropic, and more ### [Features](./features/README.md) diff --git a/docs/auth/README.md b/docs/auth/README.md index 282bcb0192..ad28769f2d 100644 --- a/docs/auth/README.md +++ b/docs/auth/README.md @@ -3,10 +3,11 @@ Choose the authentication method that best fits your deployment scenario for the GitHub Copilot SDK. * [Authenticate Copilot SDK](authenticate.md): methods, priority order, and examples +* [Server-to-server tokens](server-to-server-tokens.md): use GitHub App installation tokens for organization-attributed automation * [Bring your own key (BYOK)](./byok.md): use your own API keys from OpenAI, Azure, Anthropic, and more ## Authentication priority -When multiple credentials are configured, an explicit SDK token takes priority, followed by direct Copilot API environment authentication, environment variable GitHub tokens, stored Copilot CLI credentials, and then GitHub CLI credentials. See [Authenticate Copilot SDK](authenticate.md#authentication-priority) for details. +When multiple credentials are configured, an explicit SDK token takes priority, followed by direct Copilot API environment authentication, environment variable GitHub tokens, stored Copilot CLI credentials, and then GitHub CLI credentials. Server-to-server installation tokens use the environment variable path. See [Authenticate Copilot SDK](authenticate.md#authentication-priority) for details. For multi-user server mode, pass a per-session `gitHubToken` so each session runs with the correct GitHub identity; see [Multi-user and server deployments](../setup/multi-tenancy.md). diff --git a/docs/auth/authenticate.md b/docs/auth/authenticate.md index 1a01901863..bf4bafd3f1 100644 --- a/docs/auth/authenticate.md +++ b/docs/auth/authenticate.md @@ -9,6 +9,7 @@ The GitHub Copilot SDK supports multiple authentication methods to fit different | [GitHub Signed-in User](#github-signed-in-user) | Interactive apps where users sign in with GitHub | Yes | | [OAuth GitHub App](#oauth-github-app) | Apps acting on behalf of users via OAuth | Yes | | [Environment Variables](#environment-variables) | CI/CD, automation, server-to-server | Yes | +| [GitHub App installation token](./server-to-server-tokens.md) | Organization-attributed automation and direct organization billing | Yes | | [BYOK (Bring Your Own Key)](./byok.md) | Using your own API keys (Azure AI Foundry, OpenAI, and more) | No | ## GitHub signed-in user @@ -236,6 +237,8 @@ client.start().get(); For automation, CI/CD pipelines, and server-to-server scenarios, you can authenticate using environment variables. +For organization-attributed automation that should not use a user's personal access token, use a GitHub App installation access token. See [Server-to-server tokens](./server-to-server-tokens.md). + **Supported environment variables (in priority order):** 1. `COPILOT_GITHUB_TOKEN` - Recommended for explicit Copilot usage 1. `GH_TOKEN` - GitHub CLI compatible diff --git a/docs/auth/server-to-server-tokens.md b/docs/auth/server-to-server-tokens.md new file mode 100644 index 0000000000..12c1cb9e4d --- /dev/null +++ b/docs/auth/server-to-server-tokens.md @@ -0,0 +1,301 @@ +# Server-to-server tokens + +Use a GitHub App installation access token to authenticate the Copilot SDK from automation without relying on a user's personal access token. This flow is intended for agents, CI/CD jobs, and backend services that make Copilot requests on behalf of an organization. + +## When to use this flow + +Use server-to-server tokens when your application needs: + +* Organization-attributed Copilot usage and billing instead of user-attributed usage +* Automation that cannot depend on an interactive user sign-in +* A short-lived credential minted by a GitHub App installation +* Copilot SDK access from workflows, services, or agents that operate on repositories + +For per-user identity or per-user billing, use the standard user OAuth or token-based authentication flows in [Authenticate Copilot SDK](./authenticate.md). + +> [!NOTE] +> Server-to-server tokens do not bypass Copilot model policies. Requests authenticated with an organization installation token use the models allowed by that organization's Copilot policy. + +## Prerequisites + +Before you begin, make sure you have: + +* A GitHub Enterprise Cloud organization that is enabled for the Copilot GitHub App server-to-server flow +* A GitHub App that your service owns +* The GitHub App's app ID and private key +* Permission to install the app on the organization that should be billed +* A repository that the Copilot request can be attributed to + +If the organization or enterprise is not enabled for this flow, Copilot API requests made with the installation token return `401 Unauthorized` even when the GitHub App is configured correctly. + +## How it works + +1. Create or update a GitHub App. +1. Grant the app the **Copilot Requests** repository permission with **Read & write** access. +1. Install the app on the organization that should be billed. +1. Mint a repository-scoped installation access token that explicitly requests `copilot_requests: write`. +1. Pass the token to the Copilot CLI subprocess through `COPILOT_GITHUB_TOKEN`. +1. Create and use Copilot SDK sessions normally. + +The token returned by GitHub starts with `ghs_` and expires after 1 hour. + +## Create or update the GitHub App + +Create a GitHub App by following [Creating a GitHub App](https://docs.github.com/en/apps/creating-github-apps). + +When configuring app permissions: + +* Under **Repository permissions**, set **Copilot Requests** to **Read & write**. +* Add any other repository permissions your app needs, such as **Contents**, **Issues**, or **Pull requests**. +* Leave **Where can this GitHub App be installed?** set to the account scope that fits your application. + +If the app already exists, update it in **Settings** > **Developer settings** > **GitHub Apps** > your app > **Permissions & events**. Existing installations must re-approve the new **Copilot Requests** permission before tokens minted from those installations can call Copilot. + +## Install the app on the organization + +Install the GitHub App on the organization that should be attributed and billed for Copilot usage. + +Organization installations are recommended because: + +* Usage is attributed and billed to the organization instead of an individual user. +* Organization installations are the right shape for automation that acts on repositories owned by the organization. +* Organization installations can use organization-level Copilot policies and limits. + +When choosing repository access, select **All repositories**. The current Copilot permission check requires the parent installation to have all-repository access. You still scope each minted token to specific repositories by passing `repository_ids` when you create the installation access token. + +After installing the app, record the installation ID. You can find it in the installation URL or by calling `GET /app/installations` and filtering by `account.login`. + +## Mint an installation access token + +Follow [Generating an installation access token for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app), with two required details for Copilot: + +* Pass `repository_ids` to scope the token to one or more repositories. +* Pass `permissions` explicitly, including `"copilot_requests": "write"`. + +If your automation is not naturally tied to a repository, use a stable attribution repository that the app can access, such as the organization's `.github` repository or a dedicated placeholder repository. + +```http +POST https://api.github.com/app/installations/INSTALLATION_ID/access_tokens +Authorization: Bearer APP_JWT +Accept: application/vnd.github+json +Content-Type: application/json + +{ + "repository_ids": [REPOSITORY_ID], + "permissions": { + "copilot_requests": "write", + "metadata": "read" + } +} +``` + +The response contains a `token` field that starts with `ghs_`. Confirm that the response includes: + +* `"repository_selection": "selected"` +* `"permissions": { "copilot_requests": "write", ... }` + +Add any other permissions your app needs to the `permissions` object. Requested permissions must be a subset of the permissions approved on the installation. + +> [!WARNING] +> For Copilot server-to-server authentication, tokens minted without `repository_ids`, or without an explicit `permissions` object containing `copilot_requests: write`, are rejected by the Copilot API. + +## Pass the token to the SDK + +Do not pass a `ghs_` installation token with the SDK's `gitHubToken` or `github_token` option. Those options use the SDK's explicit user-token path, which calls user-oriented GitHub API endpoints that reject GitHub App installation tokens. + +Instead, leave the explicit GitHub token option unset and pass the installation token to the spawned Copilot CLI process with `COPILOT_GITHUB_TOKEN`. + +
+Node.js / TypeScript + + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const installationToken = await mintInstallationToken(); // Returns "ghs_..." + +const client = new CopilotClient({ + env: { + ...process.env, + COPILOT_GITHUB_TOKEN: installationToken, + }, + useLoggedInUser: false, +}); + +await client.start(); + +const session = await client.createSession({ + model: "claude-sonnet-4.6", +}); +``` + +
+ +
+Python + + +```python +import os + +from copilot import CopilotClient + +installation_token = mint_installation_token() # Returns "ghs_..." + +client = CopilotClient( + env={**os.environ, "COPILOT_GITHUB_TOKEN": installation_token}, + use_logged_in_user=False, +) + +await client.start() + +session = await client.create_session( + model="claude-sonnet-4.6", +) +``` + +
+ +
+Go + + +```go +package main + +import ( + "context" + "log" + "os" + + copilot "github.com/github/copilot-sdk/go" +) + +func mintInstallationToken() string { + return "ghs_..." +} + +func main() { + installationToken := mintInstallationToken() + + client := copilot.NewClient(&copilot.ClientOptions{ + Env: append(os.Environ(), "COPILOT_GITHUB_TOKEN="+installationToken), + UseLoggedInUser: copilot.Bool(false), + }) + + ctx := context.Background() + if err := client.Start(ctx); err != nil { + log.Fatal(err) + } + defer client.Stop() + + session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + Model: "claude-sonnet-4.6", + }) + if err != nil { + log.Fatal(err) + } + defer session.Disconnect() +} +``` + +
+ +
+.NET + + +```csharp +using System.Collections; +using GitHub.Copilot; + +var installationToken = MintInstallationToken(); // Returns "ghs_..." + +var env = System.Environment.GetEnvironmentVariables() + .Cast() + .ToDictionary( + entry => (string)entry.Key, + entry => entry.Value?.ToString() ?? string.Empty); +env["COPILOT_GITHUB_TOKEN"] = installationToken; + +await using var client = new CopilotClient(new CopilotClientOptions +{ + Environment = env, + UseLoggedInUser = false, +}); + +await client.StartAsync(); + +await using var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "claude-sonnet-4.6", +}); +``` + +
+ +
+Java + + +```java +import com.github.copilot.CopilotClient; +import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.SessionConfig; +import java.util.HashMap; + +String installationToken = mintInstallationToken(); // Returns "ghs_..." + +var env = new HashMap<>(System.getenv()); +env.put("COPILOT_GITHUB_TOKEN", installationToken); + +try (var client = new CopilotClient(new CopilotClientOptions() + .setEnvironment(env) + .setUseLoggedInUser(false))) { + client.start().get(); + + var session = client.createSession(new SessionConfig() + .setModel("claude-sonnet-4.6")).get(); +} +``` + +
+ +> [!NOTE] +> These examples configure the environment for the Copilot CLI process that the SDK spawns. If you connect to an already-running CLI server with a URI connection, set `COPILOT_GITHUB_TOKEN` in that server process instead. + +## Refresh and rotate tokens + +Installation access tokens expire after 1 hour. Cache tokens only until shortly before their expiry, then mint a fresh token. + +When you refresh the token, start a new Copilot SDK client with an updated `COPILOT_GITHUB_TOKEN` value. The Copilot CLI subprocess reads its environment when it starts and does not re-read the token during an existing session. + +Rotate the GitHub App private key according to your organization's security policy. If a token is exposed, revoke the installation token and rotate the app private key. + +## What gets billed + +Copilot usage is attributed to the account that owns the GitHub App installation used to mint the token: + +* Organization installation: usage is attributed and billed to the organization. +* User installation: usage is attributed to the individual user account. + +Use an organization installation for direct organization billing and automation scenarios that should not depend on a user's Copilot plan or personal access token. + +## Troubleshooting + +| Symptom | What to check | +|---|---| +| `401 Unauthorized` before reaching Copilot | Confirm the organization or enterprise is enabled for the Copilot GitHub App server-to-server flow. | +| `403 Resource not accessible by integration` or an error that mentions user info | Confirm you did not pass the `ghs_` token with `gitHubToken` or `github_token`. Pass it with `COPILOT_GITHUB_TOKEN` in the spawned CLI environment. | +| `403 Forbidden` from the Copilot API | Confirm the token was minted with `repository_ids` and explicit `permissions` containing `"copilot_requests": "write"`. | +| `403 Forbidden` after using the required mint body | Confirm the GitHub App installation has **All repositories** access, then mint a fresh token. | +| The requested model is unavailable | Confirm the organization's Copilot model policy allows the model and that the bundled Copilot CLI version supports it. | +| Requests are billed to the wrong account | Confirm the installation ID belongs to the organization, not a user account, before minting the token. | + +## Further reading + +* [Authenticate Copilot SDK](./authenticate.md): other SDK authentication methods and priority order +* [Creating a GitHub App](https://docs.github.com/en/apps/creating-github-apps): GitHub App setup +* [Authenticating as a GitHub App](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app): app JWTs and installation tokens +* [Generating an installation access token for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app): token minting API +* [Requests in GitHub Copilot](https://docs.github.com/en/copilot/concepts/billing/copilot-requests): Copilot request accounting