This GitLab error occurs when your account or group has two-factor authentication (2FA) enforced, and you attempt to authenticate Git operations using your password instead of a Personal Access Token or SSH key. After enabling 2FA, password authentication is no longer accepted for Git over HTTPS.
When GitLab returns "Two-factor authentication is required" during a git push, pull, or clone operation, it means your account has 2FA enabled (either by your choice or enforced by your organization), and you're trying to authenticate using your password over HTTPS. Once 2FA is enabled on a GitLab account, standard password authentication becomes invalid for Git operations over HTTPS and API requests. This is a security feature - passwords alone are considered insufficient when 2FA is active because they don't provide the second factor of authentication. The error commonly appears as: - `remote: GitLab: Two-factor authentication is required` - `HTTP Basic: Access denied. You must use a personal access token with 'api' scope for Git over HTTP` - `remote: HTTP Basic: Access denied` This happens because: 1. **Security enforcement**: 2FA means something you know (password) plus something you have (authenticator app). Git over HTTPS can only accept one credential, so passwords are blocked. 2. **Organization policies**: Many organizations enforce 2FA for all members, which automatically affects Git access. 3. **Recent 2FA enablement**: You may have just enabled 2FA and not updated your Git authentication method. To continue using Git, you must switch to one of the supported authentication methods: Personal Access Tokens (PATs), SSH keys, or OAuth-based credential helpers.
Create a Personal Access Token (PAT) to replace your password for Git operations:
1. Sign in to GitLab and click your avatar in the top-right corner
2. Select Edit profile from the dropdown
3. In the left sidebar, select Access Tokens
4. Click Add new token
5. Configure your token:
- Token name: A descriptive name (e.g., "Development laptop Git access")
- Expiration date: Set an appropriate date (max 365 days by default)
- Select scopes:
- read_repository - For clone and pull operations
- write_repository - For push operations (select this for full access)
- Or api - For complete access including API and Git
6. Click Create personal access token
7. Copy the token immediately - you won't be able to see it again!
# Test your token by cloning a repository
git clone https://oauth2:[email protected]/username/project.git
# Or use it when prompted for password
git clone https://gitlab.com/username/project.git
# Username: your-username (or any string - it's not validated)
# Password: YOUR_TOKEN (paste your Personal Access Token)If you have repositories already cloned with password authentication, update them to use your token:
Option 1: Update the remote URL
# Check current remote URL
git remote -v
# Update to include token (not recommended for security)
git remote set-url origin https://oauth2:[email protected]/username/project.git
# Better: Update without embedding token
git remote set-url origin https://gitlab.com/username/project.git
# Then enter token when promptedOption 2: Clear cached credentials and re-authenticate
# On Windows - clear from Credential Manager
git credential-manager erase
host=gitlab.com
protocol=https
# Press Enter twice
# On macOS - clear from Keychain
git credential-osxkeychain erase
host=gitlab.com
protocol=https
# Press Enter twice
# On Linux - clear stored credentials
rm ~/.git-credentials # if using 'store' helper
git credential-cache exit # if using 'cache' helperOption 3: Edit .git/config directly
# Open the config file
git config --edit
# Change the URL from:
# url = https://[email protected]/username/project.git
# To:
# url = https://gitlab.com/username/project.gitThen run a git command and enter your token when prompted for password.
SSH keys are more convenient than tokens and don't expire. Set up SSH authentication:
Step 1: Check for existing SSH keys
ls -la ~/.ssh
# Look for id_ed25519.pub or id_rsa.pubStep 2: Generate a new SSH key if needed
ssh-keygen -t ed25519 -C "[email protected]"
# Press Enter to accept default location (~/.ssh/id_ed25519)
# Enter a passphrase for additional security (optional)Step 3: Add the key to your SSH agent
# Start the SSH agent
eval "$(ssh-agent -s)"
# Add your key
ssh-add ~/.ssh/id_ed25519Step 4: Add the public key to GitLab
# Copy your public key
cat ~/.ssh/id_ed25519.pub
# Copy the entire output1. Go to GitLab → Edit profile → SSH Keys
2. Click Add new key
3. Paste your public key in the "Key" field
4. Give it a title and set an expiration date
5. Click Add key
Step 5: Switch your remote to SSH
# Check current remote
git remote -v
# Change from HTTPS to SSH
git remote set-url origin [email protected]:username/project.git
# Test the connection
ssh -T [email protected]
# Should see: "Welcome to GitLab, @username!"Git Credential Manager (GCM) can authenticate using OAuth, which works seamlessly with 2FA:
Install Git Credential Manager:
# On Windows: Included with Git for Windows
# On macOS:
brew install --cask git-credential-manager
# On Linux (Debian/Ubuntu):
wget https://github.com/git-ecosystem/git-credential-manager/releases/latest/download/gcm-linux_amd64.deb
sudo dpkg -i gcm-linux_amd64.debConfigure Git to use GCM:
git config --global credential.helper managerHow it works:
1. When you perform a Git operation, GCM opens your browser
2. GitLab asks you to authorize the application
3. After approval, GCM caches the OAuth token
4. Future operations authenticate automatically
For GitLab self-managed instances:
# Configure the GitLab host
git config --global credential.https://gitlab.yourcompany.com.provider gitlabGCM is the recommended solution as it handles token refresh automatically and doesn't expose tokens in URLs or shell history.
CI/CD pipelines need special configuration when 2FA is enforced:
Option 1: Use CI_JOB_TOKEN (for same-project operations)
# .gitlab-ci.yml
clone_job:
script:
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/${CI_PROJECT_PATH}.gitNote: CI_JOB_TOKEN has limited permissions and may not work for cross-project access.
Option 2: Use a Project Access Token
1. Go to Settings → Access Tokens in your project
2. Create a token with read_repository and/or write_repository scopes
3. Add as CI/CD variable: Settings → CI/CD → Variables
- Key: GITLAB_TOKEN
- Value: Your token
- Protect variable: Yes (if only needed on protected branches)
- Mask variable: Yes
# .gitlab-ci.yml
push_job:
script:
- git remote set-url origin "https://oauth2:${GITLAB_TOKEN}@gitlab.com/${CI_PROJECT_PATH}.git"
- git push origin mainOption 3: Use Deploy Keys for SSH
1. Generate an SSH key pair specifically for CI
2. Add the public key as a Deploy Key: Settings → Repository → Deploy keys
3. Store the private key as a CI/CD file variable
4. Configure SSH in your pipeline:
before_script:
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- ssh-keyscan gitlab.com >> ~/.ssh/known_hostsAfter generating a token, configure Git to remember it securely:
Using Git Credential Manager (Recommended):
# Already configured if you followed step 4
git config --global credential.helper managerOn macOS (Keychain):
git config --global credential.helper osxkeychainOn Linux (libsecret/GNOME Keyring):
# Install libsecret
sudo apt install libsecret-1-0 libsecret-1-dev
# Build the helper
cd /usr/share/doc/git/contrib/credential/libsecret
sudo make
# Configure Git
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecretSimple credential cache (temporary):
# Cache for 1 hour (3600 seconds)
git config --global credential.helper 'cache --timeout=3600'Avoid these practices:
- Don't embed tokens in remote URLs in shared repositories
- Don't use credential.helper store (stores in plain text)
- Don't commit .git-credentials or .netrc files
- Don't expose tokens in CI/CD logs
### Understanding GitLab 2FA Enforcement
GitLab can enforce 2FA at multiple levels:
- Account level: Individual users enable 2FA in their settings
- Group level: Group owners can require 2FA for all members
- Instance level: GitLab administrators can require 2FA for all users
When 2FA is enforced at a higher level, users must comply within a grace period or lose access.
### Token Scopes Explained
| Scope | Access Level |
|-------|-------------|
| read_repository | Clone and fetch only (read-only) |
| write_repository | Full push/pull access |
| api | Complete API access (includes repository) |
For most Git operations, write_repository is sufficient and more secure than api.
### Self-Managed GitLab Instances
For self-managed GitLab instances, the configuration may differ:
# Configure credential helper for custom domain
git config --global credential.https://gitlab.yourcompany.com.helper manager
# Or for SSH
ssh-keygen -t ed25519 -C "[email protected]"
# Add to GitLab at: https://gitlab.yourcompany.com/-/profile/keys### Debugging Authentication Issues
# Enable verbose Git output
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push origin main
# Check credential helper configuration
git config --list --show-origin | grep credential
# Test credential helper directly
echo "protocol=https
host=gitlab.com" | git credential fill### Group Access Tokens (GitLab Premium)
For teams, consider using Group Access Tokens instead of personal tokens:
1. Go to Group → Settings → Access Tokens
2. Create a token with appropriate scopes
3. Use for shared CI/CD or team automation
These tokens aren't tied to individual users, making them better for team workflows.
### Token Rotation Best Practices
- Set expiration dates on all tokens (max 365 days)
- Use different tokens for different purposes (development, CI/CD)
- Rotate tokens before they expire
- Revoke tokens immediately if compromised: Edit profile → Access Tokens → Revoke
### GitLab vs GitHub 2FA Behavior
| Aspect | GitLab | GitHub |
|--------|--------|--------|
| Error message | "Two-factor authentication is required" | "Authentication failed" |
| Token URL syntax | oauth2:TOKEN or username:TOKEN | username:TOKEN |
| Built-in OAuth | Git Credential Manager | Git Credential Manager |
| Token scopes | Granular (read/write_repository) | repo (all or nothing) |
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git