Files
coder/docs/admin/external-auth/index.md
T
Nick Vigilante d40e24cf7d docs: nest the CLI reference by subcommand (#29956)
## Summary

Nests the CLI reference by subcommand so the Command Line sidebar
collapses from 156 flat entries to 49 top-level verbs.
Each page's URL follows the invocation, its page title is the full
command as inline code, and its sidebar label is only its own verb as
inline code.

| | Before | After |
| --- | --- | --- |
| File | `docs/reference/cli/oauth2-provider_dcr_disable.md` |
`docs/reference/cli/oauth2-provider/dcr/disable.md` |
| URL | `/reference/cli/oauth2-provider_dcr_disable` |
`/reference/cli/oauth2-provider/dcr/disable` |
| Page title (front matter) | `oauth2-provider dcr disable` | `` `coder
oauth2-provider dcr disable` `` |
| Sidebar label (manifest) | `oauth2-provider dcr disable` | ``
`disable` `` |

Resolves DOCS-140 (coder/coder half). Also fixes the Makefile part of
DOCS-1073 / DOCS-912, which this change depends on.

## Changes

- `scripts/clidocgen`: pages nest by command path. A command with
visible subcommands becomes its directory's `index.md`, so
`oauth2-provider/index.md` and `oauth2-provider/dcr/index.md` sit above
`oauth2-provider/dcr/disable.md`. Subcommand table links are relative to
the parent page.
- `scripts/clidocgen`: the manifest "Command Line" section is built from
the command tree (nested, sorted by name, hidden commands skipped)
instead of a flat list. The redundant `coder` child that pointed back at
`reference/cli/index.md` is gone.
- `Makefile`: the CLI rule now replaces `docs/reference/cli/` wholesale,
so stale pages get removed. The manifest rule now runs `clidocgen
-manifest-only` on top of the API staging manifest, so the CLI nav is
generated instead of edited by hand. As a result, the nine pages that
had no nav entry (`ai-gateway*`, `secret enable/disable`, `server
fix-oidc-links`) now have one.
- `scripts/configdocgen` and 48 prose pages: links point at the new
paths (for example `reference/cli/server.md` is now
`reference/cli/server/index.md`).

## How to review/QA this PR

- `docs/reference/cli/**/*` (approximately 190 pages): Each of these
pages are generated automatically. Some pages appear deleted, and some
pages appear new. Every deleted page corresponds to a new page somewhere
else, so no information is being deleted. The docs in this section
should adhere to the following, so a test in the build preview for each
of these items should be satisfactory:
- [ ] Each subcommand of a command gets nested as a child instead of
being displayed as a flat list.
  - [ ] Each parent command is stored in an `index.md` file.
- [ ] The title of each page inside the page should now feature the full
`coder XYZ` command.
- [ ] The title of each page inside the page should now be formatted as
inline code.
- [ ] The title of each page in the sidebar should be the most granular
subcommand of the whole command string (if the page's title is `coder
ai-gateway keys create`, the sidebar label is `create`).
- [ ] The title of each page in the sidebar should now be formatted as
inline code.
  - [ ] The page slug for each page is now the most granular subcommand.
  - [ ] All links now point to the correct page.
- `docs/manifest.json`: The changes here reflect the updated structure
of the `docs/reference/cli` section.
- `docs/**/*.md` outside of `docs/reference/cli/**/*` (approximately 50
pages): The extent of the changes in these pages are just updating the
paths to point to the new page locations.
- [ ] The links on the page all point to the correct new path in
`docs/reference/cli`.
- `scripts/clidocgen` (4 files): This is the Go code responsible for
generating everything inside of `docs/reference/cli/**/*`. A typical
code review here should be satisfactory.
- `scripts/configdocgen` (2 files): these two files just have updates to
the raw `.md` path used by `docs/admin/setup/configuration-reference.md`
and updates the tests to reflect that.
- `Makefile`: This updates the `make` targets for generating the CLI
docs to fit the new structure.

## Merge ordering

Without the renderer PRs deployed, both docs sites show literal
backticks in titles and sidebar labels. Without the redirects, the old
flat URLs return 404.

1. Deploy before merging this PR: coder/docs-engine#176 (beta-docs
renders these titles as inline code) and coder/coder.com#1111 (classic
docs renders them as inline code). Both are safe to ship first, because
titles without backticks render as before.
2. Merge this PR.
3. Merge coder/coder.com#1112 (redirects from the old flat URLs) only
once this content reaches the mainline release branch that serves
unversioned `/docs`.

## Testing

- `go test ./scripts/clidocgen/ ./scripts/configdocgen/
./scripts/docshtmlcheck/`: pass. New tests cover page paths, subcommand
links, the nested manifest, and front matter quoting.
- `make docs/reference/cli/index.md docs/manifest.json
docs/admin/setup/configuration-reference.md`, then a forced rerun: the
second run produced no diff.
- Checked the manifest: all 164 CLI entries resolve to a file, no page
is missing a nav entry, and everything outside "Command Line" is
byte-identical.
- Relative `.md` links across `docs/`: 0 broken, same as `main`.
- `make pre-commit` and `make pre-push`: pass. On earlier pre-push
attempts, different unrelated vitest files timed out under host load.
Those files pass when run alone, and the final push passed with
`PARALLEL_JOBS=1`.

<details>
<summary>Implementation plan and decision log</summary>

**Goal (from DOCS-140 and the requester):** URLs follow the command
hierarchy. Pages are titled with the full command, and sidebar entries
with the last verb only. Both are formatted as inline code.

**Contract with the renderers:** a title wrapped entirely in one pair of
backticks (``^`[^`]+`$``) renders as inline code. Plain-text consumers
(`<title>`, meta, search, llms corpus) strip the backticks. Page title
comes from front matter `title`, and the sidebar label from the manifest
node `title`.

**Decisions**

- A parent command is `dir/index.md` rather than a sibling `dir.md`. The
root CLI page is already `index.md`, and both docs-engine and the legacy
coder.com renderer map `x/index.md` to `/x`.
- The manifest is built from the command tree, not from the written
files, so ordering is deterministic and the nesting matches the pages
exactly.
- The manifest discard bug (DOCS-1073 item 1) is fixed here, because
without that fix the generated nested manifest would never reach the
tree. DOCS-1073 items 2 through 4 (a prose fix and a hidden-command
policy) are out of scope.
- `-manifest-only` keeps the existing parallel `make gen` layout: the
API rule still owns `_gen/manifest-staging.json`, and CLI regeneration
doesn't retrigger swag.
- The renderer and redirect work lives in the companion PRs in
coder/docs-engine and coder/coder.com. It is linked above and not
duplicated here.

</details>

---

> [!NOTE]
> Generated by Coder Agents on behalf of @nickvigilante.
2026-09-28 15:44:10 -04:00

19 KiB

title
title
External Authentication

Coder supports external authentication via OAuth2.0. This allows enabling any OAuth provider as well as integrations with Git providers, such as GitHub, GitLab, and Bitbucket.

External authentication can also be used to integrate with external services like JFrog Artifactory and others.

To add an external authentication provider, you'll need to create an OAuth application. The following providers have been tested and work with Coder:

If you have experience with a provider that is not listed here, please file an issue

Configuration

Set environment variables

After you create an OAuth application, set environment variables to configure the control plane to use it:

CODER_EXTERNAL_AUTH_0_ID="<USER_DEFINED_ID>"
CODER_EXTERNAL_AUTH_0_TYPE=<github|gitlab|azure-devops|bitbucket-cloud|bitbucket-server|etc>
CODER_EXTERNAL_AUTH_0_CLIENT_ID=<OAuth app client ID>
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=<OAuth app client secret>

# Optionally, configure a custom display name and icon:
CODER_EXTERNAL_AUTH_0_DISPLAY_NAME="Google Calendar"
CODER_EXTERNAL_AUTH_0_DISPLAY_ICON="https://mycustomicon.com/google.svg"

The CODER_EXTERNAL_AUTH_0_ID environment variable is used as an identifier for the authentication provider.

This variable is used as part of the callback URL path that you must configure in your OAuth provider settings. If the value in your callback URL doesn't match the CODER_EXTERNAL_AUTH_0_ID value, authentication will fail with redirect URI is not valid. Set it with a value that helps you identify the provider. For example, if you use CODER_EXTERNAL_AUTH_0_ID="primary-github" for your GitHub provider, configure your callback URL as https://example.com/external-auth/primary-github/callback.

By default, the redirect URL is built from the access URL Coder is configured with. You can override the base URL with:

CODER_EXTERNAL_AUTH_0_REDIRECT_URL=https://my.tld

This would change the callback in the above example to https://my.tld/external-auth/primary-github/callback (any path component on the redirect URL is ignored).

Using this setting can break OAuth, so use with caution. The override is intended to be used when the access URL is internal and either:

  • Users access Coder via some other URL that proxies to the internal one.
  • The redirect URL redirects to the internal access URL (this can be used to work around providers that require public domains for the callback).

Ultimately, the user must end up on the same domain they were on when the authentication flow was initiated.

Add an authentication button to the workspace template

Add the following code to any template to add a button to the workspace setup page which will allow you to authenticate with your provider:

data "coder_external_auth" "<github|gitlab|azure-devops|bitbucket-cloud|bitbucket-server|other>" {
    id = "<USER_DEFINED_ID>"
}

# GitHub Example (CODER_EXTERNAL_AUTH_0_ID="primary-github")
# makes a GitHub authentication token available at data.coder_external_auth.github.access_token
data "coder_external_auth" "github" {
   id = "primary-github"
}

Inside your Terraform code, you now have access to authentication variables. Reference the documentation for your chosen provider for more information on how to supply it with a token.

Workspace CLI

Use external-auth in the Coder CLI to access a token within the workspace:

coder external-auth access-token <USER_DEFINED_ID>

Git Authentication in Workspaces

Coder provides automatic Git authentication for workspaces through SSH authentication and Git-provider specific env variables.

When performing Git operations, Coder first attempts to use external auth provider tokens if available. If no tokens are available, it defaults to SSH authentication.

OAuth (external auth)

For Git providers configured with external authentication, Coder can use OAuth tokens for Git operations over HTTPS. When using SSH URLs (like git@github.com:organization/repo.git), Coder uses SSH keys as described in the SSH Authentication section instead.

For Git operations over HTTPS, Coder automatically injects an external auth provider token. This works through Git's GIT_ASKPASS mechanism, which Coder configures in each workspace.

GIT_ASKPASS tells Coder which Git host the operation is for, but never which provider to use. Coder resolves the provider in two steps:

  1. Coder considers only the providers that the workspace's template declares with data "coder_external_auth", and selects the one whose CODER_EXTERNAL_AUTH_<N>_REGEX matches the host.
  2. If every declared provider is configured and none of them match the host, including when the template declares no providers at all, Coder matches the host against all providers configured on the deployment. This fallback keeps hosts that the template never declares, such as an unrelated Git server, reachable from the workspace.

Because the first step is scoped to the template, two workspaces built from different templates receive their own template's token for the same Git host, regardless of the order the providers appear in the deployment configuration.

To use OAuth tokens for Git authentication over HTTPS:

  1. Complete the OAuth authentication flow (Login with GitHub, Login with GitLab).
  2. Use HTTPS URLs when interacting with repositories (https://github.com/organization/repo.git).
  3. Coder automatically handles authentication. You can perform your Git operations as you normally would.

Behind the scenes, Coder:

  • Stores your OAuth token securely in its database
  • Sets up GIT_ASKPASS at /tmp/coder.<random-string>/coder in your workspaces
  • Retrieves and injects the appropriate token when Git operations require authentication

To manually access these tokens within a workspace:

coder external-auth access-token <USER_DEFINED_ID>

SSH Authentication

Coder automatically generates an SSH key pair for each user that can be used for Git operations. When you use SSH URLs for Git repositories, for example, git@github.com:organization/repo.git, Coder checks for and uses an existing SSH key. If one is not available, it uses the Coder-generated one.

The coder gitssh command wraps the standard ssh command and injects the SSH key during Git operations. This works automatically when you:

  1. Clone a repository using SSH URLs
  2. Pull/push changes to remote repositories
  3. Use any Git command that requires SSH authentication

You must add the SSH key to your Git provider.

Add your Coder SSH key to your Git provider

  1. View your Coder Git SSH key:

    coder publickey
    
  2. Add the key to your Git provider accounts:

PKCE Support

PKCE (Proof Key for Code Exchange) is an OAuth 2.0 security extension that prevents authorization code interception attacks. Coder supports PKCE when acting as an OAuth client to external identity providers.

Coder will usually assume PKCE support is available with "S256" as the code challenge method. Manual configuration is available to override any default behavior.

# Enable PKCE with S256 (recommended when supported)
CODER_EXTERNAL_AUTH_0_PKCE_METHODS="S256"

# Disable PKCE entirely
CODER_EXTERNAL_AUTH_0_PKCE_METHODS="none"

Git-provider specific env variables

Azure DevOps

Azure DevOps requires the following environment variables:

CODER_EXTERNAL_AUTH_0_ID="primary-azure-devops"
CODER_EXTERNAL_AUTH_0_TYPE=azure-devops
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
# Ensure this value is your "Client Secret", not "App Secret"
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_AUTH_URL="https://app.vssps.visualstudio.com/oauth2/authorize"
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://app.vssps.visualstudio.com/oauth2/token"

Azure DevOps (via Entra ID)

Azure DevOps (via Entra ID) requires the following environment variables:

CODER_EXTERNAL_AUTH_0_ID="primary-azure-devops"
CODER_EXTERNAL_AUTH_0_TYPE=azure-devops-entra
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_AUTH_URL="https://login.microsoftonline.com/<TENANT ID>/oauth2/authorize"

Note

Your app registration in Entra ID requires the vso.code_write scope

Bitbucket Server

Bitbucket Server requires the following environment variables:

CODER_EXTERNAL_AUTH_0_ID="primary-bitbucket-server"
CODER_EXTERNAL_AUTH_0_TYPE=bitbucket-server
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxx
CODER_EXTERNAL_AUTH_0_AUTH_URL=https://bitbucket.example.com/rest/oauth2/latest/authorize

When configuring your Bitbucket OAuth application, set the redirect URI to https://example.com/external-auth/primary-bitbucket-server/callback. This callback path includes the value of CODER_EXTERNAL_AUTH_0_ID.

Gitea

CODER_EXTERNAL_AUTH_0_ID="gitea"
CODER_EXTERNAL_AUTH_0_TYPE=gitea
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
# If self managed, set the Auth URL to your Gitea instance
CODER_EXTERNAL_AUTH_0_AUTH_URL="https://gitea.com/login/oauth/authorize"

The redirect URI for Gitea should be https://coder.example.com/external-auth/gitea/callback.

GitHub

Use this section as a reference for environment variables to customize your setup or to integrate with an existing GitHub authentication.

For a more complete, step-by-step guide, follow the configure a GitHub OAuth app section instead.

CODER_EXTERNAL_AUTH_0_ID="primary-github"
CODER_EXTERNAL_AUTH_0_TYPE=github
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_REVOKE_URL=https://api.github.com/applications/<CLIENT ID>/grant

When configuring your GitHub OAuth application, set the authorization callback URL as https://example.com/external-auth/primary-github/callback, where primary-github matches your CODER_EXTERNAL_AUTH_0_ID value.

GitHub Enterprise

GitHub Enterprise requires the following environment variables:

CODER_EXTERNAL_AUTH_0_ID="primary-github"
CODER_EXTERNAL_AUTH_0_TYPE=github
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_VALIDATE_URL="https://github.example.com/api/v3/user"
CODER_EXTERNAL_AUTH_0_AUTH_URL="https://github.example.com/login/oauth/authorize"
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://github.example.com/login/oauth/access_token"

When configuring your GitHub Enterprise OAuth application, set the authorization callback URL as https://example.com/external-auth/primary-github/callback, where primary-github matches your CODER_EXTERNAL_AUTH_0_ID value.

GitLab self-managed

GitLab self-managed requires the following environment variables:

CODER_EXTERNAL_AUTH_0_ID="primary-gitlab"
CODER_EXTERNAL_AUTH_0_TYPE=gitlab
# This value is the "Application ID"
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_VALIDATE_URL="https://gitlab.example.com/oauth/token/info"
CODER_EXTERNAL_AUTH_0_AUTH_URL="https://gitlab.example.com/oauth/authorize"
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://gitlab.example.com/oauth/token"
CODER_EXTERNAL_AUTH_0_REVOKE_URL="https://gitlab.example.com/oauth/revoke"
CODER_EXTERNAL_AUTH_0_REGEX=gitlab\.example\.com

When configuring your GitLab OAuth application, set the redirect URI to https://example.com/external-auth/primary-gitlab/callback. Note that the redirect URI must include the value of CODER_EXTERNAL_AUTH_0_ID (in this example, primary-gitlab).

JFrog Artifactory

Visit the JFrog Artifactory guide for instructions on how to set up for JFrog Artifactory.

Self-managed Git providers

Custom authentication and token URLs should be used for self-managed Git provider deployments.

CODER_EXTERNAL_AUTH_0_AUTH_URL="https://github.example.com/oauth/authorize"
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://github.example.com/oauth/token"
CODER_EXTERNAL_AUTH_0_REVOKE_URL="https://github.example.com/oauth/revoke"
CODER_EXTERNAL_AUTH_0_VALIDATE_URL="https://example.com/oauth/token/info"
CODER_EXTERNAL_AUTH_0_REGEX=github\.company\.com

Note

The REGEX variable must be set if using a custom Git domain.

Custom scopes

Optionally, you can request custom scopes:

CODER_EXTERNAL_AUTH_0_SCOPES="repo:read repo:write write:gpg_key"

OAuth provider

Configure a GitHub OAuth app

  1. Create a GitHub App

    • Set the authorization callback URL to https://coder.example.com/external-auth/primary-github/callback, where primary-github is the value you set for CODER_EXTERNAL_AUTH_0_ID.
    • Deactivate Webhooks.
    • Enable fine-grained access to specific repositories or a subset of permissions for security.

    Register GitHub App

  2. Adjust the GitHub app permissions. You can use more or fewer permissions than are listed here, this example allows users to clone repositories:

    Adjust GitHub App Permissions

    Name Permission Description
    Contents Read & Write Grants access to code and commit statuses.
    Pull requests Read & Write Grants access to create and update pull requests.
    Workflows Read & Write Grants access to update files in .github/workflows/.
    Metadata Read-only Grants access to metadata written by GitHub Apps.
    Members Read-only Grants access to organization members and teams.
  3. Install the App for your organization. You may select a subset of repositories to grant access to.

    Install GitHub App

  4. Make the app installable by other users. In the app's Advanced tab, select Make this GitHub App public.

    Without this, anyone outside the app's owning account or owning organization gets a GitHub 404 when they select Link GitHub in Coder. Each user must also install the app on their own account before linking. To surface an Install GitHub App link in the Coder UI, set the following environment variable:

    CODER_EXTERNAL_AUTH_0_APP_INSTALL_URL=https://github.com/apps/<your-app-slug>/installations/new
    

Multiple External Providers (Premium)

Note

Configuring more than one external authentication provider requires a Premium license. For more details, contact your account team.

Below is an example configuration with multiple providers:

Important

To support regex matching for paths like github\.com/org, add the following git config line to the Coder agent startup script:

git config --global credential.useHttpPath true
# Provider 1) github.com
CODER_EXTERNAL_AUTH_0_ID=primary-github
CODER_EXTERNAL_AUTH_0_TYPE=github
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_REGEX=github\.com/org

# Provider 2) github.example.com
CODER_EXTERNAL_AUTH_1_ID=secondary-github
CODER_EXTERNAL_AUTH_1_TYPE=github
CODER_EXTERNAL_AUTH_1_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_1_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_1_REGEX=github\.example\.com
CODER_EXTERNAL_AUTH_1_AUTH_URL="https://github.example.com/login/oauth/authorize"
CODER_EXTERNAL_AUTH_1_TOKEN_URL="https://github.example.com/login/oauth/access_token"
CODER_EXTERNAL_AUTH_1_REVOKE_URL="https://github.example.com/login/oauth/revoke"
CODER_EXTERNAL_AUTH_1_VALIDATE_URL="https://github.example.com/api/v3/user"

When Coder can't resolve a single provider

When several providers serve the same Git host, HTTPS Git operations resolve the provider from the workspace template's declared providers, as described in OAuth (external auth). Coder stops in two cases rather than pick a provider the template didn't ask for. In both, the request fails and coder gitaskpass prints a warning and falls back to Git's own credential behavior, so the Git operation prompts for credentials or fails instead of using an unexpected token.

  • Several of the template's declared providers match the host. Coder can't tell which one the operation needs, so it returns an HTTP 404 naming each match. Give the providers non-overlapping CODER_EXTERNAL_AUTH_<N>_REGEX values so that only one matches the host, or fetch a token with an explicit provider ID using coder external-auth access-token <USER_DEFINED_ID> in your template's startup script.
  • The template declares a provider that the deployment no longer configures, and none of its other declared providers match the host. This happens when a provider is renamed or removed after a template started declaring it. Coder returns an HTTP 404 naming the missing provider instead of falling back to a provider the template never declared. A provider that the template declares and the deployment still configures keeps serving its own host, so only the hosts that relied on the missing provider are affected. Restore that provider's configuration, or update the template to declare a provider that the deployment configures.