> ## Documentation Index
> Fetch the complete documentation index at: https://docs.r28.ai/charter/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Charter works for any HTTP API, including the user's own. The packs are examples of the result, not the limit: an API with no pack is declared the same way, following https://raw.githubusercontent.com/r28ai/charter/main/docs/.mintlify/skills/declaring-charter-tools/SKILL.md.
> Charter is for agents that act in customers' accounts: every call is checked field by field before it's sent, the app says whose account each call is for, and the same declarations list what each tool can reach. https://docs.r28.ai/charter/llms.txt says what it is and lists every page.

# Register a GitHub OAuth App

> The OAuth App your users authorize: one form, a client ID and secret, and the consent screen they see, with a screenshot for each.

Before your users can connect their GitHub accounts, GitHub needs an app that
you own and they authorize. An OAuth App is a single form and takes about two
minutes. Each user's token then acts as that user, with the scopes they
approved.

Only connecting your own account? [Set up GitHub](https://docs.r28.ai/charter/charter/auth/setup/github.md) is
shorter: a personal access token with the scopes already ticked.

Every GitHub link below opens in a new tab, so this page stays where you left it.

> [!NOTE]
>
> GitHub also offers **GitHub Apps**, with fine-grained permissions and 8-hour
> tokens that refresh. The steps are similar, starting at
> [github.com/settings/apps/new](https://github.com/settings/apps/new), and the
> token you get back depends on one checkbox there:
> [which token you have](https://docs.r28.ai/charter/charter/auth/providers/github.md#which-token-you-have).

**1. Fill in the registration form**

Open [github.com/settings/applications/new](https://github.com/settings/applications/new).
You can also get there from **Settings → Developer settings → OAuth Apps →
New OAuth App**.

![GitHub's Register a new OAuth app form, filled in: application name Example App, homepage https://app.example.com, a short description, and authorization callback URL https://app.example.com/oauth/github/callback. Enable Device Flow is unticked.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/github/01-register-app.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=c8287359e9e814e83f57af442ed0a8d0)

* **Application name**: what your users see on the consent screen. Use a name
  they'll recognize.
* **Homepage URL**: your product's public site.
* **Application description**: optional, but it's shown to users while they
  decide, so it's worth a sentence.
* **Authorization callback URL**: your app's callback route. GitHub sends the
  browser back here after consent, so it has to match the `redirect_uri` your
  flow sends.

Leave **Enable Device Flow** unticked. It's for CLIs and devices without a
browser, and this flow runs in one.

Everything on this form is public, so don't put internal hostnames or anything
secret in it. Then click **Register application**.

**2. Copy the client ID, then generate a secret**

GitHub opens the app's page. Copy the **Client ID**, then click
**Generate a new client secret** and copy the secret straight away. GitHub shows
it only once.

![The OAuth App's page: the client ID, and a newly generated client secret with a notice to copy it now because it won't be shown again.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/github/02-client-secret.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=6ba83f9e71bc44688e107238d9a33dec)

```bash theme={null}
export GITHUB_CLIENT_ID=Ov23li...
export GITHUB_CLIENT_SECRET=...
```

Lost it? Generate another one, switch your app over to it, then delete the old
one. An app can hold more than one secret, so rotating needs no downtime.

**3. Connect an account**

Your app's "Connect GitHub" button opens the link that
[`flow.authorize()`](https://docs.r28.ai/charter/charter/reference/oauth#oauth2flow-authorize) builds. GitHub
shows the app, who owns it and every scope it asks for, and the user clicks
**Authorize**.

![GitHub's Authorize Example App screen: the app and its owner, the permissions requested (codespaces, gists, notifications, projects, repositories, personal user data, workflows), an organization access section with a Request button, and an Authorize button.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/github/03-consent.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=8f5c2bb60640de020e569decd5e69fe9)

The pack's tools need `repo` and `read:user`, so that's the scope list to ask
for. The screen above asks for more, and every extra scope is another line your
users have to accept.

**Organization access** is listed separately. A user can authorize the app for
their own account straight away, but an organization that restricts OAuth Apps
keeps its private repositories out of reach until an owner approves the
**Request**.

```python github_connect.py theme={null}
from charter.auth import OAuth2Flow

flow = OAuth2Flow(
    GITHUB,  # the server constant, from /auth/providers/github
    client_id=os.environ["GITHUB_CLIENT_ID"],
    client_secret=os.environ["GITHUB_CLIENT_SECRET"],
    redirect_uri="https://app.example.com/oauth/github/callback",
)
request = flow.authorize(["repo", "read:user"])  # request.url is the link to open

# At the callback, after checking state:
grant = await flow.exchange(code, code_verifier=verifier, expect_refresh_token=False)
# An OAuth App's token never expires and comes with no refresh token.
```

Store `grant.access_token` against the user, and hand it to the pack with a
[`StaticTokenProvider`](https://docs.r28.ai/charter/charter/reference/credentials#statictokenprovider) from a
[`SubjectProvider`](https://docs.r28.ai/charter/charter/auth/your-users).

## When it does not work

> **“The redirect\_uri MUST match the registered callback URL for this application”**
>
> The flow sent a `redirect_uri` that the app doesn't list. An OAuth App takes
> one callback URL, and the one you send must start with it. Change the
> app's **Authorization callback URL**, or send the one it has.

> **403: “Resource protected by organization SAML enforcement”, or an organization's repositories missing**
>
> The organization hasn't approved the app, or the user hasn't authorized it
> for that organization's SSO. An owner approves it under the organization's
> **Settings → Third-party access**.

> **grant.scopes is one string, not a list**
>
> GitHub separates scopes with commas, not the spaces the specification
> calls for, so `repo,read:user` arrives as a single element. Split it on
> commas before you compare. [GitHub's provider page](https://docs.r28.ai/charter/charter/auth/providers/github.md#getting-the-first-grant)
> has the detail.

## Related

* [GitHub](https://docs.r28.ai/charter/charter/auth/providers/github.md) — the server constant, every token type, and refresh under expiry
* [Getting the grant](https://docs.r28.ai/charter/charter/auth/oauth-flow.md) — the two routes, `state` and PKCE, inside your app
* [Every pack](https://docs.r28.ai/charter/charter/auth/your-users.md) — what each pack's users connect with, and what you store
* [Set up GitHub](https://docs.r28.ai/charter/charter/auth/setup/github.md) — your own account, with a personal access token


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.