This guide walks through creating an Okta API Services application for github-ops-app to sync Okta groups with GitHub teams.
- Okta organization with admin access
- Super Admin or Application Admin role
-
Log in to your Okta Admin Console
-
Navigate to Applications → Applications
-
Click Create App Integration
-
Select API Services and click Next
API Services apps use OAuth 2.0 client credentials flow with no user context, ideal for server-to-server integrations.
-
Enter application name:
github-ops-app(or similar) -
Click Save
After creating the app:
- Go to the General tab
- Under Client Credentials, click Edit
- Set Client authentication to Public key / Private key
- Click Save
- Under PUBLIC KEYS, click Add Key
- Click Generate new key
- Click Download PEM to save the private key
- Click Save
The downloaded file contains your private key for APP_OKTA_PRIVATE_KEY.
# Generate private key
openssl genpkey -algorithm RSA -out okta-private-key.pem -pkeyopt rsa_keygen_bits:2048
# Extract public key in JWK format (for Okta)
# You'll need to convert PEM to JWK - use a tool like:
# https://8gwifi.org/jwkconvertfunctions.jsp
# Or use the node jose libraryThen upload the public JWK to Okta under PUBLIC KEYS → Add Key.
On the General tab, find and save:
- Client ID - alphanumeric string (e.g.,
0oa1abc2def3ghi4j5k6)
-
Go to the Okta API Scopes tab
-
Grant the following scopes:
Scope Purpose okta.groups.readRead group names and members okta.users.readRead user profiles -
Click Grant for each scope
These scopes allow read-only access to groups and users - no write access to Okta is required.
API Services applications require an admin role to access Okta APIs. Without this, API calls will fail with permission errors even if scopes are granted.
-
Go to the Admin roles tab for your application
-
Click Edit assignments
-
Select one of the following roles:
Role Access Level Read Only Admin Read access to all resources (recommended) Group Admin Full access to groups only -
If using Group Admin, optionally restrict to specific groups:
- Under Edit constraints for Group Administrator, select specific groups or group types the app can access
-
Click Save changes
Note: Read Only Admin is recommended for sync operations since it provides sufficient access without write permissions. Group Admin is an alternative if you need to limit the app's scope to group resources only.
Your Okta domain is the URL you use to access the admin console:
- Production:
your-org.okta.com - Preview/Dev:
your-org.oktapreview.comordev-123456.okta.com
Use the domain without https:// prefix for APP_OKTA_DOMAIN.
The app needs to map Okta users to GitHub usernames. Determine which Okta user profile field contains GitHub usernames:
| Common Fields | Description |
|---|---|
login |
Okta username (often email) |
email |
User's email address |
githubUsername |
Custom field (recommended) |
nickName |
Sometimes used for GitHub username |
- Go to Directory → Profile Editor
- Select Okta (or your user profile)
- Click Add Attribute
- Configure:
- Data type:
string - Display name:
GitHub Username - Variable name:
githubUsername - Description:
User's GitHub username for team sync
- Data type:
- Click Save
Then set APP_OKTA_GITHUB_USER_FIELD=githubUsername.
Ensure your Okta groups follow a naming convention that can be matched by sync rules:
Example naming conventions:
| Pattern | Example Groups |
|---|---|
github-{team} |
github-engineering, github-platform |
gh-eng-{team} |
gh-eng-frontend, gh-eng-backend |
Team - {name} |
Team - Platform, Team - Security |
Groups can be:
- Okta groups (manually managed)
- Groups synced from Active Directory
- Groups from other identity providers
# Required Okta configuration
APP_OKTA_DOMAIN=your-org.okta.com
APP_OKTA_CLIENT_ID=0oa1abc2def3ghi4j5k6
APP_OKTA_GITHUB_USER_FIELD=githubUsername
# Private key (choose one method)
APP_OKTA_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----"
# Or use a file path
APP_OKTA_PRIVATE_KEY_PATH=/path/to/okta-private-key.pem
# Or use AWS SSM parameter
APP_OKTA_PRIVATE_KEY=arn:aws:ssm:us-east-1:123456789:parameter/github-bot/okta-keyDefine how Okta groups map to GitHub teams:
APP_OKTA_SYNC_RULES='[
{
"name": "engineering-teams",
"enabled": true,
"okta_group_pattern": "^github-eng-.*",
"github_team_prefix": "eng-",
"strip_prefix": "github-eng-",
"sync_members": true,
"create_team_if_missing": true
}
]'| Field | Description |
|---|---|
name |
Rule identifier (for logging) |
enabled |
Enable/disable rule (default: true) |
okta_group_pattern |
Regex to match Okta groups |
okta_group_name |
Exact Okta group name (alternative to pattern) |
github_team_prefix |
Prefix for generated GitHub team names |
github_team_name |
Exact GitHub team name (overrides pattern) |
strip_prefix |
Remove this prefix from Okta group name |
sync_members |
Sync members between Okta and GitHub (default: true) |
create_team_if_missing |
Auto-create GitHub teams if they don't exist |
team_privacy |
GitHub team visibility: secret or closed |
See the main README for additional examples.
Test your Okta configuration:
# Test OAuth token retrieval (manual verification)
# The app will automatically authenticate on startup
# Check app logs for:
# - "okta client initialized"
# - No authentication errors during syncTrigger a sync and verify:
- POST to
/scheduled/okta-syncendpoint - Check logs for groups discovered and teams synced
- Verify GitHub team memberships match Okta groups
- Verify
APP_OKTA_CLIENT_IDmatches the Client ID in Okta - Check the private key is the one generated for this specific app
- Ensure the key format is correct (PEM with proper headers)
- Verify
okta.groups.readscope is granted - Check your sync rule patterns match actual group names
- Test the regex pattern against your group names
- Verify
okta.users.readscope is granted - Check
APP_OKTA_GITHUB_USER_FIELDpoints to a valid profile field - Ensure users have the GitHub username field populated
- Only
ACTIVEusers are synced - suspended users are skipped
Okta has API rate limits. If you hit limits:
- Reduce sync frequency
- The app handles rate limit responses gracefully
- API Services apps need explicit scope grants
- Check that scopes were granted (not just requested)
- Super Admin role may be required to grant certain scopes