Skip to main content

RBAC and Authorization

Assign roles and permissions to CodeGraph users. Configure corporate identity mapping and check access through the API, user interface, and MCP.

Enterprise

This reference helps administrators and developers check user roles, API permissions and machine-account access. User roles, project scope and machine-account permissions are separate authorization layers. Start with the role permissions, then check the project assignments and machine-account scopes for the request.

Check access

For a user, select a system role from the permission table below, then assign its group and accessible projects. For automation, create a service account and assign its scopes, interfaces, and project. A scope is a permission string, such as query:execute; use its exact value in settings and requests.

Check two requests: an allowed operation and an operation outside the assigned permissions. Compare the user or service account, project, required permission, and result in the audit log. This identifies the access layer to adjust: the role, project assignment, or machine-account scopes.

For corporate sign-in, follow the OAuth/OIDC and LDAP guide. For endpoint URLs and request formats, use the REST API reference.

Runtime Model

CodeGraph uses three related authorization layers:

Runtime Model
Layer What it controls Administrator settings
User role Operations available to a user viewer, analyst, reviewer, or admin
Machine-account permissions Operations through API and MCP Scopes and allowed interfaces for the key or service account
Group and project access Projects available to the account Group membership and access to all or selected projects

Authentication methods produce either a user identity or a machine principal. Authorization then checks role permissions, raw scopes, interface allowlists, tenant/project restrictions, and middleware-specific security controls.

User Roles and Permissions

Roles

A system role defines the user’s base permissions:

Roles
Role Value Meaning
VIEWER viewer Read-only product access
ANALYST analyst Viewer plus query/scenario execution, session writes, API-key creation, basic digital-employee task work
REVIEWER reviewer Analyst plus review integrations and higher-risk digital-employee operations
ADMIN admin Platform admin role

Minimum-role checks use this hierarchy: viewer < analyst < reviewer < admin.

Permission Behavior

Permission currently contains 31 values, including admin:all.

Permission grants follow these rules:

  • The admin role allows every operation in the catalog.
  • An explicit scope grants a permission when its string matches the catalog value.
  • The admin:all scope allows every operation in role and machine-scope checks.

Check a role permission

From the CodeGraph source root with its Python dependencies installed, save this example as check_rbac.py and run python check_rbac.py:

from src.api.auth.permissions import Permission, Role, has_permission

assert has_permission(Role.VIEWER, Permission.SCENARIOS_READ)
assert not has_permission(Role.VIEWER, Permission.SCENARIOS_EXECUTE)
assert has_permission(Role.ANALYST, Permission.SCENARIOS_EXECUTE)
assert not has_permission(Role.ANALYST, Permission.REVIEW_EXECUTE)
assert has_permission(Role.REVIEWER, Permission.REVIEW_EXECUTE)
print("Role permission checks passed")

Expect Role permission checks passed and exit status 0. An AssertionError identifies a mismatch with the expected grant. This example checks the role function only; it does not exercise tokens, HTTP requests or project restrictions.

Permission Catalog

Permission Catalog
Permission Enum name Viewer Analyst Reviewer Admin
scenarios:read SCENARIOS_READ x x x x
scenarios:execute SCENARIOS_EXECUTE x x x
query:execute QUERY_EXECUTE x x x
query:validate QUERY_VALIDATE x x x
review:execute REVIEW_EXECUTE x x
review:github REVIEW_GITHUB x x
review:gitlab REVIEW_GITLAB x x
sessions:read SESSIONS_READ x x x x
sessions:write SESSIONS_WRITE x x x
sessions:delete SESSIONS_DELETE x x x
history:read HISTORY_READ x x x x
history:export HISTORY_EXPORT x x x
users:read USERS_READ x
users:write USERS_WRITE x
users:delete USERS_DELETE x
api_keys:read API_KEYS_READ x x x
api_keys:write API_KEYS_WRITE x x x
api_keys:delete API_KEYS_DELETE x
stats:read STATS_READ x x x x
metrics:read METRICS_READ x
digital_employees:read DIGITAL_EMPLOYEES_READ x x x
digital_employees:task:write DIGITAL_EMPLOYEES_TASK_WRITE x x x
digital_employees:task:high_risk DIGITAL_EMPLOYEES_HIGH_RISK_TASK x x
digital_employees:handoff:write DIGITAL_EMPLOYEES_HANDOFF_WRITE x x
digital_employees:approval:write DIGITAL_EMPLOYEES_APPROVAL_WRITE x x
digital_employees:audit:write DIGITAL_EMPLOYEES_AUDIT_WRITE x x
digital_employees:external_publish:write DIGITAL_EMPLOYEES_EXTERNAL_PUBLISH x x
digital_employees:lifecycle:write DIGITAL_EMPLOYEES_LIFECYCLE_WRITE x
digital_employees:control_tower:repair DIGITAL_EMPLOYEES_CONTROL_TOWER_REPAIR x
digital_employees:admin DIGITAL_EMPLOYEES_ADMIN x
admin:all ADMIN_ALL x

ROLE_PERMISSIONS does not currently include USERS_*, API_KEYS_DELETE, METRICS_READ, the admin-only digital-employee permissions, or ADMIN_ALL for non-admin roles. They are effectively admin-only through Role.ADMIN or an explicit admin:all scope.

Permission Helpers

Use src.api.auth.permissions for role/scope checks:

from src.api.auth.permissions import (
    Permission,
    Role,
    get_default_scopes_for_api_key,
    get_role_permissions,
    has_all_permissions,
    has_any_permission,
    has_permission,
    validate_scopes,
)

Key behavior:

  • get_default_scopes_for_api_key() returns scenarios:read, scenarios:execute, query:execute, sessions:read, sessions:write, history:read.
  • validate_scopes() filters unknown strings against the Permission enum.
  • has_permission() considers role grants first and explicit scope strings second.

FastAPI Auth Context

AuthContext is defined in src/contracts/auth_context.py and exported through src.api.auth.middleware. It combines the access subject (AuthSubject) and permission scope (AuthScope).

Fields:

FastAPI Auth Context
Field Meaning
user_id Human user id when available
subject_id Canonical subject id; user id or service-account id
subject_type user or service_account
username Display name or username
role Optional Role for user RBAC
scopes Raw permission/scope strings
auth_method jwt, api_key, service_account_api_key, iam, local_plugin, or none
group_id Optional tenant/group scope
project_id Optional project scope for service accounts
service_account_id Service-account id when authenticated as one
credential_id API key or service-account credential id
allowed_interfaces Interfaces allowed for the authenticated subject

The main dependency flow is:

  1. Reuse request.state.machine_principal if ScopeEnforcementMiddleware has already validated an API key.
  2. Check X-YC-IAM-Token if IAM is enabled.
  3. Check JWT bearer token and reject blacklisted JWT ids.
  4. Check X-API-Key as a machine credential.
  5. Return unauthenticated context when no valid credentials exist.

Use these dependencies for route code:

from src.api.auth.middleware import (
    get_auth_context,
    get_current_user,
    get_optional_user,
    require_admin,
    require_analyst,
    require_any_permission,
    require_auth,
    require_auth_or_localhost,
    require_permission,
    require_reviewer,
    require_role,
)

require_auth_or_localhost() allows local plugin traffic only when security.local_dev_unauthenticated_surfaces_enabled is true and the request is loopback. Mutating local-plugin requests must include X-Actor-Agent-Id and X-Task-Id, and generic agent ids such as codex or codex-headless are rejected.

src/api/dependencies.py also contains database-backed dependencies returning ORM User records. Auth-suite routes use those dependencies for account administration and API-key CRUD.

Authentication Methods

Local Username and Password

Routes are mounted under /api/v1/auth:

Local Username and Password
Route Purpose
POST /token Primary username/password token endpoint
POST /refresh Rotate refresh token and issue new tokens
POST /oauth/token RFC 6749-compatible password and refresh-token grants

An administrator creates a local account through POST /api/v1/admin/identity/users and assigns its role; the default is analyst. The response discloses the password once. Local passwords are hashed with bcrypt. To log out, call DELETE /api/v1/me/logout with the current bearer token; the route adds that token to the blacklist.

JWT Bearer Tokens

TokenPayload fields:

JWT Bearer Tokens
Field Meaning
sub Subject user id
jti JWT id
exp Expiration
iat Issued-at time
type access or refresh
scopes Raw permission scope strings
role Optional user role string
group_id Optional group scope
iss Optional token issuer
aud Optional audience: a string or list of strings
resource Optional RFC 8707 resource indicator
client_id Optional public OAuth client identity
project_id Optional consent-bound project

Defaults:

  • Access token TTL: the JWT configuration setting access_token_expire_minutes when expires_delta is omitted; the fallback is 30 minutes.
  • Refresh token TTL: 7 days.
  • Signing settings come from src/api/config.py (API_JWT_ALGORITHM, JWT secret resolution).

Revocation:

  • blacklist_token() stores a SHA-256 fingerprint of the JTI in memory and in the token_blacklist table.
  • is_token_blacklisted() checks memory first, then the database.
  • load_blacklist_cache() loads non-expired blacklist rows at startup.
  • _blacklist_sync_task() refreshes the in-memory cache periodically.

User API Keys

User API keys are created through /api/v1/me/api-keys and transported as X-API-Key.

Format:

rag_<8 hex chars>_<48 hex chars>

Implementation details:

  • generate_api_key() returns (full_key, prefix, key_hash).
  • The prefix starts with rag_.
  • The secret is secrets.token_hex(24).
  • The stored hash is SHA-256.
  • verify_api_key() uses secrets.compare_digest().
  • ApiKey.group_id optionally limits a key to one tenant group.
  • User API keys authenticate as MachinePrincipal(subject_type="user", auth_method="api_key").
  • User API keys allow api and mcp interfaces by default.
  • ApiKeyInfo is the secret-free response model.
  • ApiKeyWithSecret extends ApiKeyInfo and is returned only when the key is created.

API-key CRUD:

User API Keys
Route Purpose
POST /api/v1/me/api-keys Create a key for the current user
GET /api/v1/me/api-keys List current user’s keys without secrets
DELETE /api/v1/me/api-keys/{key_id} Revoke one owned key

The create request defaults to ["scenarios:read", "query:execute"]; the lower-level helper get_default_scopes_for_api_key() returns a broader legacy default list. Route behavior is the authoritative runtime behavior.

Service Accounts

Service accounts use the same X-API-Key transport with a distinct credential prefix:

svc_<8 hex chars>_<48 hex chars>

Service-account records are persisted in service_accounts; credentials are persisted in service_account_credentials.

Implemented controls:

  • Admin-only service-account management.
  • Policy templates: ci, portal, bot, agent.
  • Explicit scopes and allowed_interfaces.
  • Optional group_id or project_id restriction.
  • Account expiry and credential expiry.
  • Credential rotation without immediate old-credential revocation.
  • Active credential quota through security.service_account_max_active_credentials.
  • Revoke and deactivate flows.
  • Machine contract header: X-CodeGraph-Machine-Contract, currently 2026-03-v1.
  • Optional proxy-terminated mTLS binding for configured interfaces.
  • Unified machine audit events through AuditAction.MACHINE_ACCESS.

Admin routes:

Service Accounts
Route Purpose
GET /api/v1/admin/identity/service-accounts/action-catalog Return route scopes, machine contract, policy templates, and security limits
POST /api/v1/admin/identity/service-accounts Create service account and initial credential
GET /api/v1/admin/identity/service-accounts List service accounts
GET /api/v1/admin/identity/service-accounts/{service_account_id} Inspect service account and credential metadata
POST /api/v1/admin/identity/service-accounts/{service_account_id}/rotate Issue a new credential
POST /api/v1/admin/identity/service-accounts/{service_account_id}/credentials/{credential_id}/revoke Revoke one credential
POST /api/v1/admin/identity/service-accounts/{service_account_id}/deactivate Disable account and revoke credentials

Policy templates:

Service Accounts, table 2
Template Scopes Interfaces
ci query:execute, scenarios:read, mcp:access api, mcp
portal stats:read, history:read api
bot query:execute, scenarios:read, mcp:access api, mcp
agent query:execute, scenarios:execute, review:execute, mcp:access, digital_employees:read, digital_employees:task:write api, mcp

OAuth2/OIDC

Supported provider implementations:

OAuth2/OIDC
Provider Notes
github GitHub OAuth
gitlab GitLab OAuth, configurable server URL
google Google OIDC
keycloak Keycloak OIDC, configurable server and realm
sourcecraft Yandex ID based SourceCraft provider
gitverse GitVerse provider

Routes:

OAuth2/OIDC, table 2
Route Purpose
GET /api/v1/auth/oauth/providers List configured providers and generated authorize URLs
GET /api/v1/auth/oauth/{provider} Start OAuth flow
GET /api/v1/auth/oauth/{provider}/callback Exchange code, create or find user, issue JWTs

Configured OAuth users receive JWTs using their persisted database role.

LDAP/Active Directory

Routes:

LDAP/Active Directory
Route Purpose
POST /api/v1/auth/ldap Authenticate against LDAP/AD and issue JWTs
GET /api/v1/auth/ldap/status Report LDAP availability and connection status

LDAPAuthenticator.map_groups_to_role() reads LDAPConfig.role_mapping and normalizes group DN/CN aliases. If several roles match, it selects the first in the order admin, reviewer, analyst, viewer. With no match it returns analyst. This fallback grants more permissions than viewer; verify group mappings before enabling LDAP.

Yandex Cloud IAM

IAM validation lives in src/api/auth/providers/iam.py and is initialized from API settings when IAM is enabled.

Request header:

X-YC-IAM-Token: <token>

When the token validates, get_auth_context() returns:

  • role=Role.ANALYST
  • scopes=["scenarios:read", "query:execute", "review:execute"]
  • auth_method="iam"

Validation results are cached for the configured IAM token cache TTL.

Machine Scope Enforcement

ScopeEnforcementMiddleware is added in src/api/main.py when security.api_key_scope_enforcement is true. It checks requests with X-API-Key before the router executes.

Process:

  1. Validate the key as a user API key or service-account credential.
  2. Save MachinePrincipal on request.state.machine_principal.
  3. Resolve requested project scope from X-Project-Id.
  4. Validate optional machine contract header.
  5. Validate optional mTLS binding for service accounts.
  6. Run authorize_machine_request().
  7. Emit a machine audit event.
  8. Return 403 insufficient_scope on denial.

API route scope map excerpt:

Machine Scope Enforcement
Prefix Required scope
/review review:execute
/metrics metrics:read
/security scenarios:execute
/code query:execute
/import scenarios:execute
/documentation scenarios:execute
/edit scenarios:execute
/optimize scenarios:execute
/compliance scenarios:execute
/dashboard stats:read
/context query:execute
/admin/runtime/acp scenarios:execute
/workflows scenarios:execute
/webhooks No separate scope requirement
/health No separate scope requirement
/auth No separate scope requirement
/admin/workforce admin:all
/groups admin:all
/projects admin:all
/digital-employees digital_employees:read
/employee-lanes digital_employees:read
/employee-tasks digital_employees:read
/demo No separate scope requirement
/ws No separate scope requirement

Each route applies its authentication, webhook-signature and handler-dependency checks. authorize_machine_request() checks allowed interfaces and service-account project or group restrictions for every path. The scope map adds the required permission for the prefixes listed above.

MCP route scope map:

Machine Scope Enforcement, table 2
Prefix Required scope
/mcp mcp:access

For MCP only, mcp:access can be satisfied by aliases including query:execute, scenarios:read, scenarios:execute, review:execute, stats:read, history:read, and admin:all.

Explicit endpoint scope checks can use:

In your FastAPI application, import Depends from fastapi and create an APIRouter named router. Add the handler below and mount the router in the application. With authentication configured, send requests from accounts with and without the required scope to check both access outcomes.

from src.api.auth.tokens.scope_enforcement import require_scope

@router.get("/example")
async def example(auth=Depends(require_scope("query:execute"))):
    ...

require_scope() checks raw AuthContext.scopes; admin:all bypasses it.

Tenant and Project Access

Tenant/project authorization is modeled separately from global user roles.

Enums and tables:

Tenant and Project Access
Surface Values or purpose
GroupRole viewer, editor, admin
ProjectScopeMode all, selected
project_groups Tenant/group records
user_group_access User membership and tenant role
user_project_access Selected project allowlist when membership uses selected mode
api_keys.group_id Optional tenant restriction for user API keys
service_accounts.group_id / project_id Optional tenant/project restriction for service accounts

Admin access-management routes:

Tenant and Project Access, table 2
Route Purpose
GET /api/v1/admin/identity/users/{user_id}/access Read unified system role plus tenant/project assignments
PUT /api/v1/admin/identity/users/{user_id}/access Replace system role and tenant/project assignments

Assign a role and projects in the interface

You need administrator access, an existing user, and a group containing projects. The interface calls a group a tenant.

  1. Open Users and select the user’s card.
  2. Fill in Audit tenant and Change reason to record the context of the administrative operation.
  3. In System role, select a role from the permission table above, save it, and confirm the change.
  4. On the Access tab, add a tenant and select its role: Viewer, Editor, or Admin.
  5. Under Project scope, choose Selected projects and select at least one project belonging to that tenant. Choose All projects to grant access to all its projects.
  6. Review the selected assignments, select Save access, and confirm the save. Reopen the card and compare the stored tenant, role, and projects with your selections.
  7. Sign in again as that user to obtain a token with the current role. Open an allowed project and perform an operation permitted by the system role. To check the project restriction, use a test user with a single selected assignment: the selected project should open, and another project should return an access denial.

This screen edits direct user assignments. Check access inherited through tenant groups in Tenants. For automation, supply tenant_id, change_reason, system_role, and the assignments list in the PUT request. Each assignment contains group_id, role, project_scope_mode, and allowed_project_ids. The request replaces the assignment list: include every assignment you want to retain, then read the result through GET.

Guardrails:

  • A platform admin cannot deactivate their own account.
  • The last active platform admin cannot be deactivated or downgraded.
  • The last tenant admin cannot be removed or downgraded without another tenant admin.
  • selected project scope must include at least one project, and all selected projects must belong to the assigned tenant.
  • all project scope cannot include explicit project ids.

Project resolution and tenant-sensitive runtime paths must use ProjectContext and resolved project scope, not user-provided database paths.

Path Validation Middleware

PathValidationMiddleware validates db_path and source_path fields in JSON bodies for POST, PUT, and PATCH requests.

Rules:

  • Empty strings and NUL bytes are rejected.
  • Relative paths are rejected when path_validation_deny_relative is true.
  • Path components are normalized before checking the file location.
  • Paths are resolved with os.path.realpath().
  • The resolved path must be under an allowed base directory.

Allowed directories are built from:

  • security.path_validation_allowed_base_dirs
  • registered project db_path parent directories
  • registered project source_path directories

Config fields are defined in SecurityConfig:

Path Validation Middleware
Field Default
path_validation_enabled true
path_validation_deny_symlinks true
path_validation_deny_relative true
path_validation_allowed_base_dirs ["data/projects/"]

Webhook Authentication

Webhook verification lives in src/api/auth/machine/webhook_auth.py. The main verification function is verify_webhook_signature().

Supported platforms:

Webhook Authentication
Platform Signature/token header Timestamp header
sourcecraft X-SourceCraft-Signature X-SourceCraft-Timestamp
gitverse X-GitVerse-Signature; fallbacks include X-Gitea-Signature, X-Gogs-Signature, X-Hub-Signature-256, X-Hub-Signature X-GitVerse-Timestamp
github X-Hub-Signature-256 X-Hub-Timestamp
gitlab X-Gitlab-Token none

sourcecraft, gitverse, and github use HMAC signature validation. gitlab uses constant-time token comparison. Timestamp age validation uses security.webhook_max_age_seconds only when the request supplies a nonempty supported timestamp header. This check does not reject a missing header or track previously accepted events, so it does not itself prevent repeats within the allowed window. GitLab has no timestamp check.

Audit and SIEM-Relevant Events

Relevant authorization actions include:

Audit and SIEM-Relevant Events
Category Actions
Auth auth.login.success, auth.login.failure, auth.logout, auth.token.refresh, auth.token.revoked
OAuth and LDAP oauth.login.*, ldap.login.*
API keys api_key.created, api_key.revoked, api_key.deleted, api_key.used
Service accounts service_account.created, service_account.rotated, service_account.credential_revoked, service_account.disabled
Machine access machine.access
User and tenant admin user.created, user.updated, user.role.changed, user.tenant.*, user.project.scope.changed
Security security.permission.denied, security.rate_limit.exceeded, security.token.invalid, security.path_violation, security.idor_attempt, security.webhook.replay_rejected, security.mcp.auth_failure

Machine audit records include subject type/id, interface, operation, resource, result, credential id, optional service-account id, optional contract version, project id, and group id.

Configuration Reference

Selected current config fields:

Configuration Reference
Field Purpose
security.api_key_scope_enforcement Enables machine API-key scope middleware
security.local_dev_unauthenticated_surfaces_enabled Enables guarded loopback local-plugin auth bypass
security.webhook_max_age_seconds Maximum allowed age of a supplied webhook timestamp
security.auth_rate_limit_* Login/register/refresh/LDAP rate-limit settings
security.service_account_token_ttl_days Default service-account account and credential TTL
security.service_account_default_interfaces Default interfaces for service accounts
security.service_account_max_active_credentials Rotation quota
security.service_account_rotation_overlap_seconds Intended overlap window for rotations
security.service_account_rotation_warning_days Warning window for credential age/expiry
security.service_account_revoke_sla_seconds Revocation SLA reference
security.service_account_mtls_enabled Enables service-account mTLS binding checks
security.service_account_mtls_required_interfaces Interfaces requiring mTLS when enabled
security.service_account_mtls_subject_header Proxy header for certificate subject
security.service_account_mtls_fingerprint_header Proxy header for certificate fingerprint
security.service_account_mtls_bindings Allowed fingerprints by service-account id

Operational Guidance

For administrators:

  1. Use admin only for platform administration.
  2. Prefer tenant GroupRole and selected project access for day-to-day segregation.
  3. Use service accounts for CI, bots, portals, MCP clients, and long-lived machine access.
  4. Use policy templates as a baseline and add only the scopes needed by the caller.
  5. Rotate service-account credentials before revoking old credentials.
  6. Keep JWT blacklist cache loading enabled during application startup.
  7. Keep path validation and API-key scope enforcement enabled in shared environments.

For developers:

  1. Use require_permission() or require_role() for human user route checks.
  2. Use require_scope() only when raw scope strings are the intended contract.
  3. Add new machine routes to API_ROUTE_SCOPE_MAP or MCP_ROUTE_SCOPE_MAP.
  4. Preserve REST/MCP parity by calling shared services rather than duplicating auth logic.
  5. Never authorize tenant-sensitive operations from user-provided db_path.
  6. For local-plugin mutating routes, require named actor and task evidence.
  7. Do not log bearer tokens, API keys, service-account secrets, raw webhook secrets, or raw prompts.

Version: 3.0 | May 2026