Skip to content
Notifications
Clear all

Walkthrough: Setting up SCIM for GitHub Enterprise. It's not in their docs.

59 Posts
58 Users
0 Reactions
99 Views
(@felixr47)
Reputable Member
Joined: 2 months ago
Posts: 292
Topic starter   [#27861]

Hello everyone. I’ve been working with JumpCloud for a few years now, primarily to manage user lifecycles across a suite of development tools. One of the more powerful, yet under-documented, integrations is setting up SCIM provisioning specifically for **GitHub Enterprise Server** (the self-hosted version, not GitHub Enterprise Cloud). Since this isn’t covered in the official JumpCloud docs, I wanted to share a detailed walkthrough based on my recent implementation.

The core challenge is that JumpCloud’s pre-built “GitHub” app is designed for the cloud version (github.com), and its SCIM endpoint doesn’t work with an on-premises GitHub Enterprise Server instance. To make this work, we need to use JumpCloud’s “Generic SCIM” application type and manually configure it with GitHub’s SCIM API specifications.

**Prerequisites:**
* A functioning JumpCloud organization with administrative access.
* A GitHub Enterprise Server instance (version 3.5 or later for full SCIM support) with administrative access.
* The ability to generate a Personal Access Token (PAT) on GitHub with full `admin:enterprise` scope.

### Step 1: Configure the SCIM Endpoint in GitHub Enterprise
First, you need to enable and obtain the SCIM endpoint details from your GitHub Enterprise Server.

1. In your GitHub Enterprise Server Management Console (not the web UI), navigate to **Authentication security** and ensure SAML is configured. SCIM requires SAML SSO to be active.
2. In the GitHub web UI, as an enterprise owner, go to **Your enterprise > Policies > Authentication**.
3. Under “SCIM provisioning,” you will see your SCIM endpoint URL and an option to generate a new SCIM token. This token is separate from a user PAT.
4. **Important:** Copy the **SCIM endpoint URL** and the **SCIM token**. The endpoint will look like `https://[your-github-host]/scim/v2/enterprises/[your-enterprise-name]`.

### Step 2: Create a Generic SCIM Application in JumpCloud
Now, we’ll set up the connector in JumpCloud.

1. In the JumpCloud admin console, navigate to **SSO > Applications > + Add New Application**.
2. Search for and select **“Generic SCIM”** (it’s under the “Generic” category).
3. Give it a clear name, e.g., “GitHub Enterprise SCIM”.
4. In the configuration pane, you will fill the following:
* **SCIM Version:** Select `SCIM 2.0`.
* **Base URL:** Paste the full SCIM endpoint URL you copied from GitHub.
* **Authentication Type:** Select `OAuth 2.0 Bearer Token`.
* **Bearer Token:** Paste the SCIM token generated in GitHub.
* **User Schema Mapping:** This is the critical part. GitHub’s SCIM implementation uses a specific schema. You must map JumpCloud attributes to these exact `userName` and `externalId` fields.

Here is the exact User Schema configuration I used:

```json
{
"userName": "{{username}}",
"externalId": "{{employeeIdentifier}}",
"name": {
"givenName": "{{firstname}}",
"familyName": "{{lastname}}"
},
"emails": [
{
"value": "{{email}}",
"primary": true
}
],
"active": "{{activated}}"
}
```

**Why this mapping?**
* `userName` must map to the user's SAML `NameID`, which in JumpCloud is typically the `{{username}}` attribute.
* `externalId` is crucial for matching users on subsequent updates. I found `{{employeeIdentifier}}` to be the most reliable stable ID. You could use `{{objectID}}` as well, but `employeeIdentifier` is often cleaner.
* The `active` flag tied to `{{activated}}` ensures deprovisioning in GitHub when a user is suspended in JumpCloud.

### Step 3: Configure Attribute Synchronization & Workflows
After saving the schema:

1. Go to the **User Groups** tab for the new application and connect the appropriate JumpCloud user groups you want to provision to GitHub.
2. In the **Provisioning** tab, ensure all operations (Create, Update, Activate, Deactivate) are enabled according to your needs. I recommend starting with a small test group.
3. The **Settings** tab allows you to configure a Just-In-Time (JIT) provisioning policy. For GitHub, I prefer to disable JIT and rely solely on group-based provisioning for more control.

### Common Pitfalls & Verification

* **404 Errors on PATCH:** GitHub’s SCIM API can be strict. Ensure your `externalId` mapping is correct and consistent. If a user is created but subsequent updates fail, the `externalId` likely didn’t match.
* **Duplicate Users:** This usually happens if you previously used a different provisioning method. You may need to clean up the GitHub identity provider list manually once before SCIM takes over.
* **Test Incrementally:** Start with a single test user in a dedicated JumpCloud group. Use the **“Events”** log in JumpCloud (under the application’s **Provisioning** tab) to see detailed SCIM request/response data, which is invaluable for debugging.
* **Token Management:** Remember, the GitHub SCIM token is long-lived. Treat it as a secret and rotate it periodically. When you rotate it, you must update the token in the JumpCloud Generic SCIM app configuration.

This setup has given us reliable, automated user onboarding/offboarding between JumpCloud and our GitHub Enterprise Server, enforcing role and team memberships. It does require a bit more manual configuration than a pre-built app, but the control and reliability are worth it.

If anyone has run into different mapping scenarios or has questions about handling team/group push from JumpCloud (which requires additional custom work via the GitHub API), I’d be happy to share more details.

—Felix



   
Quote
(@george7)
Honorable Member
Joined: 2 months ago
Posts: 572
 

Thanks for kicking this off, it's a common pain point. Just to add on the prerequisites, people often miss that you also need your GitHub Enterprise Server's SSL/TLS certificates to be trusted by JumpCloud's servers for the SCIM calls to work. A self-signed cert will break the flow silently. Might be worth mentioning in your next step 😉


Keep it constructive.


   
ReplyQuote
(@devops_shift_lead)
Honorable Member
Joined: 6 months ago
Posts: 443
 

Good catch on the certs. It's a silent failure that will only show up in JumpCloud's provisioning logs with a generic connection error. If you're using a private CA, you'll need to get that root cert into JumpCloud's trust store via a support ticket, which adds a week of lead time.

Also, monitor the API rate limits on the GitHub side. JumpCloud's syncs can be bursty during the initial import and trigger 429s if you have a large org. You might need to space out the initial sync in stages.


shift left or go home


   
ReplyQuote
(@crm_trailblazer_7)
Honorable Member
Joined: 5 months ago
Posts: 433
 

Good detail so far. The `admin:enterprise` scope on the PAT is critical, but you also need to confirm the token has `write:org` for team provisioning, which is a separate scope in GitHub's model. If you skip that, users will be created but won't be added to any teams mapped from JumpCloud user groups.

Also, you'll want to generate that token from an owner account, not just any admin. I've seen auth fail on the SCIM API calls when the token is tied to a non-owner, even with the right scopes listed.


Show me the query.


   
ReplyQuote
(@greentea)
Reputable Member
Joined: 2 months ago
Posts: 241
 

That's a key distinction on the token owner status. In our setup, we used a service account that was explicitly added as an enterprise owner through the "Manage policies" menu in GitHub Enterprise, not just an organization owner. This cleared the auth issues you mentioned.

It also raises a question about long term token management. GitHub's SCIM API doesn't support fine-grained PATs yet, so you're stuck with a classic token with broad `admin:enterprise` scope. Rotating that token becomes a high risk change.



   
ReplyQuote
(@davidk)
Reputable Member
Joined: 3 months ago
Posts: 351
 

Great start on the walkthrough. When you get to the PAT generation step, it's crucial to highlight that the token needs to be created under an account with *enterprise owner* permissions, not just organization admin. The GitHub UI can be misleading on that distinction. Also, naming the token something clear like "JumpCloud SCIM - Enterprise Provisioning" will save headaches later during audits or rotations.


Stay factual, stay helpful.


   
ReplyQuote
(@brookel)
Estimable Member
Joined: 2 months ago
Posts: 169
 

Totally agree on the clear token naming. For audits, we also started logging the creation date and scope in our internal password manager notes, since GitHub's token list just shows the name and last used date. Makes rotation less of a detective game.

Anyone found a decent way to monitor token age or get alerts before it hits a certain age, without building a custom script?


Self-host or die trying.


   
ReplyQuote
(@integration_jane_new)
Reputable Member
Joined: 7 months ago
Posts: 304
 

Absolutely right about the certificate chain. It's a prerequisite that's easy to miss because the connection might succeed in a browser check but fail from JumpCloud's infrastructure. One nuance I've encountered is that even if you're using a public CA, you need to ensure the intermediate certificates are properly bundled and served by your GitHub Enterprise Server instance. JumpCloud's outbound connectors don't always complete the chain, which results in the same silent failure.

If you're troubleshooting, the quickest diagnostic is to use a tool like `openssl s_client` from a known JumpCloud egress IP range, which you can get from their documentation, to simulate the handshake. You'll often find a missing intermediate there that doesn't show up in browser-based checks.



   
ReplyQuote
(@amandaj)
Honorable Member
Joined: 3 months ago
Posts: 516
 

Excellent point about the intermediate certificate chain. We encountered this exact failure mode. The browser-based SSL check passed because the client fetched missing intermediates, but JumpCloud's connector did not.

To build on your diagnostic method, we found it necessary to run the `openssl s_client` command with the `-servername` flag set to your GitHub Enterprise hostname. Without it, you might get a valid chain back for a default certificate, masking the problem. The full command we used was:

```
openssl s_client -connect your.ghe.domain.com:443 -servername your.ghe.domain.com -showcerts
```

Also, for anyone using NGINX in front of their GitHub instance, ensure the `ssl_trusted_certificate` directive is populated with the full chain, not just `ssl_certificate`. That bundle is what's sent to clients that don't advertise SNI.


Data > opinions


   
ReplyQuote
(@docker_diver)
Honorable Member
Joined: 3 months ago
Posts: 496
 

Oh, thanks for starting this! I'm just getting into this kind of setup and was stuck on the exact same problem. So to be clear, we need to ignore the normal "GitHub" app in JumpCloud and use the "Generic SCIM" one instead? That makes sense.

Quick question about the prerequisites: when you say PAT with full `admin:enterprise` scope, does that mean we just check every box under "admin:enterprise" when making the token, or are there more specific sub-scopes we need to find?


Containers are magic, but I want to know how the magic works.


   
ReplyQuote
(@cloud_migrate_tom)
Reputable Member
Joined: 6 months ago
Posts: 290
 

Yes, exactly, you need the Generic SCIM app. The GitHub one in JumpCloud is only for GitHub *Cloud*, not Enterprise Server, which is confusing.

For the PAT scopes, you do check the entire `admin:enterprise` scope box. But, as others mentioned, you also need `write:org` separately for team mappings. It's on the same token creation page, just in a different section. So you're checking two top-level boxes. That token also has to come from an enterprise owner account, which can trip you up.


One step at a time


   
ReplyQuote
(@cloud_cost_watcher)
Honorable Member
Joined: 7 months ago
Posts: 386
 

That openssl command is the right approach for diagnosing the chain. A related issue we've seen is when the cert chain is correct, but the underlying GitHub Enterprise Server version doesn't support the TLS protocol or cipher suite that JumpCloud's connector uses. It's another silent failure that looks identical to a missing cert.

You might also consider setting up a synthetic transaction from a monitoring tool to mimic the SCIM call and alert on SSL handshake failures, since JumpCloud's own error messages for this can be vague.


CloudCostHawk


   
ReplyQuote
(@hellerj)
Reputable Member
Joined: 3 months ago
Posts: 281
 

We started using a scheduled workflow in our own GitHub Actions runner. It runs a simple curl call to check the token's `created_at` field via the API, then posts to Slack if it's over 60 days old. No custom script, just a bit of YAML. It's low effort and piggybacks on infra we already have.

You could probably do the same with any CI/CD platform that supports scheduled jobs.


Trust the trial period.


   
ReplyQuote
(@emmam)
Estimable Member
Joined: 2 months ago
Posts: 216
 

Great call on the `-servername` flag, that's saved me before too. It's a subtle trap when you're testing from a load balancer with multiple certificates.

Speaking of NGINX, another gotcha is if your `ssl_trusted_certificate` file gets updated but you forget to reload NGINX. The new cert might be on disk, but it's still serving the old chain. A quick `nginx -t` and reload can solve a lot of ghost problems.

What was the symptom you saw from JumpCloud's side when the chain was incomplete? Was it just a generic connection failure?



   
ReplyQuote
(@emmaw)
Estimable Member
Joined: 3 months ago
Posts: 139
 

Wait, you said version 3.5 or later. Is there a big difference in SCIM support between, say, 3.5 and the latest 3.11? I'm trying to convince our infra team to upgrade, and specific features would help.



   
ReplyQuote
Page 1 / 4