Configuration Guide for IT and Security Teams
Table of Contents¶
- Overview
- Key Capabilities
- Architecture
- Authentication Flow
- Provider Registration
- OAuth2/OIDC
- Supported Providers
- Configuration
- Authorization Flow
- User Provisioning
- LDAP/Active Directory
- Configuration
- Group-to-Role Mapping
- User Provisioning
- SIEM Integration
- Authentication Events
- Event Examples
- API Reference
- OAuth Endpoints
- LDAP Endpoints
- Startup and Shutdown
- Troubleshooting
- Related Documents
Overview¶
CodeGraph supports enterprise Single Sign-On (SSO) via OAuth2/OIDC and LDAP/Active Directory integration. Both methods are initialized at application startup and produce SIEM-auditable security events on every authentication attempt.
Key Capabilities¶
| Feature | Description |
|---|---|
| 6 OAuth2 providers | GitHub, Google, Keycloak (OIDC), GitLab, SourceCraft (Yandex ID), GitVerse (Sber ID) |
| LDAP/AD support | Active Directory, OpenLDAP with group sync |
| Auto-provisioning | Users created on first login from SSO/LDAP |
| RBAC mapping | LDAP groups map to CodeGraph roles (ADMIN, REVIEWER, ANALYST, VIEWER) |
| CSRF protection | State parameter with server-side validation |
| SIEM auditing | AUTH_SUCCESS / AUTH_FAILURE events on every login |
| Graceful degradation | App starts even if OAuth/LDAP is unavailable |
Architecture¶
Authentication Flow¶
┌──────────────┐
│ Browser / │
│ API Client │
└──────┬───────┘
│
┌─────────────┼─────────────┐
│ │ │
┌──────▼──────┐ ┌───▼────┐ ┌──────▼──────┐
│ OAuth2 │ │ LDAP │ │ Local JWT │
│ /OIDC │ │ /AD │ │ + API Key │
└──────┬──────┘ └───┬────┘ └──────┬──────┘
│ │ │
└─────────────┼─────────────┘
│
┌───────▼───────┐
│ User Repo │
│ (find or │
│ create) │
└───────┬───────┘
│
┌───────▼───────┐ ┌──────────────┐
│ JWT Tokens │ │ SIEM Event │
│ (access + │──────►│ Dispatch │
│ refresh) │ │ (auth.*) │
└───────────────┘ └──────────────┘
Provider Registration¶
On startup, the application lifespan() initializes authentication subsystems:
- OAuth —
setup_oauth_providers()readsOAUTH_*environment variables, registers enabled providers withOAuthManager - IAM —
setup_iam_validator()initializes Yandex Cloud IAM token validation (ifIAM_ENABLED=true) - LDAP —
setup_ldap_authenticator()readsLDAP_*environment variables, creates the singletonLDAPAuthenticator
All blocks are wrapped in try/except — the application starts normally even if no SSO is configured.
OAuth2/OIDC¶
Supported Providers¶
CodeGraph supports 6 OAuth2/OIDC providers. The 4 base providers are listed in the /oauth/providers endpoint; SourceCraft and GitVerse are platform-specific integrations with their own dedicated documentation.
| Provider | Authorize URL | Token URL | Scopes |
|---|---|---|---|
| GitHub | github.com/login/oauth/authorize |
github.com/login/oauth/access_token |
user:email |
accounts.google.com/o/oauth2/v2/auth |
oauth2.googleapis.com/token |
openid, email, profile |
|
| Keycloak | {server}/realms/{realm}/protocol/openid-connect/auth |
.../token |
openid, email, profile |
| GitLab | gitlab.com/oauth/authorize (or self-hosted) |
gitlab.com/oauth/token |
read_user, openid |
| SourceCraft | {server}/authorize (Yandex ID) |
{server}/token |
login:email, login:info |
| GitVerse | {server}/oauth/authorize (Sber ID) |
{server}/oauth/token |
read:user, user:email |
Note: SourceCraft and GitVerse are documented in detail in SourceCraft Integration and GitVerse Integration.
Configuration¶
Set environment variables for each provider you want to enable:
# GitHub
export OAUTH_GITHUB_CLIENT_ID="your_github_client_id"
export OAUTH_GITHUB_CLIENT_SECRET="your_github_client_secret"
# Google
export OAUTH_GOOGLE_CLIENT_ID="your_google_client_id"
export OAUTH_GOOGLE_CLIENT_SECRET="your_google_client_secret"
# Keycloak (OIDC)
export OAUTH_KEYCLOAK_SERVER_URL="https://keycloak.corp.example.com"
export OAUTH_KEYCLOAK_REALM="codegraph"
export OAUTH_KEYCLOAK_CLIENT_ID="codegraph-app"
export OAUTH_KEYCLOAK_CLIENT_SECRET="your_keycloak_client_secret"
# SourceCraft (Yandex ID)
export OAUTH_SOURCECRAFT_CLIENT_ID="your_sourcecraft_client_id"
export OAUTH_SOURCECRAFT_CLIENT_SECRET="your_sourcecraft_client_secret"
export OAUTH_SOURCECRAFT_SERVER_URL="https://oauth.yandex.ru" # default
# GitVerse (Sber ID)
export OAUTH_GITVERSE_CLIENT_ID="your_gitverse_client_id"
export OAUTH_GITVERSE_CLIENT_SECRET="your_gitverse_client_secret"
export OAUTH_GITVERSE_SERVER_URL="https://gitverse.ru" # default
Providers without CLIENT_ID and CLIENT_SECRET are skipped automatically.
Note: GitLab OAuth is supported in the codebase but does not have dedicated
OAUTH_GITLAB_*environment variables. To enable GitLab, configure it directly inconfig.yamlunderauth.oauth.gitlabwithclient_id,client_secret, and optionallyauthorize_url/token_urlfor self-hosted instances.
Authorization Flow¶
1. User → GET /api/v1/auth/oauth/{provider}
2. Server generates CSRF state, stores in memory
3. 302 redirect to provider's authorize URL
4. User authenticates at provider
5. Provider redirects to GET /api/v1/auth/oauth/{provider}/callback?code=...&state=...
6. Server verifies state (CSRF), exchanges code for access token
7. Server fetches user info from provider API
8. Server finds or creates user in database
9. Server returns JWT tokens (access + refresh)
User Provisioning¶
On first OAuth login, a new user is created automatically:
| Field | Source |
|---|---|
username |
Provider-specific (GitHub: login, Google: email prefix, Keycloak: preferred_username) |
email |
Provider user info |
auth_provider |
Per-provider: oauth_github, oauth_google, oauth_gitlab, oauth_keycloak |
role |
Default: ANALYST |
external_id |
Provider’s unique user ID |
Note: The
display_namefield from the provider’s user info is passed to the provisioning method but is not currently persisted in the database. Theusernamefield serves as the primary display identifier.
Subsequent logins match by (auth_provider, external_id) and reuse the existing account.
LDAP/Active Directory¶
LDAP Configuration¶
# Required
export LDAP_SERVER="ldaps://ldap.corp.example.com"
export LDAP_BASE_DN="dc=corp,dc=example,dc=com"
# Service account for searches
export LDAP_BIND_USER="cn=codegraph-svc,ou=service-accounts,dc=corp,dc=example,dc=com"
export LDAP_BIND_PASSWORD="service_account_password"
# Search bases
export LDAP_USER_SEARCH_BASE="ou=users,dc=corp,dc=example,dc=com"
export LDAP_GROUP_SEARCH_BASE="ou=groups,dc=corp,dc=example,dc=com"
Full LDAPConfig in config.yaml:
auth:
ldap:
enabled: true
server: "ldaps://ldap.corp.example.com"
port: 636
use_ssl: true
base_dn: "dc=corp,dc=example,dc=com"
user_search_base: "ou=users,dc=corp,dc=example,dc=com"
group_search_base: "ou=groups,dc=corp,dc=example,dc=com"
bind_user: "cn=codegraph-svc,ou=service-accounts,dc=corp,dc=example,dc=com"
bind_password: "${LDAP_BIND_PASSWORD}"
user_object_class: "person"
group_object_class: "group"
username_attribute: "sAMAccountName"
email_attribute: "mail"
group_membership_attribute: "memberOf"
role_mapping:
"cn=codegraph-admins,ou=groups,dc=corp,dc=example,dc=com": "admin"
"cn=codegraph-reviewers,ou=groups,dc=corp,dc=example,dc=com": "reviewer"
"cn=codegraph-analysts,ou=groups,dc=corp,dc=example,dc=com": "analyst"
Group-to-Role Mapping¶
LDAP groups map to CodeGraph RBAC roles via role_mapping in LDAPConfig. The LDAPAuthenticator checks the user’s groups in priority order (admin > reviewer > analyst > viewer). First match wins.
| LDAP Group | CodeGraph Role | Permissions |
|---|---|---|
codegraph-admins |
ADMIN | Full access (admin:all) |
codegraph-reviewers |
REVIEWER | Code review + analyst permissions |
codegraph-analysts |
ANALYST | Query execution + API keys |
| (no match) | ANALYST | Default fallback |
Known issue: The
LDAPAuthenticator.map_groups_to_role()method accessesconfig.group_role_mappinginstead of therole_mappingfield defined inLDAPConfig. This causes the mapping to silently fall through to the defaultanalystrole. The fix requires aligning the attribute name inldap_auth.pywith theLDAPConfigfield name.
LDAP User Provisioning¶
On first LDAP login, a user is created via LDAPAuthenticator:
| Field | Source |
|---|---|
username |
LDAP sAMAccountName (or configured attribute) |
email |
LDAP mail attribute |
ldap_dn |
Full distinguished name (stored as external_id) |
auth_provider |
ldap |
role |
From group-to-role mapping |
Note: The
display_namefrom LDAPdisplayNameattribute is passed tocreate_ldap_user()but is not currently persisted in the database.
Subsequent logins match by ldap_dn (stored as external_id with auth_provider=ldap).
SIEM Integration¶
All authentication attempts (success and failure) generate SIEM events dispatched to configured handlers (Syslog, CEF, LEEF).
Authentication Events¶
| Event Type | Severity | Trigger |
|---|---|---|
AUTH_SUCCESS |
6 (Informational) | Successful login via any method |
AUTH_FAILURE |
4 (Warning) | Failed login — wrong credentials, unknown user, provider error |
Event Examples¶
Successful OAuth login:
{
"event_type": "auth.success",
"timestamp": "2026-02-26T10:30:00.000Z",
"severity": 6,
"message": "OAuth login: ghuser via github",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"ip_address": "10.0.0.50",
"provider": "github",
"request_id": "req-abc123"
}
Failed LDAP login:
{
"event_type": "auth.failure",
"timestamp": "2026-02-26T10:31:00.000Z",
"severity": 4,
"message": "LDAP auth failed: unknown_user",
"ip_address": "10.0.0.51",
"request_id": "req-def456"
}
Failed local login (wrong password):
{
"event_type": "auth.failure",
"timestamp": "2026-02-26T10:32:00.000Z",
"severity": 4,
"message": "Login failed: invalid password for analyst01",
"user_id": "550e8400-e29b-41d4-a716-446655440001",
"ip_address": "10.0.0.52",
"request_id": "req-ghi789"
}
API Reference¶
OAuth Endpoints¶
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/auth/oauth/providers |
List 4 base OAuth providers and their status |
GET |
/api/v1/auth/oauth/{provider} |
Start OAuth flow (redirects to provider) |
GET |
/api/v1/auth/oauth/{provider}/callback |
Handle OAuth callback, return JWT tokens |
List providers response:
Note: The
/oauth/providersendpoint lists the 4 base providers (github, google, gitlab, keycloak) with a hardcoded list. SourceCraft and GitVerse are registered inOAuthManagerand accept OAuth flows via/oauth/{provider}, but are not included in the providers listing.
[
{"name": "github", "enabled": true, "authorize_url": "https://github.com/login/oauth/authorize?..."},
{"name": "google", "enabled": false, "authorize_url": null},
{"name": "gitlab", "enabled": false, "authorize_url": null},
{"name": "keycloak", "enabled": false, "authorize_url": null}
]
Callback success response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 1800
}
LDAP Endpoints¶
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/auth/ldap |
Authenticate with LDAP credentials |
GET |
/api/v1/auth/ldap/status |
Check LDAP connection status |
LDAP login request:
{
"username": "jdoe",
"password": "corporate_password"
}
LDAP status response (connected):
{
"enabled": true,
"available": true,
"connected": true,
"server": "ldaps://ldap.corp.example.com"
}
LDAP status response (not configured):
{
"enabled": false,
"available": false,
"message": "LDAP not configured"
}
LDAP status response (ldap3 library not installed):
{
"enabled": true,
"available": false,
"message": "LDAP configured but ldap3 library not installed"
}
LDAP status response (connection failed):
{
"enabled": true,
"available": true,
"connected": false,
"error": "Connection refused"
}
Startup and Shutdown¶
Startup Sequence¶
During application startup (lifespan()), authentication subsystems are initialized in order:
# 1. OAuth providers (6 providers)
auth_config = get_auth_config()
oauth_providers = setup_oauth_providers({
"github": auth_config.oauth.github,
"google": auth_config.oauth.google,
"gitlab": auth_config.oauth.gitlab,
"keycloak": auth_config.oauth.keycloak,
"sourcecraft": auth_config.oauth.sourcecraft,
"gitverse": auth_config.oauth.gitverse,
})
# Logs: "OAuth providers initialized: 2" or "No OAuth providers configured"
# 2. Yandex Cloud IAM (optional)
if settings.iam_enabled:
setup_iam_validator(enabled=True, validation_url=settings.iam_validation_url)
# Logs: "Yandex Cloud IAM validator initialized" or "IAM auth disabled"
# 3. LDAP authenticator
ldap = setup_ldap_authenticator(auth_config.ldap)
# Logs: "LDAP authenticator initialized: ldaps://..." or "LDAP not configured"
Shutdown Cleanup¶
On shutdown, OAuth HTTP clients are closed gracefully:
await get_oauth_manager().close_all()
Graceful Degradation¶
All subsystems are optional. If initialization fails (missing library, bad config), the application logs a warning and continues:
WARNING OAuth initialization skipped: <error>
WARNING IAM initialization skipped: <error>
WARNING LDAP initialization skipped: <error>
All other endpoints (local JWT login, API keys, query, scenarios) remain fully functional.
Troubleshooting¶
OAuth providers show enabled: false¶
Verify environment variables are set:
echo $OAUTH_GITHUB_CLIENT_ID
echo $OAUTH_GITHUB_CLIENT_SECRET
Both CLIENT_ID and CLIENT_SECRET must be non-empty for a provider to register.
OAuth callback returns invalid_state_parameter¶
The CSRF state token is stored in server memory. This error occurs if: - The state expired (server restarted between redirect and callback) - The state was already consumed (browser refresh) - Multi-instance deployment without shared state storage (use Redis in production)
LDAP returns 503 ldap_not_configured¶
The LDAP_SERVER environment variable is not set, or the ldap3 library is not installed:
pip install ldap3>=2.9.1
LDAP connection test fails¶
Check network connectivity and credentials:
# Test LDAP connectivity
ldapsearch -H ldaps://ldap.corp.example.com -x -D "cn=codegraph-svc,..." -w PASSWORD -b "dc=corp,dc=example,dc=com" "(sAMAccountName=testuser)"
LDAP role mapping always returns analyst¶
The LDAPAuthenticator.map_groups_to_role() method has a known attribute name mismatch: it accesses config.group_role_mapping instead of config.role_mapping. Until this is fixed in ldap_auth.py, all LDAP users receive the default analyst role regardless of role_mapping configuration.
No SIEM events appear¶
Verify SIEM dispatcher is enabled in config.yaml:
siem:
enabled: true
handlers:
- type: syslog
host: "siem.corp.example.com"
port: 514
See SIEM Integration for full configuration.
Startup logs show initialization warnings¶
Check application logs for specific errors:
uvicorn src.api.main:app --log-level debug 2>&1 | grep -i "oauth\|ldap\|iam"
Related Documents¶
- RBAC and Authorization — Role hierarchy and permission catalog
- SIEM Integration — Security event formats and dispatcher configuration
- Security Brief — Enterprise security overview
- DLP Security — Data loss prevention
- Deployment Guide — High-availability deployment
- SourceCraft Integration — SourceCraft (Yandex ID) OAuth
- GitVerse Integration — GitVerse (Sber ID) OAuth