Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

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