# Set up Single Sign-On with GitHub

This guide explains how to set up GitHub Single Sign On (SSO) so you can automatically map teams in your GitHub organization to users and roles in Teleport.

## How it works

A Teleport administrator creates a GitHub authentication connector on the Teleport Auth Service backend and requires Teleport users to authenticate through GitHub by creating a cluster authentication preference.

When a user authenticates to Teleport, they receive instructions to finish authenticating using GitHub. The Teleport Auth Service receives an OAuth 2.0 access token from GitHub and authenticates the token. (The GitHub authentication connector acts as an OAuth 2.0 [client](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1).)

The Auth Service then issues short-lived TLS and SSH certificates to the user by reading data from the OAuth 2.0 access token. In particular, the Auth Service:

- Maps the user's GitHub teams to Teleport roles in order to assign the user permissions.
- Assigns the user's Teleport username to their GitHub username.

---

WARNING

GitHub usernames are not formatted as email addresses. As a result, any Teleport plugin that expects to send email to a user based on their Teleport username will not work as expected. For example, the [PagerDuty Access Request plugin](https://goteleport.com/docs/identity-governance/access-requests/plugins/pagerduty.md) has this limitation.

---

## Prerequisites

- A GitHub organization with at least one team.

  In Teleport Community Edition, this organization must not have external SSO set up, or Teleport will refuse to create the GitHub authentication connector.

  In Teleport Enterprise and Enterprise Cloud, organization can be hosted from either GitHub Cloud or GitHub Enterprise Server.

- Teleport role with access to maintaining `github` resources for using `tctl` from the Desktop. This is available in the default `editor` role.

* A running Teleport cluster. If you want to get started with Teleport, [sign up](https://goteleport.com/signup) for a free trial or [set up a demo environment](https://goteleport.com/docs/get-started/deploy-community.md).

* The `tctl` and `tsh` clients.

  Installing `tctl` and `tsh` clients

  1. Determine the version of your Teleport cluster. The `tctl` and `tsh` clients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at `/v1/webapi/find` and use a JSON query tool to obtain your cluster version. Replace teleport.example.com:443 with the web address of your Teleport Proxy Service:

     ```
     $ TELEPORT_DOMAIN=teleport.example.com:443
     $ TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"
     ```

  2. Follow the instructions for your platform to install `tctl` and `tsh` clients:

     **Mac**

     Download the signed macOS .pkg installer for Teleport, which includes the `tctl` and `tsh` clients:

     ```
     $ curl -O https://cdn.teleport.dev/teleport-${TELEPORT_VERSION?}.pkg
     ```

     In Finder double-click the `pkg` file to begin installation.

     ---

     DANGER

     Using Homebrew to install Teleport is not supported. The Teleport package in Homebrew is not maintained by Teleport and we can't guarantee its reliability or security.

     ---

     **Windows - Powershell**

     ```
     $ curl.exe -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-windows-amd64-bin.zip
     Unzip the archive and move the `tctl` and `tsh` clients to your %PATH%
     NOTE: Do not place the `tctl` and `tsh` clients in the System32 directory, as this can cause issues when using WinSCP.
     Use %SystemRoot% (C:\Windows) or %USERPROFILE% (C:\Users\<username>) instead.
     ```

     **Linux**

     All of the Teleport binaries in Linux installations include the `tctl` and `tsh` clients. For more options (including RPM/DEB packages and downloads for i386/ARM/ARM64) see our [installation page](https://goteleport.com/docs/installation.md).

     ```
     $ curl -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ tar -xzf teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ cd teleport
     $ sudo ./install
     Teleport binaries have been copied to /usr/local/bin
     ```

- To check that you can connect to your Teleport cluster, sign in with `tsh login`, then verify that you can run `tctl` commands using your current credentials. For example, run the following command, assigning teleport.example.com to the domain name of the Teleport Proxy Service in your cluster and email\@example.com to your Teleport username:
  ```
  $ tsh login --proxy=teleport.example.com --user=email@example.com
  $ tctl status
  Cluster  teleport.example.com
  Version  19.0.0-dev
  CA pin   sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678
  ```
  If you can connect to the cluster and run the `tctl status` command, you can use your current credentials to run subsequent `tctl` commands from your workstation. If you host your own Teleport cluster, you can also run `tctl` commands on the computer that hosts the Teleport Auth Service for full permissions.

## Step 1/3. Create a GitHub OAuth app

Create and register a GitHub OAuth App. When you do so, ensure that your OAuth App's "Authentication callback URL" is the following:

```
https://PROXY_ADDRESS/v1/webapi/github/

```

Replace `PROXY_ADDRESS` with be the public address of the Teleport Proxy Service or your Teleport Cloud workspace URL (e.g., `example.teleport.sh`). If you have a self-hosted cluster with multiple public addresses for the Teleport Proxy Service (the value of `proxy_service.public_addr` in the Teleport configuration file), ensure that this address points to the first one listed.

The app must have the `read:org` scope in order to be able to read org and team membership details.

Instructions for creating a GitHub OAuth app are available in [GitHub's documentation](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)

Create a client secret to use along with the client ID in the next step:

![A GitHub Oauth App, highlighting Client ID and secret](/docs/assets/images/github-app-vars-3bd1d246cdcaec877caa0ad84e678924.png)

## Step 2/3. Connect GitHub to Teleport

In this section, you will create an authentication connector that provides Teleport with the information it needs to connect to GitHub and issue certificates to users.

### Assign a role mapping

When a user authenticates to Teleport, the Teleport Auth Service issues SSH and TLS certificates to the user that contain the user's Teleport roles.

For SSO authentication connectors, the Auth Service determines which roles to encode in the certificate by reading the authentication connector's **role mapping**. The role mapping indicates which Teleport roles to assign based on data that your identity provider stores about the user.

When you configure the GitHub authentication connector using the `tctl` CLI, a role mapping takes the following format:

```
<github_org_name>,<github_team_name>,<teleport_role_1>,<teleport_role_2>,...,<teleport_role_n>

```

For example, the following role mapping means that any user with the team `admins` in the `gravitational` org receives the Teleport roles `editor` and `auditor`:

```
gravitational,admins,editor,auditor

```

For the purpose of this guide, assign two separate role mappings:

- A more permissive role mapping: github\_mapping\_1
- A more restrictive role mapping: github\_mapping\_2

GitHub organizations and teams should be referred to by their slug, not display name. To create the team slug, GitHub replaces special characters in the name string, changes all words to lowercase, and replaces spaces with a `-` separator.

For example, `My TEam Näme` would become `my-team-name`. The organization slug is treated the same except the organization is not changed to lowercase. You can confirm the slug in GitHub Web application URLs or via the GitHub API. Example: navigate to the team `My Team` in the GitHub web application. The URL `https://github.com/orgs/org-name/teams/my-team` shows the slug is `my-team`.

### Configure a GitHub authentication connector

In this section, you will define a GitHub authentication connector using `tctl`.

On your workstation, create a file called `client-secret.txt` consisting only of your client secret.

Update this example command with:

- Your OAuth app's client ID and client secret created during the previous step.
- The roles you want to map from your GitHub organization to Teleport roles. Roles are defined in the **Repository roles** section of your organization's settings.

See [tctl sso configure github](https://goteleport.com/docs/reference/cli/tctl.md) for a full reference of flags for this command:

```
$ tctl sso configure github \
--id=GITHUB-CLIENT-ID \
--secret=$(cat client-secret.txt) \
--teams-to-roles=github_mapping_1 \
--teams-to-roles=github_mapping_2 \
> github.yaml
```

The contents of `github.yaml` should resemble the following:

**Teleport Community Edition**

```
kind: github
metadata:
  name: github
spec:
  api_endpoint_url: ""
  client_id: <client-id>
  client_secret: <client-secret>
  display: GitHub
  endpoint_url: ""
  redirect_url: https://<proxy-address>/v1/webapi/github/callback
  teams_to_logins: null
  teams_to_roles:
    - organization: ORG-NAME
      roles:
        - access
        - editor
      team: github-team
version: v3

```

**Commercial**

```
kind: github
metadata:
  name: github
spec:
  api_endpoint_url: https://api.github.com
  client_id: <client-id>
  client_secret: <client-secret>
  display: GitHub
  endpoint_url: https://github.com
  redirect_url: https://<proxy-address>/v1/webapi/github/callback
  teams_to_logins: null
  teams_to_roles:
    - organization: org-name
      roles:
        - access
        - editor
        - reviewer
      team: github-team
version: v3

```

Mapping multiple roles

You can add multiple instances of the `--teams-to-roles` flag or edit the connector file to define multiple mappings. For example:

```
$ tctl sso configure github \
--id=GITHUB-CLIENT-ID \
--secret=$(cat client-secret.txt) \
--teams-to-roles=ORG-NAME,github-team,access,editor \
--teams-to-roles="org-name,administrators,admins \
--teams-to-roles="different-org,developers,dev \
> github.yaml
```

```
spec.
  teams_to_roles:
    - organization: org-name
      roles:
        - access
        - editor
      team: github-team
    - organization: org-name
      roles:
        - admins
      team: administrators
    - organization: different-org
      roles:
        - devs
      team: developers

```

Self-hosting GitHub Enterprise?

For self-hosted GitHub Enterprise servers, you can specify the server instance endpoints with the `--endpoint-url`, `--api-endpoint-url` parameters, replacing github-enterprise-server-address with your endpoint URL and api-github-enterprise-server-address with your API endpoint URL:

```
$ tctl sso configure github \
--id=GITHUB-CLIENT-ID \
--secret=$(cat client-secret.txt) \
--teams-to-roles=ORG-NAME,github-team,access,editor \
--endpoint-url=https://github-enterprise-server-address
--api-endpoint-url=https://api-github-enterprise-server-address
> github.yaml
```

```
...
spec:
  ...
  api_endpoint_url: https://<api-github-enterprise-server-address>
  endpoint_url: https://<github-enterprise-server-address>
  ...

```

### Test the connector

You can test the connector configuration before applying it to your cluster. This is strongly encouraged to avoid interruption to active clusters:

```
$ cat github.yaml | tctl sso test
If browser window does not open automatically, open it by clicking on the link:
 http://127.0.0.1:52690/35714f6b-...
Success! Logged in as: alice
--------------------------------------------------------------------------------
Authentication details:
   roles:
   - access
   - editor
   traits:
     github_teams:
     - admins
     kubernetes_groups: null
     kubernetes_users: null
     logins:
     - alice
   username: alice
--------------------------------------------------------------------------------
[GitHub] Received claims:
organization_to_teams:
  Octocats:
  - admins
teams:
- admins
username: alice

--------------------------------------------------------------------------------
[GitHub] Connector team to roles mapping:
- organization: Octocats
  roles:
  - access
  - editor
  team: admins

--------------------------------------------------------------------------------
For more details repeat the command with --debug flag.
```

### Create the connector

Finally, create the connector using `tctl`:

```
$ tctl create -f github.yaml
authentication connector "github" has been created
```

---

TIP

When going through the GitHub authentication flow for the first time, the application must be granted access to all organizations that are present in the "teams to logins" mapping, otherwise Teleport will not be able to determine team memberships for these organizations.

---

After a user authenticates, Teleport will add the user's GitHub username to their `internal.logins` trait for their Teleport session. The preset `access` role has this trait variable configured to include the GitHub user as an authorized SSH login. Here is an example role configuration snippet using the trait variable:

```
  allow:
    # List of allowed SSH logins
    logins: ['{{internal.logins}}', ubuntu, debian]

    # List of node labels that users can SSH into
    node_labels:
      '*': '*'

```

You can now log in with Teleport using GitHub SSO. Run the following to log out of Teleport and log in again using GitHub SSO.

**Self-Hosted**

```
$ tsh logout
Logged out all users from all proxies.
$ tsh login --proxy=tele.example.com --auth=github
If browser window does not open automatically, open it by clicking on the link:
 http://127.0.0.1:56334/6bf976e6-a4be-4898-94eb-8a7b01af2158
```

**Teleport Enterprise Cloud**

```
$ tsh logout
Logged out all users from all proxies.
$ tsh login --proxy=mytenant.teleport.sh --auth=github
If browser window does not open automatically, open it by clicking on the link:
 http://127.0.0.1:56334/6bf976e6-a4be-4898-94eb-8a7b01af2158
```

You can also log to the web UI using GitHub by clicking **Other sign-in options** at the login screen.

When you sign in for the first time, you will see a prompt to authorize your GitHub OAuth app:

![GitHub SSO authorization view](/docs/assets/images/github-sso-auth-screen-9201472bb34dbc0ebad02a6dba04f2f5.jpg)

Teleport will request only the `read:org` OAuth scope. Read more about OAuth scopes in GitHub's documentation: [GitHub OAuth scopes](https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/)

After logging in, you will receive a "Login Successful" window, which you can then close.

You will receive the details of your user session within the CLI:

**Teleport Community Edition**

```
> Profile URL:        https://tele.example.com:443
  Logged in as:       jeff
  Cluster:            tele.example.com
  Roles:              access
  Logins:             jeff, ubuntu, debian
  Kubernetes:         enabled
  Kubernetes users:   dev
  Kubernetes groups:  developer
  Valid until:        2023-03-08 17:13:50 -0600 CST [valid for 7h51m0s]
  Extensions:         permit-port-forwarding, permit-pty, private-key-policy
```

**Teleport Enterprise**

```
> Profile URL:        https://tele.example.com:443
  Logged in as:       jeff
  Cluster:            tele.example.com
  Roles:              access, requester
  Logins:             jeff, ubuntu, debian
  Kubernetes:         enabled
  Kubernetes users:   dev
  Kubernetes groups:  developer
  Valid until:        2023-03-08 17:13:50 -0600 CST [valid for 7h51m0s]
  Extensions:         permit-port-forwarding, permit-pty, private-key-policy
```

**Teleport Enterprise Cloud**

```
> Profile URL:        https://mytenant.teleport.sh:443
  Logged in as:       jeff
  Cluster:            mytenant.teleport.sh
  Roles:              access, requester
  Logins:             jeff, ubuntu, debian
  Kubernetes:         enabled
  Kubernetes users:   dev
  Kubernetes groups:  developer
  Valid until:        2023-03-08 17:13:50 -0600 CST [valid for 7h51m0s]
  Extensions:         permit-port-forwarding, permit-pty, private-key-policy

```

## Step 3/3. Configure authentication preferences

In the previous step we signed in to Teleport using GitHub credentials by specifying GitHub as our auth type. By editing the `cluster_auth_preference` resource we can make it the default auth type.

Edit the existing `cluster_auth_preference` resource using `tctl`:

```
$ tctl edit cap
```

A temporary file will open in your default editor with your `cluster_auth_preference` definition.

Ensure that `cap.yaml` includes the following content:

```
kind: cluster_auth_preference
metadata:
  name: cluster-auth-preference
spec:
  type: github
  webauthn:
    rp_id: 'example.teleport.sh'
version: v2

```

For `rp_id`, use the public address of your Teleport Proxy Service or Teleport Cloud workspace.

When you save and close the temporary file, `tctl` will update the resource:

```
cluster auth preference has been updated
```

Static configuration file

You can also edit your Teleport configuration file to include the following:

```
# Snippet from /etc/teleport.yaml
auth_service:
  authentication:
    type: github

```

After logging out of `tsh`, you can log back in without specifying `--auth=github`. You will automatically be redirected to the GitHub auth flow.

## Next steps

Now that you have connected Teleport to your identity provider, you can customize the way Teleport includes IdP data in Teleport roles.

With **role templates**, you can include user data from your IdP directly in Teleport roles. If you use the `external` template variable in the value of a role field, Teleport passes that value from your IdP. In this example, all of the role options you can use for allowing users to assume certain principals on remote systems come from your IdP:

```
kind: role
version: v7
metadata:
  name: sso-users
spec:
  allow:
    logins: ['{{external.logins}}']
    aws_role_arns: ['{{external.aws_role_arns}}']
    azure_identities: ['{{external.azure_identities}}']
    db_names: ['{{external.db_names}}']
    db_roles: ['{{external.db_roles}}']
    db_users: ['{{external.db_users}}']
    desktop_groups: ['{{external.desktop_groups}}']
    gcp_service_accounts: ['{{external.gcp_service_accounts}}']
    host_groups: ['{{external.host_groups}}']
    host_sudoers: ['{{external.host_sudoers}}']
    kubernetes_groups: ['{{external.kubernetes_groups}}']
    kubernetes_users: ['{{external.kubernetes_users}}']
    windows_desktop_logins: ['{{external.windows_desktop_logins}}']

```

For more information on using the `external` template variable, see [Role Templates](https://goteleport.com/docs/zero-trust-access/rbac-get-started/role-templates.md).

For an explanation of the fields listed above, see the [Role Reference](https://goteleport.com/docs/reference/access-controls/roles.md).

If you need to transform your IdP user data before you include it in Teleport roles, you can do so using **Login Rules**. Login Rules allow you to include external traits within Teleport roles even if your IdP provides user data in a different format than the one expected by Teleport. Read more about [Login Rules](https://goteleport.com/docs/zero-trust-access/sso/login-rules.md).

## Troubleshooting

Troubleshooting SSO configuration can be challenging. Usually a Teleport administrator must be able to:

- Be able to see what SAML/OIDC claims and values are getting exported and passed by the SSO provider to Teleport.
- Be able to see how Teleport maps the received claims to role mappings as defined in the connector.
- For self-hosted Teleport Enterprise clusters, ensure that HTTP/TLS certificates are configured properly for both the Teleport Proxy Service and the SSO provider.

If something is not working, we recommend to:

- Double-check the host names, tokens and TCP ports in a connector definition.

### Using the Web UI

If you get "access denied" or other login errors, the number one place to check is the Audit Log. To view the recording, select **Audit** in the Teleport Web UI, then click **Session Recordings** in the menu.

![Audit Log Entry for SSO Login error](/docs/assets/images/teleportauditlogssofailed-4e41f7109e748750a157651c2f702dac.png)

Example of a user being denied because the role `clusteradmin` wasn't set up:

```
{
  "code": "T1001W",
  "error": "role clusteradmin is not found",
  "event": "user.login",
  "message": "Failed to calculate user attributes.\n\trole clusteradmin is not found",
  "method": "oidc",
  "success": false,
  "time": "2024-11-07T15:41:25.584Z",
  "uid": "71e46f17-d611-48bb-bf5e-effd90016c13"
}

```

### Teleport does not show the expected Nodes

When the Teleport Auth Service receives a request to list Teleport-connected resources (e.g., to display resources in the Web UI or via `tsh ls`), it only returns the resources that the current user is authorized to view.

For each resource in the user's Teleport cluster, the Auth Service applies the following checks in order and, if one check fails, hides the resource from the user:

- None of the user's roles contain a `deny` rule that matches the resource's labels.
- At least one of the user's roles contains an `allow` rule that matches the resource's labels.

If you are not seeing resources when expected, make sure that your user's roles include the appropriate `allow` and `deny` rules as documented in the [Access Controls Reference](https://goteleport.com/docs/reference/access-controls/roles.md).

When configuring SSO, ensure that the identity provider is populating each user's traits correctly. For a user to see a Node in Teleport, the result of populating a template variable in a role's `allow.logins` must match at least one of a user's `traits.logins`.

In this example a user will have usernames `ubuntu`, `debian` and usernames from the SSO trait `logins` for Nodes that have a `env: dev` label. If the SSO trait username is `bob` then the usernames would include `ubuntu`, `debian`, and `bob`.

```
kind: role
metadata:
  name: example-role
spec:
  allow:
    logins: ['{{external.logins}}', ubuntu, debian]
    node_labels:
      'env': 'dev'
version: v5

```

### Single sign-on fails with OIDC

When encountering the error message **"Failed to verify JWT: oidc: unable to verify JWT signature: no matching keys"**, it typically indicates a discrepancy between the algorithm used to sign the JWT token and the algorithm(s) supported by the JSON Web Key Set (JWKS). Specifically, the token might be signed with one algorithm, e.g., HS256, while the JWKS only lists keys for a different algorithm. e.g., RS256. This issue predominantly arises when using identity providers that offer extremely low-level functionality.

Here are some things to check:

- Verify the JWT header specifies the correct signing algorithm. This should match one of the algorithms listed in the keys section of the JWKS endpoint response.
- Ensure the JWKS endpoint is returning all relevant public keys. Sometimes key rotation can cause valid keys to be omitted.

To resolve the issue, align the JWT algorithm header with a supported algorithm in the JWKS. Rotate keys if necessary. Verify the JWKS only publishes the active public keys. With proper configuration, the signature should validate successfully.

### Teams Not Mapping to Roles

If you are not seeing teams mapping to roles as expected confirm you are using the slug of the organizations and teams in the connector. To create the slug, GitHub replaces special characters in the name string, changes all words to lowercase, and replaces spaces with a `-` separator. For example, "My TEam Näme" would become `my-team-name`. The GitHub Web application URLs and GitHub API can provide the slug.

Example: navigate to the team `My Team` in the GitHub web application. The URL `https://github.com/orgs/org-name/teams/my-team` shows the slug is `my-team`. Update the teams to roles mapping.

```
  teams_to_roles:
    - organization: my-org
      roles:
        - access
        - editor
        - reviewer
      team: my-team

```
