| 1 | --- |
| 2 | title: Single sign-on |
| 3 | description: Connect a workspace to your identity provider over SAML 2.0, OpenID Connect or OAuth 2.0, let its people join, and require it of members. On every paid plan. |
| 4 | --- |
| 5 | |
| 6 | A workspace can connect to its own identity provider, such as Okta, |
| 7 | Microsoft Entra ID, Google Workspace, Auth0, OneLogin or JumpCloud, over |
| 8 | **OpenID Connect**, **SAML 2.0** or plain **OAuth 2.0**. It comes with |
| 9 | every paid plan at no extra price. A free workspace sees the page with |
| 10 | everything turned off until an owner starts the plan. |
| 11 | |
| 12 | Single sign-on never signs anyone in to g1t. People sign in to g1t as |
| 13 | themselves, with their password, GitHub and two-factor authentication as |
| 14 | usual, then sign in through your provider to use the workspace. The |
| 15 | first time they do, g1t links who they are at your provider to their g1t |
| 16 | account. Their personal repositories and other workspaces are never your |
| 17 | provider's to open. |
| 18 | |
| 19 | ## Set it up |
| 20 | |
| 21 | Owners only, at **Workspace → Access → Single sign-on**, |
| 22 | `g1t.sh/<workspace>/-/sso`. |
| 23 | |
| 24 | 1. Choose the protocol. Use **OpenID Connect** when your provider offers |
| 25 | it; **SAML 2.0** for providers set up for SAML apps; **OAuth 2.0** for |
| 26 | one with neither. |
| 27 | 2. In your provider, create an app with the addresses the page shows: |
| 28 | |
| 29 | | | | |
| 30 | | --- | --- | |
| 31 | | Redirect URI (OpenID Connect, OAuth 2.0) | `https://g1t.sh/auth/sso/<workspace>/callback` | |
| 32 | | Entity id, audience (SAML) | `https://g1t.sh/auth/sso/<workspace>/metadata` | |
| 33 | | Assertion consumer service URL, HTTP-POST (SAML) | `https://g1t.sh/auth/sso/<workspace>/saml` | |
| 34 | | Metadata (SAML) | `https://g1t.sh/auth/sso/<workspace>/metadata` | |
| 35 | |
| 36 | 3. Copy what the provider gives you back into the page: |
| 37 | |
| 38 | - **OpenID Connect**: the issuer (where its |
| 39 | `/.well-known/openid-configuration` lives), the client id and the |
| 40 | client secret. Scopes default to `openid email profile`. |
| 41 | - **SAML 2.0**: the provider's entity id (its issuer), its single |
| 42 | sign-on URL, and its signing certificate (PEM). Paste more than one |
| 43 | while you rotate certificates. |
| 44 | - **OAuth 2.0**: the authorization, token and userinfo URLs, the client |
| 45 | id and secret, the scopes, and which claims of the userinfo answer |
| 46 | are the person's stable id (`sub`, then `id`, by default) and their |
| 47 | address (`email`). |
| 48 | |
| 49 | 4. Choose **Set up single sign-on**. For OpenID Connect g1t reads the |
| 50 | issuer's discovery document first, and says so if it cannot. |
| 51 | |
| 52 | The client secret is kept encrypted and never shown again; leave it empty |
| 53 | when you save other changes to keep it. |
| 54 | |
| 55 | ## Test it |
| 56 | |
| 57 | Choose **Test sign-in**. g1t sends you to your provider and back, links |
| 58 | your account, and marks the connection **Tested** with your name. You |
| 59 | must have signed in through it yourself before you can require it of |
| 60 | everyone. |
| 61 | |
| 62 | ## Let people join |
| 63 | |
| 64 | With **Let people from your provider join** on (the default), anyone your |
| 65 | provider signs in becomes a member of the workspace when they first sign |
| 66 | in through it, with no invitation: send them to |
| 67 | `g1t.sh/auth/sso/<workspace>`. They need a g1t account with a confirmed |
| 68 | email address first, and the workspace's other rules still apply, such as |
| 69 | [requiring two-factor authentication](/guides/workspaces/#require-two-factor-authentication). |
| 70 | Turn it off and only people who are already members can sign in through |
| 71 | it. |
| 72 | |
| 73 | ## Require it |
| 74 | |
| 75 | Turn on **Require single sign-on** under **Test and require**. From then |
| 76 | on a member uses the workspace only: |
| 77 | |
| 78 | - **On the site**, from a session that signed in through your provider in |
| 79 | the last day. After a day they go through it again, which is usually |
| 80 | instant: your provider remembers them. |
| 81 | - **With an access token, SSH key or git credentials**, once they have |
| 82 | linked their account. |
| 83 | |
| 84 | A member who has not keeps their place but cannot use the workspace: its |
| 85 | private repositories, pages and API answer as if they were not a member, |
| 86 | including repositories they have a role on directly, and every page shows |
| 87 | a notice with a link to sign in through your provider. |
| 88 | |
| 89 | **Owners are never held out**, so a provider that breaks or a certificate |
| 90 | that expires never locks you out of your own workspace. Outside |
| 91 | collaborators are not asked: they are not your provider's people. |
| 92 | |
| 93 | ## Linked people |
| 94 | |
| 95 | The page lists everyone who has signed in through your provider, who they |
| 96 | are there, and when they last did. **Unlink** someone to let them link |
| 97 | another account at your provider; their sessions go through it again. |
| 98 | Each account at your provider links to one g1t account, and each g1t |
| 99 | account to one account at your provider. |
| 100 | |
| 101 | Changing the provider itself (the protocol, the issuer, or the SAML entity |
| 102 | id) unlinks everyone and stops requiring it, until someone has signed in |
| 103 | through the new one. Changing the client, the scopes or the certificate |
| 104 | keeps everyone linked. |
| 105 | |
| 106 | ## What g1t checks |
| 107 | |
| 108 | - **OpenID Connect and OAuth 2.0** use the authorization code flow with |
| 109 | PKCE. For OpenID Connect, the ID token must be issued by your issuer, for |
| 110 | your client id, unexpired, and answer this sign-in's nonce. g1t receives |
| 111 | it straight from your provider's token endpoint over TLS. |
| 112 | - **SAML** responses must be signed, the assertion or the whole response, |
| 113 | with RSA and SHA-256, SHA-384 or SHA-512, by one of the certificates you |
| 114 | saved. The response must answer the request g1t sent (sign-ins started |
| 115 | at your provider are refused), be addressed to the workspace's assertion |
| 116 | consumer service, name g1t's entity id as the audience, come from your |
| 117 | provider's entity id, and be in its validity window. Leave the assertion |
| 118 | unencrypted: it reaches g1t over HTTPS. |
| 119 | |
| 120 | ## If the plan ends |
| 121 | |
| 122 | The settings stay. Members cannot sign in through your provider until the |
| 123 | plan starts again, so a workspace that requires it holds its members out |
| 124 | meanwhile; owners can still turn the requirement off. Turning it off and |
| 125 | removing it never need a plan. |
| 126 | |
| 127 | ## Remove it |
| 128 | |
| 129 | **Danger zone → Remove** forgets your provider and who everyone is there, |
| 130 | and stops requiring it. Members keep their place in the workspace. |
| 131 | |
| 132 | ## Audit log |
| 133 | |
| 134 | Recorded in the workspace's [audit log](/guides/audit-log/): |
| 135 | `sso.created`, `sso.updated`, `sso.deleted`, `sso.required`, |
| 136 | `sso.not_required`, `sso.identity_linked`, `sso.identity_unlinked`, and |
| 137 | `member.joined_by_sso`. |
| 138 | |
| 139 | ## API and MCP |
| 140 | |
| 141 | Owners can do all of it with an access token, through the REST API or the |
| 142 | `workspace` tool's actions on the MCP server: |
| 143 | |
| 144 | | | REST | MCP action | Scope | |
| 145 | | --- | --- | --- | --- | |
| 146 | | Read it, with where to point the provider | `GET /workspaces/{workspace}/sso` | `get_sso` | `workspace:read` | |
| 147 | | Set it up or change it | `PUT /workspaces/{workspace}/sso` | `configure_sso` | `workspace:admin` | |
| 148 | | Require it, or stop | `PUT /workspaces/{workspace}/sso/required` | `set_sso_required` | `workspace:admin` | |
| 149 | | Remove it | `DELETE /workspaces/{workspace}/sso` | `delete_sso` | `workspace:admin` | |
| 150 | | The linked people | `GET /workspaces/{workspace}/sso/identities` | `list_sso_identities` | `access:read` | |
| 151 | | Unlink someone | `DELETE /workspaces/{workspace}/sso/identities/{username}` | `unlink_sso_identity` | `access:admin` | |
| 152 | |
| 153 | `configure_sso` takes `protocol` (`oidc`, `oauth2` or `saml`) and the |
| 154 | protocol's fields as the page names them: `issuer`, `client_id`, |
| 155 | `client_secret`, `scopes`; `authorization_url`, `token_url`, |
| 156 | `userinfo_url`, `subject_claim`, `email_claim`; `idp_entity_id`, |
| 157 | `idp_sso_url`, `certificate` (PEM). Its answer, like `get_sso`'s, has |
| 158 | `service_provider`: the `redirect_uri`, `entity_id`, `acs_url`, |
| 159 | `metadata_url` and `sign_in_url` to give your provider and your people. |
| 160 | The client secret is never returned. |
| 161 | |
| 162 | ```sh |
| 163 | curl -X PUT https://api.g1t.sh/workspaces/acme/sso -H "Authorization: Bearer $G1T_TOKEN" -d '{"protocol": "oidc", "issuer": "https://acme.okta.com", "client_id": "0oa1b2c3", "client_secret": "…"}' |
| 164 | ``` |
| 165 | |
| 166 | Signing in through the provider is a trip in the browser, so it has no |
| 167 | API: send people to `sign_in_url`. |
| 168 | |
| 169 | ## SCIM |
| 170 | |
| 171 | With SCIM, your provider tells g1t who is assigned to the workspace, and |
| 172 | who is not any more: |
| 173 | |
| 174 | - Only people your provider has assigned, and not deactivated, can sign in |
| 175 | through it. Someone else is told to ask whoever runs your provider. |
| 176 | - Someone it unassigns, deactivates or deletes leaves the workspace at |
| 177 | once: their membership, their roles on its repositories and teams, their |
| 178 | link, and their sessions through it. Their g1t account is theirs and |
| 179 | stays. **Owners are never removed this way**; the audit log says so, and |
| 180 | you remove one by hand if they should go. |
| 181 | - People, not groups. Assign a group to the app in your provider and its |
| 182 | members are provisioned one by one; turn off pushing groups. |
| 183 | |
| 184 | To turn it on, open **Single sign-on → SCIM provisioning** and choose |
| 185 | **Turn on SCIM**. Give your provider: |
| 186 | |
| 187 | | | | |
| 188 | | --- | --- | |
| 189 | | SCIM base URL (tenant URL) | `https://api.g1t.sh/scim/v2/<workspace>` | |
| 190 | | Authentication | Bearer token: the SCIM token, shown once | |
| 191 | | Unique identifier | `userName` | |
| 192 | | Supported | Create, update (PUT and PATCH), deactivate and delete users; filtering by `userName`, `externalId`, `id` or `emails.value` | |
| 193 | |
| 194 | Whoever signs in through your provider is matched to the person it |
| 195 | assigned by their id there, their userName, or their address. **Make a new |
| 196 | token** replaces the old one at once; **Turn off SCIM** forgets who was |
| 197 | assigned and removes nobody. |
| 198 | |
| 199 | Through the API: `POST /workspaces/{workspace}/sso/scim` (`enable_scim`) |
| 200 | answers with the token and `scim_url`; `DELETE` turns it off |
| 201 | (`disable_scim`). `get_sso` lists who is assigned under `scim`. Recorded as |
| 202 | `sso.scim_enabled`, `sso.scim_token_rotated`, `sso.scim_disabled`, |
| 203 | `sso.scim_user_provisioned`, `sso.scim_user_activated`, |
| 204 | `sso.scim_user_deactivated`, `sso.scim_user_deleted`, |
| 205 | `member.removed_by_scim` and `sso.scim_owner_kept`, with `scim` as the |
| 206 | actor. |