Git Connections
Git Connections enable secure integration with GitHub and GitLab, allowing OEC.SH to access private repositories for deploying Odoo projects. The platform supports multiple authentication methods to fit your security requirements and workflow.
Git Connections are available at two levels: Platform-level (managed by portal admins) and Organization-level (managed by organization admins).
Overview
Git Connections provide seamless integration with Git providers to:
- Clone private repositories for Odoo deployments
- Access custom addon repositories
- Pull updates during environment synchronization
- Support multi-provider workflows (GitHub + GitLab)
- Track connection usage and audit trail
Connection Levels
| Level | Managed By | Use Case | Sharing |
|---|---|---|---|
| Platform | Portal Admins | Global platform repositories (e.g., base Odoo addons) | Can be shared with all organizations |
| Organization | Org Admins/Owners | Organization-specific repositories | Scoped to single organization |
Authentication Methods
| Method | Provider | Best For | Token Refresh |
|---|---|---|---|
| OAuth | GitHub, GitLab | Simplest setup, automatic token refresh | Yes (automatic) |
| Personal Access Token (PAT) | GitHub, GitLab | Long-term access, CI/CD pipelines | No (manual rotation) |
| GitHub App | GitHub only | Advanced integration, fine-grained permissions | Yes (automatic) |
| GitLab Token | GitLab only | Project/Group tokens with specific scopes | No (manual rotation) |
GitHub OAuth Connection
OAuth provides the simplest and most secure method to connect GitHub accounts, with automatic token refresh.
Prerequisites
- GitHub account with access to target repositories
- Organization membership in OEC.SH (for org-level connections)
- Portal admin privileges (for platform-level connections)
Connection Flow
Creating GitHub OAuth Connection
Via Web Interface
- Navigate to Settings → Git Connections
- Click "Add Git Connection"
- Select Provider: GitHub
- Select Auth Method: OAuth
- Click "Connect with GitHub"
- Authorize OEC.SH in GitHub (grants:
repo,read:org,read:user) - Connection created automatically with your GitHub username
Via API
Step 1: Initiate OAuth Flow
POST /api/v1/git-oauth/authorize
Content-Type: application/json
Authorization: Bearer YOUR_JWT_TOKEN
{
"provider": "github",
"level": "organization",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"redirect_url": "https://app.oec.sh/settings/git-connections"
}Response:
{
"authorize_url": "https://github.com/login/oauth/authorize?client_id=...",
"state": "eyJwcm92aWRlciI6ImdpdGh1YiIsImxldmVsIjoib3JnYW5pemF0aW9uIi..."
}Step 2: User Authorizes on GitHub
Redirect the user to authorize_url. GitHub will redirect back to the configured callback URL with:
https://api.oec.sh/api/v1/auth/github/callback?code=AUTHORIZATION_CODE&state=STATE_PARAMStep 3: Backend Handles Callback
The backend automatically:
- Validates the
stateparameter (CSRF protection) - Exchanges authorization code for access/refresh tokens
- Fetches GitHub user information
- Creates the Git connection with encrypted tokens
- Redirects to
redirect_urlwith success/error message
OAuth Scopes
GitHub OAuth connections request the following scopes:
| Scope | Purpose |
|---|---|
repo | Access private repositories (clone, pull) |
read:org | Read organization membership (for permission validation) |
read:user | Read user profile information |
Token Expiration
- GitHub OAuth tokens: Do not expire by default (GitHub classic)
- GitHub OAuth with expiration: Automatically refreshed before expiration
- Refresh buffer: Tokens refresh 10 minutes before expiration
If a GitHub token is revoked by the user, the connection will enter error status. Re-authorize the connection to restore functionality.
GitLab OAuth Connection
GitLab OAuth provides similar functionality to GitHub OAuth, with automatic token refresh (GitLab tokens expire after 2 hours by default).
Prerequisites
- GitLab account (GitLab.com or self-hosted instance)
- Access to target repositories/groups
- Organization membership in OEC.SH
Connection Flow
The GitLab OAuth flow is identical to GitHub, with provider-specific authorization:
- Click "Connect GitLab"
- Authorize OEC.SH in GitLab (grants:
api,read_user) - Tokens automatically refresh every 2 hours
Creating GitLab OAuth Connection
Via Web Interface
- Navigate to Settings → Git Connections
- Click "Add Git Connection"
- Select Provider: GitLab
- Select Auth Method: OAuth
- (Optional) Enter GitLab URL for self-hosted instances
- Click "Connect with GitLab"
- Authorize OEC.SH in GitLab
- Connection created with automatic token refresh
Via API
POST /api/v1/git-oauth/authorize
Content-Type: application/json
{
"provider": "gitlab",
"level": "organization",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"gitlab_url": "https://gitlab.example.com", // Optional for self-hosted
"redirect_url": "https://app.oec.sh/settings/git-connections"
}Self-Hosted GitLab
OEC.SH supports self-hosted GitLab instances:
- Ensure your GitLab instance is accessible from OEC.SH servers
- Configure OAuth application in GitLab (Admin → Applications)
- Set Redirect URI:
https://api.oec.sh/api/v1/auth/gitlab/callback - Note the Application ID and Secret
- Provide GitLab URL during connection creation
OAuth Scopes
| Scope | Purpose |
|---|---|
api | Full API access (includes repository access) |
read_user | Read user profile information |
GitLab tokens expire after 2 hours by default. OEC.SH automatically refreshes tokens using the refresh token 10 minutes before expiration.
Personal Access Token (PAT)
Personal Access Tokens provide a manual authentication method suitable for long-term access and CI/CD pipelines.
When to Use PAT
- Machine-to-machine authentication: CI/CD, automation scripts
- Long-term access: Tokens don't expire (unless configured)
- Specific scope requirements: Fine-grained control over permissions
- OAuth not available: Self-hosted instances without OAuth
Security Considerations
Security Best Practices for PATs:
- Use minimum required scopes
- Rotate tokens regularly (every 90 days recommended)
- Never commit tokens to version control
- Use separate tokens for different purposes
- Revoke tokens immediately if compromised
- Enable token expiration when possible
GitHub Personal Access Token
Creating GitHub PAT
- Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click "Generate new token (classic)"
- Set Note:
OEC.SH Repository Access - Set Expiration: 90 days (recommended)
- Select scopes:
repo(Full control of private repositories)read:org(Read org membership)
- Click "Generate token"
- Copy the token (starts with
ghp_)
Adding GitHub PAT to OEC.SH
Via Web Interface:
- Navigate to Settings → Git Connections
- Click "Add Git Connection"
- Fill in:
- Name:
GitHub Production Access - Provider: GitHub
- Auth Method: Personal Access Token
- Token:
ghp_EXAMPLE1234567890abcdefghijklmnopqrstuvwxyz(your actual token)
- Name:
- Click "Create Connection"
- Connection automatically validated
Via API:
POST /api/v1/organizations/{org_id}/git-connections
Content-Type: application/json
Authorization: Bearer YOUR_JWT_TOKEN
{
"name": "GitHub Production Access",
"description": "Main GitHub PAT for private repositories",
"provider": "github",
"auth_method": "pat",
"api_token": "ghp_EXAMPLE1234567890abcdefghijklmnopqrstuvwxyz"
}Response:
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "GitHub Production Access",
"provider": "github",
"auth_method": "pat",
"status": "active",
"is_active": true,
"validated_at": "2025-12-11T10:30:00Z",
"created_by": "user-uuid-here",
"create_date": "2025-12-11T10:30:00Z"
}GitLab Personal Access Token
Creating GitLab PAT
- Go to GitLab → Preferences → Access Tokens
- Fill in:
- Token name:
OEC.SH Repository Access - Expiration date: 90 days from now
- Select scopes:
read_api(Read API)read_repository(Pull repositories)
- Token name:
- Click "Create personal access token"
- Copy the token (starts with
glpat-)
Adding GitLab PAT to OEC.SH
POST /api/v1/organizations/{org_id}/git-connections
Content-Type: application/json
{
"name": "GitLab Private Repos",
"provider": "gitlab",
"auth_method": "gitlab_token",
"api_token": "glpat-EXAMPLE1234567890abcdefghijk",
"gitlab_url": "https://gitlab.com" // Or self-hosted URL
}Token Rotation
PATs do not automatically refresh. Set up token rotation:
- Create new token 7 days before expiration
- Update connection with new token:
PATCH /api/v1/organizations/{org_id}/git-connections/{connection_id}
Content-Type: application/json
{
"api_token": "ghp_NEW_TOKEN_HERE"
}- Revoke old token after validation
- Monitor connection status via
/validateendpoint
GitHub App Integration
GitHub Apps provide the most advanced and secure integration method, with fine-grained permissions and automatic token generation.
Advantages of GitHub Apps
- Fine-grained permissions: Access specific repositories, not all repos
- Installation-level tokens: Automatically refreshed JWT tokens
- Audit trail: All actions logged to GitHub's audit log
- Webhook support: Real-time event notifications
- No user impersonation: App acts as itself, not a user
Creating a GitHub App
- Go to GitHub → Settings → Developer settings → GitHub Apps → New GitHub App
- Fill in:
- GitHub App name:
OEC.SH Deployment Bot - Homepage URL:
https://oec.sh - Webhook URL:
https://api.oec.sh/api/v1/webhooks/github(optional) - Webhook secret: Generate random secret (optional)
- GitHub App name:
- Set Repository permissions:
- Contents: Read-only (to clone/pull)
- Metadata: Read-only (required)
- Set Where can this GitHub App be installed?: Any account
- Click "Create GitHub App"
- Note the App ID
- Generate and download Private Key (PEM format)
- Click "Install App" → Select target organization/repositories
Configuring GitHub App in OEC.SH
Via Web Interface:
- Navigate to Settings → Git Connections
- Click "Add Git Connection"
- Fill in:
- Name:
GitHub App - Production - Provider: GitHub
- Auth Method: GitHub App
- App ID:
123456(from GitHub App settings) - Installation ID:
78901234(from installation URL) - Private Key: Paste entire PEM file content
- Name:
- Click "Create Connection"
Via API:
POST /api/v1/organizations/{org_id}/git-connections
Content-Type: application/json
{
"name": "GitHub App - Production",
"provider": "github",
"auth_method": "github_app",
"github_app_id": "123456",
"github_app_installation_id": "78901234",
"github_app_private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----"
}Finding Installation ID
The Installation ID is in the URL after installing the app:
https://github.com/settings/installations/78901234
^^^^^^^^
Installation IDOr via API:
curl -H "Authorization: Bearer <JWT_TOKEN>" \
https://api.github.com/app/installationsToken Generation
GitHub Apps use JWT tokens to generate short-lived installation access tokens:
- OEC.SH generates JWT using App ID + Private Key
- JWT is used to request installation access token
- Installation token is valid for 1 hour
- Token is automatically refreshed for each deployment
GitHub App tokens are generated on-demand and expire after 1 hour. OEC.SH automatically refreshes tokens as needed.
Repository Selection
Once a Git connection is established, you can browse and select repositories for Odoo deployments.
Listing Repositories
Via API:
GET /api/v1/organizations/{org_id}/git-connections/{connection_id}/repositories?page=1&per_page=30
Authorization: Bearer YOUR_JWT_TOKENResponse:
{
"repositories": [
{
"id": "123456789",
"name": "odoo-custom-addons",
"full_name": "mycompany/odoo-custom-addons",
"url": "https://github.com/mycompany/odoo-custom-addons",
"clone_url": "https://github.com/mycompany/odoo-custom-addons.git",
"private": true,
"default_branch": "main",
"description": "Custom Odoo modules for production"
},
{
"id": "987654321",
"name": "odoo-theme",
"full_name": "mycompany/odoo-theme",
"url": "https://github.com/mycompany/odoo-theme",
"clone_url": "https://github.com/mycompany/odoo-theme.git",
"private": true,
"default_branch": "develop",
"description": "Custom Odoo theme"
}
],
"page": 1,
"per_page": 30,
"total": 2
}Repository Access Validation
The platform validates repository access before deployment:
- Connection status: Must be
active - Token validity: OAuth tokens auto-refresh if needed
- Repository permissions: Must have read access
- Branch existence: Default branch must exist
Using Repositories in Deployments
When creating an environment, specify the Git connection and repository:
POST /api/v1/projects/{project_id}/environments
Content-Type: application/json
{
"name": "Production",
"odoo_version": "17.0",
"repository_url": "https://github.com/mycompany/odoo-custom-addons.git",
"default_branch": "main",
"git_connection_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}Branch Management
Git connections enable browsing and selecting branches for deployment.
Listing Branches
Via API:
GET /api/v1/organizations/{org_id}/git-connections/{connection_id}/branches?repo=mycompany/odoo-custom-addons&page=1&per_page=100
Authorization: Bearer YOUR_JWT_TOKENResponse:
{
"branches": [
{
"name": "main",
"is_default": true,
"is_protected": true
},
{
"name": "develop",
"is_default": false,
"is_protected": false
},
{
"name": "feature/new-module",
"is_default": false,
"is_protected": false
}
],
"page": 1,
"per_page": 100
}Branch Search
Use the search query parameter to filter branches:
GET /api/v1/organizations/{org_id}/git-connections/{connection_id}/branches?repo=owner/repo&search=featureReturns only branches matching "feature".
Default Branch
Each repository has a default branch (usually main or master). When creating environments without specifying default_branch, the repository's default branch is used.
Protected Branches
Protected branches (indicated by is_protected: true) have additional safeguards:
- Cannot be force-pushed
- Require pull request reviews (if configured in GitHub/GitLab)
- Recommended for production deployments
Webhook Configuration
Git connections support webhooks for automatic deployment triggers (future feature).
Webhook Setup (Coming Soon)
Webhooks will enable:
- Auto-deploy on push: Deploy when code is pushed to a branch
- Pull request previews: Create staging environments for PRs
- Branch synchronization: Keep deployments in sync with Git
Webhook URLs (Reserved)
GitHub: https://api.oec.sh/api/v1/webhooks/github/{connection_id}
GitLab: https://api.oec.sh/api/v1/webhooks/gitlab/{connection_id}Webhook integration is planned for a future release. Currently, deployments are triggered manually via API or web interface.
Creator Tracking
All Git connections track which user created them, providing an audit trail for security and compliance.
Creator Information
Every Git connection includes created_by field:
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"name": "GitHub Production",
"created_by": "550e8400-e29b-41d4-a716-446655440000",
"create_date": "2025-12-11T10:30:00Z"
}OAuth Flow Creator Tracking
For OAuth connections, the creator is tracked through the flow:
- User initiates OAuth flow (tracked in state parameter)
- User authorizes on GitHub/GitLab
- Callback creates connection with
created_by = initiating_user_id - Connection includes creator information in response
Direct Creation (PAT/GitHub App)
For manually created connections (PAT, GitHub App):
created_byis set to the authenticated user making the API call- User must have
org.git.connections.createpermission
Audit Trail
Git connection operations are logged:
| Event | Description | Logged Data |
|---|---|---|
| Connection Created | New Git connection added | creator_id, provider, auth_method |
| Connection Updated | Connection settings changed | updater_id, changed_fields |
| Connection Validated | Connection tested/validated | validator_id, validation_result |
| Connection Used | Connection used for deployment | user_id, environment_id |
| Connection Deleted | Connection removed | deleter_id, connection_name |
Viewing Creator Information
Via Web Interface:
The Git Connections list shows creator name for each connection:
Name Provider Created By Created At
GitHub Production GitHub John Doe 2025-12-11 10:30
GitLab Private GitLab Jane Smith 2025-12-09 14:22Via API:
GET /api/v1/organizations/{org_id}/git-connectionsResponse includes creator UUID (resolve to user name via Users API).
Connection Management
Updating Connections
Update connection settings (name, description, tokens):
PATCH /api/v1/organizations/{org_id}/git-connections/{connection_id}
Content-Type: application/json
{
"name": "GitHub Production (Updated)",
"description": "Updated access token on 2025-12-11",
"api_token": "ghp_NEW_TOKEN_HERE"
}Updating tokens replaces the encrypted stored value. Ensure the new token has the same or greater permissions.
Testing Connections
Validate a connection to ensure it's working:
POST /api/v1/organizations/{org_id}/git-connections/{connection_id}/validate
Authorization: Bearer YOUR_JWT_TOKENResponse (Success):
{
"valid": true,
"username": "mycompany-bot",
"scopes": ["repo", "read:org"],
"rate_limit_remaining": 4999
}Response (Error):
{
"valid": false,
"error": "Token expired or revoked",
"rate_limit_remaining": null
}Connection Status
Git connections have four status states:
| Status | Description | Action Required |
|---|---|---|
active | Connection working | None |
expired | OAuth token expired | Re-authorize or refresh token |
revoked | Token revoked by user | Re-create connection |
error | Validation failed | Check token, re-create if needed |
Removing Connections
Delete a Git connection:
DELETE /api/v1/organizations/{org_id}/git-connections/{connection_id}
Authorization: Bearer YOUR_JWT_TOKENWarning: Deleting a connection will prevent deployments using that connection. Ensure no active environments depend on it before deletion.
Deactivating Connections
Instead of deleting, you can deactivate a connection:
PATCH /api/v1/organizations/{org_id}/git-connections/{connection_id}
Content-Type: application/json
{
"is_active": false
}Deactivated connections:
- Remain in the database
- Cannot be used for new deployments
- Can be reactivated later
SSH Keys & Deploy Keys
OEC.SH can use SSH deploy keys for additional security with private repositories.
Deploy Keys vs. Git Connections
| Method | Use Case | Scope |
|---|---|---|
| Git Connection | Multi-repository access | Organization-wide |
| Deploy Key | Single repository access | Repository-specific |
Adding Deploy Keys (Manual Method)
For repositories that require deploy keys:
- Generate SSH key pair on OEC.SH server (handled by platform)
- Add public key to repository Settings → Deploy Keys
- OEC.SH uses private key to clone/pull
Deploy keys are generated per environment by OEC.SH and injected at deployment time. Git connections provide a more flexible alternative.
Permissions
Git connection operations require specific RBAC permissions:
Organization-Level Permissions
| Permission | Action | Required Role |
|---|---|---|
org.git.connections.list | View Git connections | Member, Admin, Owner |
org.git.connections.create | Create new connections | Admin, Owner |
org.git.connections.update | Edit connections | Admin, Owner |
org.git.connections.delete | Remove connections | Admin, Owner |
org.git.connections.test | Validate connections | Admin, Owner |
Platform-Level Permissions
Platform Git connections are restricted to portal admins:
- Only users with
portal_adminrole can manage platform connections - Platform connections can be shared with organizations via
allow_org_usageflag
Permission Checks
All API endpoints enforce permissions:
# Example: Creating org connection requires permission
@require_permission("org.git.connections.create")
async def create_org_connection(org_id, data, user):
# Connection creation logic
passAPI Reference
Organization Git Connections
List Connections
GET /api/v1/organizations/{organization_id}/git-connections
Authorization: Bearer {token}
Query Parameters:
- active_only: boolean (default: true)Create Connection
POST /api/v1/organizations/{organization_id}/git-connections
Authorization: Bearer {token}
Content-Type: application/json
Body:
{
"name": string (required, 1-100 chars),
"description": string | null,
"provider": "github" | "gitlab",
"auth_method": "oauth" | "pat" | "github_app" | "gitlab_token",
"access_token": string | null, // For OAuth
"refresh_token": string | null, // For OAuth
"api_token": string | null, // For PAT
"github_app_id": string | null,
"github_app_installation_id": string | null,
"github_app_private_key": string | null,
"gitlab_url": string | null // For self-hosted GitLab
}Get Connection
GET /api/v1/organizations/{organization_id}/git-connections/{connection_id}
Authorization: Bearer {token}Update Connection
PATCH /api/v1/organizations/{organization_id}/git-connections/{connection_id}
Authorization: Bearer {token}
Content-Type: application/json
Body: (all fields optional)
{
"name": string,
"description": string,
"api_token": string,
"is_active": boolean
}Delete Connection
DELETE /api/v1/organizations/{organization_id}/git-connections/{connection_id}
Authorization: Bearer {token}Validate Connection
POST /api/v1/organizations/{organization_id}/git-connections/{connection_id}/validate
Authorization: Bearer {token}
Response:
{
"valid": boolean,
"username": string | null,
"scopes": string[] | null,
"error": string | null,
"rate_limit_remaining": number | null
}List Repositories
GET /api/v1/organizations/{organization_id}/git-connections/{connection_id}/repositories
Authorization: Bearer {token}
Query Parameters:
- page: number (default: 1)
- per_page: number (default: 30, max: 100)List Branches
GET /api/v1/organizations/{organization_id}/git-connections/{connection_id}/branches
Authorization: Bearer {token}
Query Parameters:
- repo: string (required) - Full repo name (e.g., "owner/repo")
- search: string | null - Filter branches by name
- page: number (default: 1)
- per_page: number (default: 100, max: 100)Platform Git Connections
Platform endpoints follow the same pattern with /api/v1/platform/git-connections prefix. All operations require portal admin privileges.
OAuth Flow Endpoints
Get OAuth Status
GET /api/v1/git-oauth/status
Response:
{
"github_enabled": boolean,
"gitlab_enabled": boolean
}Initiate OAuth Flow
POST /api/v1/git-oauth/authorize
Authorization: Bearer {token}
Content-Type: application/json
Body:
{
"provider": "github" | "gitlab",
"level": "platform" | "organization",
"organization_id": string | null, // Required for org-level
"redirect_url": string | null, // Where to redirect after callback
"gitlab_url": string | null // For self-hosted GitLab
}
Response:
{
"authorize_url": string,
"state": string
}OAuth Callback (Handled Automatically)
GET /api/v1/auth/{provider}/callback?code={code}&state={state}
Note: This endpoint is called by GitHub/GitLab after user authorization.
The backend automatically handles token exchange and connection creation.List Available Connections
GET /api/v1/git-oauth/connections
Authorization: Bearer {token}
Response:
{
"connections": [
{
"id": string,
"provider": string,
"username": string,
"name": string,
"level": "platform" | "organization",
"organization_id": string | null,
"organization_name": string | null
}
]
}Refresh OAuth Token
POST /api/v1/git-oauth/refresh/{connection_id}
Authorization: Bearer {token}
Response:
{
"success": boolean,
"message": string,
"expires_at": string | null
}Troubleshooting
OAuth Flow Errors
"OAuth callback URL mismatch"
Cause: Callback URL in provider settings doesn't match the one used in authorization.
Solution:
- Check GitHub/GitLab OAuth app settings
- Ensure callback URL is exactly:
https://api.oec.sh/api/v1/auth/{provider}/callback - No trailing slash
- HTTPS required
"State parameter mismatch"
Cause: CSRF protection detected a tampered state parameter.
Solution:
- Ensure the state parameter is not modified during redirect
- Complete OAuth flow within 10 minutes (state expires)
- Don't refresh or go back during OAuth flow
"Token exchange failed"
Cause: Invalid authorization code or expired code.
Solution:
- Authorization codes expire after 10 minutes
- Re-initiate OAuth flow from the beginning
- Don't reuse authorization codes (single-use only)
Permission Issues
"403 Forbidden: Invalid token"
Cause: PAT doesn't have required scopes or was revoked.
Solution:
- Validate connection:
POST /validate - Check token scopes match requirements
- Generate new token with correct scopes
- Update connection with new token
"404 Not Found: Repository not found"
Cause: Token doesn't have access to the repository.
Solution:
- For PAT: Ensure token has
reposcope - For GitHub App: Check app installation includes the repository
- For OAuth: Re-authorize to update repository access
"Rate limit exceeded"
Cause: Too many API requests to GitHub/GitLab.
Solution:
- GitHub: 5,000 requests/hour for OAuth, 60/hour for unauthenticated
- GitLab: 2,000 requests/hour for authenticated users
- Wait for rate limit reset (check
X-RateLimit-Resetheader) - Use GitHub App (higher rate limits: 15,000/hour)
Connection Validation Failures
"Connection status: expired"
Cause: OAuth token expired and refresh failed.
Solution:
- Manually refresh token:
POST /git-oauth/refresh/{connection_id} - If refresh fails, re-authorize the connection
- For PAT, generate new token and update connection
"Connection status: revoked"
Cause: User revoked token in GitHub/GitLab settings.
Solution:
- Delete old connection
- Create new connection with fresh authorization
- Update environments to use new connection
"Connection status: error"
Cause: Validation failed (network issue, invalid credentials, etc.)
Solution:
- Check connection:
POST /validatefor detailed error - Review error message in
validation_errorfield - Test token manually with
curl:
# GitHub
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.github.com/user
# GitLab
curl -H "Authorization: Bearer YOUR_TOKEN" https://gitlab.com/api/v4/userGitLab Self-Hosted Issues
"SSL certificate verification failed"
Cause: Self-hosted GitLab uses self-signed certificate.
Solution:
- Ensure GitLab instance has valid SSL certificate
- If using self-signed, contact OEC.SH support to add exception
- Use HTTP (not recommended for production)
"GitLab OAuth not configured"
Cause: OEC.SH doesn't have OAuth credentials for your GitLab instance.
Solution:
- Self-hosted GitLab requires per-instance OAuth configuration
- Contact OEC.SH support to add your GitLab instance
- Provide OAuth application ID and secret
Token Security
"Token leaked in logs"
Cause: Tokens should never appear in logs.
Solution:
- Immediately revoke compromised token in GitHub/GitLab
- Generate new token
- Update connection with new token
- Review audit logs for unauthorized access
- Report to security@oec.sh if suspicious activity detected
"Connection used by unauthorized user"
Cause: User gained access to connection without proper permissions.
Solution:
- Review organization membership
- Check user's role and permissions
- Audit connection usage: Review
last_used_atand deployment logs - If necessary, deactivate or delete connection
Best Practices
- Use OAuth when possible: Automatic refresh, easier management
- Rotate PATs regularly: Set expiration dates, rotate every 90 days
- Use GitHub Apps for production: Fine-grained permissions, better security
- Monitor connection status: Set up alerts for
expiredorerrorstatus - Audit connection usage: Review creator tracking and last usage dates
- Limit token scopes: Use minimum required permissions
- Use separate connections per environment: Production, staging, development
Security & Encryption
All sensitive data in Git connections is encrypted at rest.
Encryption Method
- Algorithm: Fernet (symmetric encryption)
- Key Management: Environment variable
FERNET_ENCRYPTION_KEY - Encrypted Fields:
access_token,refresh_token,api_token,github_app_private_key
Token Storage
# Example encryption (internal)
from cryptography.fernet import Fernet
cipher = Fernet(encryption_key)
encrypted_token = cipher.encrypt(plain_token.encode())
# Stored in database as encrypted bytes
connection.access_token = encrypted_tokenToken Retrieval
Tokens are decrypted on-demand for deployments:
- Deployment initiated
- Connection fetched from database (encrypted)
- Token decrypted in memory
- Used for Git operations
- Never logged or exposed in API responses
Security Recommendations
Critical Security Practices:
- Never log tokens: Tokens should never appear in logs, error messages, or API responses
- Rotate encryption key: Change
FERNET_ENCRYPTION_KEYduring upgrades (requires token re-encryption) - Limit token scopes: Use minimum required permissions for each connection
- Monitor token usage: Review audit logs for unexpected access patterns
- Revoke on compromise: Immediately revoke and recreate connections if token leaked
- Use short-lived tokens: Prefer OAuth (auto-refresh) over long-lived PATs
- Enable 2FA: Require two-factor authentication for users managing Git connections
Related Documentation
- Environment Deployment - Using Git connections in deployments
- Addon Repositories - Managing custom Odoo addons
- RBAC Permissions - Understanding permission system
- Organization Settings - Managing organization-level resources
Support
For assistance with Git Connections:
- Documentation: https://docs.oec.sh (opens in a new tab)
- Support Email: support@oec.sh
- Security Issues: security@oec.sh