Solving Common Git Authentication Errors Using SSH and Tokens
Fix common Git authentication failures by matching your remote URL to SSH keys, HTTPS tokens, and credential helpers.
Solving Common Git Authentication Errors Using SSH and Tokens
Most Git authentication problems come down to a mismatch between the remote URL, the credential Git is trying to use, and the permissions on the Git hosting account. The error text can look dramatic, but the fix is usually mechanical once you know which path Git is taking.
Start with the remote URL:
git remote -v
If you see https://github.com/org/repo.git, Git is using HTTPS and will need a username plus a token or a credential helper that can provide one. If you see [email protected]:org/repo.git, Git is using SSH and will need a private key that matches a public key registered with the host.
Do not mix the two while troubleshooting. Pick HTTPS or SSH, make the remote URL match, then test that path directly.
Read the error literally
fatal: Authentication failed for 'https://...' usually means Git reached the server but did not present a valid HTTPS credential. The old credential may be cached, the token may be expired, or the token may not have access to that repository.
remote: Permission to org/repo denied or 403 Forbidden usually means your identity was recognized but does not have permission for the operation. For example, you may be using a token from the wrong account, a token without write access, or an SSH key attached to a different user.
Permission denied (publickey) means SSH did not offer a key the server accepts.
A repeated username/password prompt over HTTPS usually means Git keeps receiving a rejection and asking again. Typing your account password repeatedly will not help on hosts that require tokens for Git operations.
Fixing HTTPS with a personal access token
For HTTPS remotes, create a personal access token in your Git host account settings. The exact menu names differ between GitHub, GitLab, Bitbucket, and self-hosted platforms, but the shape is the same: create a token, give it repository access, set an expiration that matches your organization's policy, and copy it immediately.
When Git prompts you, use your normal username and paste the token as the password:
Username: your-username
Password: <paste-token-here>
Use the narrowest permissions that work. For a private repository you push to, you need read and write repository access. For cloning public repositories, you may not need a token at all. Fine-grained tokens and organization SSO rules can add another layer: a token may exist but still need approval or SSO authorization before it can access a company repository.
If Git never prompts for the new token, it is probably using an old cached credential. Clear the stored entry for the host, then try again.
On macOS, check Keychain Access for entries related to your Git host, such as github.com or git:https://github.com.
On Windows, open Credential Manager and remove the relevant generic credential for the host.
On Linux, inspect your configured helper:
git config --global --get credential.helper
git config --show-origin --get-all credential.helper
The cache helper stores credentials temporarily in memory. The store helper writes them to disk in plain text unless you configure a safer storage layer, so use it carefully. Git's credential helper system is designed to ask helpers for credentials and let helpers save successful ones; the security depends on the helper you choose.
A useful HTTPS reset sequence is:
git remote -v
git config --show-origin --get-all credential.helper
# remove the old credential from the OS credential store
git ls-remote origin
git ls-remote origin is a clean test because it contacts the remote without changing your working tree.
Fixing SSH with keys
For SSH remotes, first check whether you already have keys:
ls -al ~/.ssh
Common key names include id_ed25519, id_rsa, and host-specific names like github_work_ed25519. The .pub file is the public key you upload to the Git host. The file without .pub is private and should not be shared.
If you need a new key, Ed25519 is a good default on modern systems:
ssh-keygen -t ed25519 -C "[email protected]"
Use a passphrase unless your environment has a specific automation reason not to. Then add the public key to your Git host account or deploy key settings:
cat ~/.ssh/id_ed25519.pub
Test the connection directly:
ssh -T [email protected]
For GitLab or Bitbucket, replace the host name. A successful test usually prints a greeting or a message saying shell access is not provided. That is fine; Git-over-SSH authentication can still work.
If SSH still fails, ask the SSH client what it is doing:
ssh -vT [email protected]
Look for lines showing which keys are offered. If your key is not offered, load it into the agent:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
For multiple accounts on the same host, use ~/.ssh/config so Git knows which key belongs to which remote:
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/github_work_ed25519
IdentitiesOnly yes
Then point the repository at that alias:
git remote set-url origin git@github-work:org/repo.git
This avoids the common problem where SSH offers your personal key to a work repository.
Switching protocols cleanly
If your team standardizes on SSH, change an HTTPS remote like this:
git remote set-url origin [email protected]:ORG/REPO.git
If your company blocks SSH or requires HTTPS inspection, switch the other way:
git remote set-url origin https://github.com/ORG/REPO.git
After changing the URL, test with a read operation:
git fetch origin
Then test the action that failed:
git push origin HEAD
If fetch works but push fails, authentication is probably valid and authorization is the issue. Check branch protection, repository role, token scope, and organization SSO rules.
CI and server environments
On build agents and servers, avoid using a human's personal token when possible. Prefer deploy keys, machine users, or your CI system's built-in credentials store. Keep secrets out of command history and logs. Do not paste tokens into remote URLs like this unless you are in a controlled throwaway environment:
https://[email protected]/org/repo.git
That style can leak through logs, process lists, shell history, and config files.
For Jenkins, GitHub Actions runners, GitLab runners, and similar systems, store credentials in the platform's secret mechanism and inject them only for the job that needs them.
A quick checklist
Run these in order when you are stuck:
git remote -v
git ls-remote origin
If the URL is HTTPS, clear old cached credentials and use a current token with the right repository permissions.
If the URL is SSH, run:
ssh -T git@your-hostname
ssh -vT git@your-hostname
Confirm the expected key is being offered and that its public half is registered with the right account.
If authentication succeeds but push fails, look for authorization rules: protected branches, missing write role, expired SSO authorization, read-only deploy keys, or tokens without write permission.
The reliable fix is not to try random passwords. Match the remote protocol to the credential type, remove stale cached credentials, test the connection directly, and then check permissions on the repository.