|
| 1 | +# Server-to-server authentication |
| 2 | + |
| 3 | +Use a short-lived installation access token when a service needs to make Copilot requests on behalf of an organization without a user's credentials. In GitHub Actions, use the built-in `GITHUB_TOKEN` instead. |
| 4 | + |
| 5 | +## GitHub Actions |
| 6 | + |
| 7 | +For workflows in an organization-owned repository, grant the built-in token permission to make Copilot requests: |
| 8 | + |
| 9 | +```yaml |
| 10 | +permissions: |
| 11 | + contents: read |
| 12 | + copilot-requests: write |
| 13 | + |
| 14 | +jobs: |
| 15 | + copilot: |
| 16 | + runs-on: ubuntu-latest |
| 17 | + steps: |
| 18 | + - uses: actions/checkout@v6 |
| 19 | + - run: your-application |
| 20 | + env: |
| 21 | + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 22 | +``` |
| 23 | +
|
| 24 | +The organization's **Allow use of Copilot CLI billed to the organization** policy must be enabled. This approach needs no GitHub App or stored authentication secret. For details, see [Using Copilot CLI in GitHub Actions with GITHUB_TOKEN](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli-in-actions). |
| 25 | +
|
| 26 | +## Other services and CI systems |
| 27 | +
|
| 28 | +For services outside GitHub Actions: |
| 29 | +
|
| 30 | +1. Create a GitHub App with the **Copilot Requests** repository permission set to **Read & write**. |
| 31 | +1. Install it on the organization that should be billed. The current Copilot permission check requires **All repositories** access. |
| 32 | +1. [Create an installation access token](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app) with a repository ID and the Copilot permission: |
| 33 | +
|
| 34 | + ```json |
| 35 | + { |
| 36 | + "repository_ids": [123456789], |
| 37 | + "permissions": { |
| 38 | + "copilot_requests": "write" |
| 39 | + } |
| 40 | + } |
| 41 | + ``` |
| 42 | + |
| 43 | +1. Pass the resulting `ghs_` token to the runtime as `COPILOT_GITHUB_TOKEN`. |
| 44 | + |
| 45 | +The organization must be enabled for Copilot requests from GitHub App installations. Installation tokens expire after one hour. |
| 46 | + |
| 47 | +> [!WARNING] |
| 48 | +> Do not pass an installation token through the SDK's `gitHubToken`, `github_token`, or equivalent option. That option is for user tokens. Installation tokens must use the runtime environment authentication path. |
| 49 | +
|
| 50 | +## Configure the runtime |
| 51 | + |
| 52 | +The following examples assume the minted token is in `INSTALLATION_TOKEN`. They pass it only to the child runtime and disable fallback to stored user credentials. |
| 53 | + |
| 54 | +<details open> |
| 55 | +<summary><strong>TypeScript</strong></summary> |
| 56 | + |
| 57 | +```typescript |
| 58 | +import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk"; |
| 59 | + |
| 60 | +const token = process.env.INSTALLATION_TOKEN; |
| 61 | +if (!token) throw new Error("INSTALLATION_TOKEN is required"); |
| 62 | + |
| 63 | +const client = new CopilotClient({ |
| 64 | + connection: RuntimeConnection.forStdio(), |
| 65 | + env: { |
| 66 | + ...process.env, |
| 67 | + COPILOT_GITHUB_TOKEN: token, |
| 68 | + }, |
| 69 | + useLoggedInUser: false, |
| 70 | +}); |
| 71 | +``` |
| 72 | + |
| 73 | +</details> |
| 74 | +<details> |
| 75 | +<summary><strong>Python</strong></summary> |
| 76 | + |
| 77 | +```python |
| 78 | +import os |
| 79 | + |
| 80 | +from copilot import CopilotClient, RuntimeConnection |
| 81 | + |
| 82 | +client = CopilotClient( |
| 83 | + connection=RuntimeConnection.for_stdio(), |
| 84 | + env={**os.environ, "COPILOT_GITHUB_TOKEN": os.environ["INSTALLATION_TOKEN"]}, |
| 85 | + use_logged_in_user=False, |
| 86 | +) |
| 87 | +``` |
| 88 | + |
| 89 | +</details> |
| 90 | +<details> |
| 91 | +<summary><strong>Go</strong></summary> |
| 92 | + |
| 93 | +```go |
| 94 | +package main |
| 95 | + |
| 96 | +import ( |
| 97 | + "log" |
| 98 | + "os" |
| 99 | + |
| 100 | + copilot "github.com/github/copilot-sdk/go" |
| 101 | +) |
| 102 | + |
| 103 | +func main() { |
| 104 | + token, ok := os.LookupEnv("INSTALLATION_TOKEN") |
| 105 | + if !ok { |
| 106 | + log.Fatal("INSTALLATION_TOKEN is required") |
| 107 | + } |
| 108 | + client := copilot.NewClient(&copilot.ClientOptions{ |
| 109 | + Connection: copilot.StdioConnection{}, |
| 110 | + Env: append(os.Environ(), "COPILOT_GITHUB_TOKEN="+token), |
| 111 | + UseLoggedInUser: copilot.Bool(false), |
| 112 | + }) |
| 113 | + _ = client |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +</details> |
| 118 | +<details> |
| 119 | +<summary><strong>Rust</strong></summary> |
| 120 | + |
| 121 | +```rust |
| 122 | +use github_copilot_sdk::{ClientOptions, Transport}; |
| 123 | + |
| 124 | +fn main() { |
| 125 | + let token = std::env::var("INSTALLATION_TOKEN").expect("INSTALLATION_TOKEN is required"); |
| 126 | + let options = ClientOptions::new() |
| 127 | + .with_transport(Transport::Stdio) |
| 128 | + .with_env([("COPILOT_GITHUB_TOKEN", token)]) |
| 129 | + .with_use_logged_in_user(false); |
| 130 | + drop(options); |
| 131 | +} |
| 132 | +``` |
| 133 | + |
| 134 | +</details> |
| 135 | +<details> |
| 136 | +<summary><strong>.NET</strong></summary> |
| 137 | + |
| 138 | +```csharp |
| 139 | +using System.Collections; |
| 140 | +using GitHub.Copilot; |
| 141 | + |
| 142 | +var token = Environment.GetEnvironmentVariable("INSTALLATION_TOKEN") |
| 143 | + ?? throw new InvalidOperationException("INSTALLATION_TOKEN is required"); |
| 144 | +var environment = Environment.GetEnvironmentVariables() |
| 145 | + .Cast<DictionaryEntry>() |
| 146 | + .ToDictionary(entry => (string)entry.Key, entry => entry.Value?.ToString() ?? ""); |
| 147 | +environment["COPILOT_GITHUB_TOKEN"] = token; |
| 148 | + |
| 149 | +await using var client = new CopilotClient(new CopilotClientOptions |
| 150 | +{ |
| 151 | + Connection = RuntimeConnection.ForStdio(), |
| 152 | + Environment = environment, |
| 153 | + UseLoggedInUser = false, |
| 154 | +}); |
| 155 | +``` |
| 156 | + |
| 157 | +</details> |
| 158 | +<details> |
| 159 | +<summary><strong>Java</strong></summary> |
| 160 | + |
| 161 | +```java |
| 162 | +import com.github.copilot.CopilotClient; |
| 163 | +import com.github.copilot.rpc.CopilotClientOptions; |
| 164 | +import java.util.HashMap; |
| 165 | +import java.util.Objects; |
| 166 | + |
| 167 | +var environment = new HashMap<>(System.getenv()); |
| 168 | +var token = Objects.requireNonNull( |
| 169 | + System.getenv("INSTALLATION_TOKEN"), "INSTALLATION_TOKEN is required"); |
| 170 | +environment.put("COPILOT_GITHUB_TOKEN", token); |
| 171 | + |
| 172 | +try (var client = new CopilotClient(new CopilotClientOptions() |
| 173 | + .setEnvironment(environment) |
| 174 | + .setUseLoggedInUser(false))) { |
| 175 | + // Use the client. |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +</details> |
| 180 | + |
| 181 | +For in-process FFI, set `COPILOT_GITHUB_TOKEN` in the host environment before loading the runtime; per-client environment options are not supported. For an existing runtime URI, set it on that runtime process. |
| 182 | + |
| 183 | +## Refresh tokens |
| 184 | + |
| 185 | +Mint a new installation token before the current token expires. For a child process, restart the SDK client with the new environment. For an in-process or existing runtime, restart the host runtime with the new token. |
| 186 | + |
| 187 | +## Billing |
| 188 | + |
| 189 | +Usage is attributed and billed to the account that owns the GitHub App installation. Use an organization installation for organization billing; a user-account installation attributes usage to that user. |
| 190 | + |
| 191 | +## Troubleshooting |
| 192 | + |
| 193 | +| Symptom | Check | |
| 194 | +|---|---| |
| 195 | +| `401 Unauthorized` | Confirm the organization supports GitHub App installation authentication for Copilot. | |
| 196 | +| `403 Resource not accessible by integration` or an error mentioning user information | Confirm the installation token is in `COPILOT_GITHUB_TOKEN`, not the SDK's explicit token option. | |
| 197 | +| `403 Forbidden` from the Copilot API | Confirm the token request contains `repository_ids` and `copilot_requests: write`. | |
| 198 | +| `403 Forbidden` with the required token request | Confirm the app installation has **All repositories** access, then mint a new token. | |
| 199 | +| Requested model is unavailable | Confirm the organization's Copilot policy allows the model and the bundled runtime supports it. | |
| 200 | +| Wrong account billed | Confirm the installation belongs to the intended organization. | |
| 201 | + |
| 202 | +## Further reading |
| 203 | + |
| 204 | +* [Authenticate Copilot SDK](./authenticate.md): other authentication methods and priority |
| 205 | +* [Generating an installation access token](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app): GitHub App token creation |
0 commit comments