Skip to content

Commit 24c62b3

Browse files
authored
Merge pull request #126 from AMDResearch/docs-github-app-team-sync-config
docs(jupyterhub): document GitHub App team sync config
2 parents ac17816 + 0747b57 commit 24c62b3

2 files changed

Lines changed: 102 additions & 20 deletions

File tree

docs/jupyterhub/How_to_Setup_GitHub_OAuth.md

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,15 @@ Teams allow you to organize members and control access to different resources in
116116

117117
3. Set permissions:
118118
- **Repository permissions**:
119-
- `Contents`: Read-only (for cloning private repos)
120-
- `Metadata`: Read-only (selected by default)
119+
- `Contents`: Read-only (for cloning private repos)
120+
- `Metadata`: Read-only (selected by default)
121121

122122
![Repository permissions](../imgs/github-app-4.png)
123123

124124
- **Organization permissions**:
125-
- `Members`: Read-only (for team-based access control)
125+
- `Members`: Read-only (required for team-based resource access control and group sync)
126+
127+
> **Important**: `Members: Read-only` is required for the Hub's platform-owned team synchronization. Without this organization permission, the Hub cannot list organization teams or team members and logs errors such as `Resource not accessible by integration` when calling the GitHub GraphQL API.
126128
127129
![Organization permissions](../imgs/github-app-5.png)
128130

@@ -133,12 +135,26 @@ Teams allow you to organize members and control access to different resources in
133135
![Installation scope and Create button](../imgs/github-app-6.png)
134136

135137
5. After creation, note down the following:
136-
- **Client ID**: Displayed on the App's General page (e.g., `Iv23liXXXXXX`)
137-
- **Client secret**: Click **Generate a new client secret** — copy it immediately
138-
- **App slug**: The URL-safe name in the App's URL (e.g., `auplc-hub`)
138+
- **Client ID**: Displayed on the App's General page (e.g., `Iv23liXXXXXX`)
139+
- **Client secret**: Click **Generate a new client secret** — copy it immediately
140+
- **App ID**: Displayed on the App's General page. This is different from the Client ID.
141+
- **App slug**: The URL-safe name in the App's URL (e.g., `auplc-hub`)
139142

140143
![Client ID and secret generation](../imgs/github-app-7.png)
141144

145+
6. Generate a private key:
146+
- On the App's General page, click **Generate a private key**.
147+
- Store the downloaded `.pem` file as a Kubernetes secret or mount it into the Hub pod by your deployment's secret-management process.
148+
- Record the mounted file path. You will use it as `hub.config.GitHubOAuthenticator.private_key_file`.
149+
150+
7. Install the GitHub App on the organization:
151+
- Open the App's **Install App** page.
152+
- Install it on the same organization configured as `custom.githubOrgName`.
153+
- Select the repositories users may access if private repository cloning is enabled.
154+
- If you later add or change permissions, an organization owner must approve the updated installation permissions.
155+
156+
`installation_id` does not normally need to be configured manually. The Hub can discover it from the organization installation with `GET /orgs/{org}/installation` as long as the app is installed on that organization.
157+
142158
## Step 5: Configure JupyterHub
143159

144160
1. Open your deployment configuration file (`runtime/values.yaml` or environment-specific override)
@@ -147,13 +163,20 @@ Teams allow you to organize members and control access to different resources in
147163

148164
```yaml
149165
custom:
166+
githubOrgName: "<YOUR-ORG-NAME>"
167+
150168
gitClone:
151-
githubAppName: "your-app-slug" # Enables private repo access & repo picker
169+
githubAppName: "your-app-slug" # Enables private repo access & repo picker
152170

153171
hub:
154172
config:
155173
GitHubOAuthenticator:
156174
oauth_callback_url: "https://<Your.domain>/hub/github/oauth_callback"
175+
app_id: "<GitHub App App ID>"
176+
installation_id: "" # Optional; leave blank to auto-discover from the org installation
177+
private_key_file: "/path/to/mounted/github-app-private-key.pem"
178+
# private_key: "" # Alternative to private_key_file; prefer mounted secrets for production
179+
team_sync_ttl_seconds: 3600
157180
client_id: "<GitHub App Client ID>"
158181
client_secret: "<GitHub App Client Secret>"
159182
allowed_organizations:
@@ -163,6 +186,10 @@ Teams allow you to organize members and control access to different resources in
163186
164187
> **Note**: `scope: []` is correct for GitHub Apps. Permissions (Contents, Members, etc.) are configured in the App settings on GitHub, not via OAuth scopes.
165188

189+
> **Tip**: The Hub uses the GitHub App installation token for server-to-server team synchronization. It first lists actual organization teams, intersects them with `custom.teams.mapping`, and then batches team member lookups through GitHub GraphQL. This avoids per-user OAuth token lookups and reduces GitHub API traffic. If a configured team no longer exists on GitHub, it is logged and skipped instead of failing the entire sync.
190+
191+
> **Warning**: The GitHub App must still be installed on the organization. Leaving `installation_id` blank only skips manual ID entry; it does not remove the installation requirement.
192+
166193
3. Configure team-to-resource mapping in `values.yaml`:
167194

168195
```yaml
@@ -182,6 +209,8 @@ Teams allow you to organize members and control access to different resources in
182209
- Course-LLM
183210
```
184211

212+
Team mapping keys should correspond to GitHub team names/slugs. The Hub normalizes configured keys for GitHub API lookup, for example `AUP` is queried as the GitHub team slug `aup`, while the JupyterHub group remains `AUP`.
213+
185214
4. Deploy:
186215

187216
```bash
@@ -201,6 +230,9 @@ Teams allow you to organize members and control access to different resources in
201230
- **OAuth callback error**: Ensure your callback URL exactly matches what you configured in GitHub (including HTTPS)
202231
- **Organization not found**: Verify the organization name in your configuration matches your GitHub organization exactly
203232
- **Users can't access resources**: Check that users are added to the correct teams in GitHub
233+
- **Team sync fails with `Resource not accessible by integration`**: Ensure the GitHub App installation on your organization has `Members: Read-only` under Organization permissions. If you changed permissions after installing the app, an organization owner must approve the updated permissions on the installed app.
234+
- **Configured team is skipped**: Verify the team exists in the GitHub organization. The Hub lists actual GitHub teams first and only syncs teams that exist.
235+
- **Installation token unavailable**: Verify `app_id` and `private_key_file` are configured and that the GitHub App is installed on `custom.githubOrgName`. `installation_id` can usually remain blank.
204236
- **Authentication fails**: Verify your Client ID and Client Secret are correct and the secret hasn't expired
205237

206238
## Migrating from OAuth App to GitHub App
@@ -220,16 +252,22 @@ If you are currently using a legacy GitHub OAuth App, follow these steps to migr
220252

221253
1. **Create a GitHub App** under your organization (see [Step 4](#step-4-create-a-github-app) above)
222254

223-
2. **Update `values.yaml`** — change 3 fields, add 1:
255+
2. **Update `values.yaml`** — change the OAuth credentials and add GitHub App server-to-server settings:
224256

225257
```yaml
226258
custom:
259+
githubOrgName: "<YOUR-ORG-NAME>"
260+
227261
gitClone:
228-
githubAppName: "your-app-slug" # NEW — add this
262+
githubAppName: "your-app-slug" # NEW — add this
229263
230264
hub:
231265
config:
232266
GitHubOAuthenticator:
267+
app_id: "<GitHub App App ID>" # NEW — App ID, not Client ID
268+
installation_id: "" # NEW — optional; auto-discovered from org installation
269+
private_key_file: "/path/to/mounted/github-app-private-key.pem" # NEW
270+
team_sync_ttl_seconds: 3600 # NEW — cache/throttle team sync
233271
client_id: "<GitHub App Client ID>" # CHANGE — from OAuth App's ID
234272
client_secret: "<GitHub App Client Secret>" # CHANGE — from OAuth App's secret
235273
scope: [] # CHANGE — was [read:user, read:org]

source/jupyterhub/github-oauth-setup.md

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,37 @@ GitHub Apps are the recommended way to integrate with GitHub. They are created u
9090

9191
3. Set permissions:
9292
- **Repository permissions**:
93-
- `Contents`: Read-only (for cloning private repos)
94-
- `Metadata`: Read-only (selected by default)
93+
- `Contents`: Read-only (for cloning private repos)
94+
- `Metadata`: Read-only (selected by default)
9595
- **Organization permissions**:
96-
- `Members`: Read-only (for team-based access control)
96+
- `Members`: Read-only (required for team-based resource access control and group sync)
97+
98+
:::{important}
99+
`Members: Read-only` is required for the Hub's platform-owned team synchronization. Without this organization permission, the Hub cannot list organization teams or team members and logs errors such as `Resource not accessible by integration` when calling the GitHub GraphQL API.
100+
:::
97101

98102
4. Installation scope:
99103
- **Where can this GitHub App be installed?**: Any account
100104
- Click **Create GitHub App**
101105

102106
5. After creation, note down the following:
103-
- **Client ID**: Displayed on the App's General page (e.g., `Iv23liXXXXXX`)
104-
- **Client secret**: Click **Generate a new client secret** -- copy it immediately
105-
- **App slug**: The URL-safe name in the App's URL (e.g., `auplc-hub`)
107+
- **Client ID**: Displayed on the App's General page (e.g., `Iv23liXXXXXX`)
108+
- **Client secret**: Click **Generate a new client secret** -- copy it immediately
109+
- **App ID**: Displayed on the App's General page. This is different from the Client ID.
110+
- **App slug**: The URL-safe name in the App's URL (e.g., `auplc-hub`)
111+
112+
6. Generate a private key:
113+
- On the App's General page, click **Generate a private key**.
114+
- Store the downloaded `.pem` file as a Kubernetes secret or mount it into the Hub pod by your deployment's secret-management process.
115+
- Record the mounted file path. You will use it as `hub.config.GitHubOAuthenticator.private_key_file`.
116+
117+
7. Install the GitHub App on the organization:
118+
- Open the App's **Install App** page.
119+
- Install it on the same organization configured as `custom.githubOrgName`.
120+
- Select the repositories users may access if private repository cloning is enabled.
121+
- If you later add or change permissions, an organization owner must approve the updated installation permissions.
122+
123+
`installation_id` does not normally need to be configured manually. The Hub can discover it from the organization installation with `GET /orgs/{org}/installation` as long as the app is installed on that organization.
106124

107125
## Step 5: Configure JupyterHub
108126

@@ -112,22 +130,37 @@ GitHub Apps are the recommended way to integrate with GitHub. They are created u
112130

113131
```yaml
114132
custom:
133+
githubOrgName: "<YOUR-ORG-NAME>"
134+
115135
gitClone:
116-
githubAppName: "your-app-slug" # Enables private repo access & repo picker
136+
githubAppName: "your-app-slug" # Enables private repo access & repo picker
117137

118138
hub:
119139
config:
120140
GitHubOAuthenticator:
121141
oauth_callback_url: "https://<Your.domain>/hub/github/oauth_callback"
142+
app_id: "<GitHub App App ID>"
143+
installation_id: "" # Optional; leave blank to auto-discover from the org installation
144+
private_key_file: "/path/to/mounted/github-app-private-key.pem"
145+
# private_key: "" # Alternative to private_key_file; prefer mounted secrets for production
146+
team_sync_ttl_seconds: 3600
122147
client_id: "<GitHub App Client ID>"
123148
client_secret: "<GitHub App Client Secret>"
124149
allowed_organizations:
125150
- <YOUR-ORG-NAME>
126151
scope: [] # GitHub App uses App-level permissions, not OAuth scopes
127152
```
128153
129-
:::{note}
130-
`scope: []` is correct for GitHub Apps. Permissions (Contents, Members, etc.) are configured in the App settings on GitHub, not via OAuth scopes.
154+
:::{note}
155+
`scope: []` is correct for GitHub Apps. Permissions (Contents, Members, etc.) are configured in the App settings on GitHub, not via OAuth scopes.
156+
:::
157+
158+
:::{tip}
159+
The Hub uses the GitHub App installation token for server-to-server team synchronization. It first lists actual organization teams, intersects them with `custom.teams.mapping`, and then batches team member lookups through GitHub GraphQL. This avoids per-user OAuth token lookups and reduces GitHub API traffic. If a configured team no longer exists on GitHub, it is logged and skipped instead of failing the entire sync.
160+
:::
161+
162+
:::{warning}
163+
The GitHub App must still be installed on the organization. Leaving `installation_id` blank only skips manual ID entry; it does not remove the installation requirement.
131164
:::
132165

133166
3. Configure team-to-resource mapping in `values.yaml`:
@@ -149,6 +182,8 @@ GitHub Apps are the recommended way to integrate with GitHub. They are created u
149182
- Course-LLM
150183
```
151184

185+
Team mapping keys should correspond to GitHub team names/slugs. The Hub normalizes configured keys for GitHub API lookup, for example `AUP` is queried as the GitHub team slug `aup`, while the JupyterHub group remains `AUP`.
186+
152187
4. Deploy:
153188

154189
```bash
@@ -168,6 +203,9 @@ GitHub Apps are the recommended way to integrate with GitHub. They are created u
168203
- **OAuth callback error**: Ensure your callback URL exactly matches what you configured in GitHub (including HTTPS)
169204
- **Organization not found**: Verify the organization name in your configuration matches your GitHub organization exactly
170205
- **Users can't access resources**: Check that users are added to the correct teams in GitHub
206+
- **Team sync fails with `Resource not accessible by integration`**: Ensure the GitHub App installation on your organization has `Members: Read-only` under Organization permissions. If you changed permissions after installing the app, an organization owner must approve the updated permissions on the installed app.
207+
- **Configured team is skipped**: Verify the team exists in the GitHub organization. The Hub lists actual GitHub teams first and only syncs teams that exist.
208+
- **Installation token unavailable**: Verify `app_id` and `private_key_file` are configured and that the GitHub App is installed on `custom.githubOrgName`. `installation_id` can usually remain blank.
171209
- **Authentication fails**: Verify your Client ID and Client Secret are correct and the secret hasn't expired
172210

173211
## Migrating from OAuth App to GitHub App
@@ -187,16 +225,22 @@ If you are currently using a legacy GitHub OAuth App, follow these steps to migr
187225

188226
1. **Create a GitHub App** under your organization (see Step 4 above)
189227

190-
2. **Update `values.yaml`** -- change 3 fields, add 1:
228+
2. **Update `values.yaml`** -- change the OAuth credentials and add GitHub App server-to-server settings:
191229

192230
```yaml
193231
custom:
232+
githubOrgName: "<YOUR-ORG-NAME>"
233+
194234
gitClone:
195-
githubAppName: "your-app-slug" # NEW -- add this
235+
githubAppName: "your-app-slug" # NEW -- add this
196236
197237
hub:
198238
config:
199239
GitHubOAuthenticator:
240+
app_id: "<GitHub App App ID>" # NEW -- App ID, not Client ID
241+
installation_id: "" # NEW -- optional; auto-discovered from org installation
242+
private_key_file: "/path/to/mounted/github-app-private-key.pem" # NEW
243+
team_sync_ttl_seconds: 3600 # NEW -- cache/throttle team sync
200244
client_id: "<GitHub App Client ID>" # CHANGE -- from OAuth App's ID
201245
client_secret: "<GitHub App Client Secret>" # CHANGE -- from OAuth App's secret
202246
scope: [] # CHANGE -- was [read:user, read:org]

0 commit comments

Comments
 (0)