{"id":"l-git-3-2","courseId":"git-github","lang":"en","kind":"lesson","summary":"Authenticate Git with GitHub over HTTPS through a credential manager or over SSH with an ed25519 key, test the connection, and switch a remote between the two URL styles.","html":"<p>Your first <code dir=\"ltr\">git push</code> asked for a username and password. GitHub needs proof that the person pushing to <code dir=\"ltr\">maya-okafor/portfolio</code> is Maya. It won’t accept your account password for that; it hasn’t since 2021, when it switched Git operations to stronger credentials. You have two good options, and you only set either one up once per computer.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">The two protocols</h2>\n<p>The remote URL decides how Git talks to GitHub:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>https://github.com/maya-okafor/portfolio.git    HTTPS\ngit@github.com:maya-okafor/portfolio.git        SSH</code></pre></div>\n<p><strong>HTTPS</strong> authenticates with a token. A credential manager gets one by sending you through GitHub’s normal browser sign-in (including two-factor) and stores it in your system’s secure storage. <strong>SSH</strong> authenticates with a key pair you generate; GitHub stores the public half and checks you hold the private half.</p>\n<p>Which should you pick? HTTPS with a credential manager is the easiest to set up and works on any network that allows web browsing. SSH takes five more minutes, doesn’t expire (GitHub only removes a key after a year without use), and is what many developers use day to day. Both are equally secure when set up properly. If in doubt, start with HTTPS; you can switch any time.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Option 1: HTTPS with a credential manager</h2>\n<p><strong>Git Credential Manager</strong> (GCM) comes with Git for Windows. On macOS, install it with Homebrew (<code dir=\"ltr\">brew install --cask git-credential-manager</code>); on Linux, follow the install guide on its GitHub page. The next time Git needs credentials, a browser window opens, you sign in to GitHub and approve, and GCM stores the token. You won’t be asked again.</p>\n<p>If you’ve installed the GitHub CLI, it can do the same job:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">gh</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> auth</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> login</span></span></code></pre></div>\n<p>Choose GitHub.com, HTTPS, and answer yes to “Authenticate Git with your GitHub credentials?”. It signs you in through the browser and configures Git to use it as a credential helper.</p>\n<p>The manual alternative is a <strong>personal access token</strong>: create one under Settings, Developer settings, and paste it where Git asks for a password. It works, but you have to manage expiry and storage yourself, so prefer a credential manager on your own machine and keep tokens for automation.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Option 2: SSH keys</h2>\n<p>Generate a key pair. Ed25519 is the modern default:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">ssh-keygen</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -t</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> ed25519</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -C</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"maya.okafor@example.com\"</span></span></code></pre></div>\n<p>Press Enter to accept the default location, then set a passphrase. The passphrase protects the private key if your laptop is ever stolen. The comment after <code dir=\"ltr\">-C</code> is a label to help you recognise the key later.</p>\n<p>You now have two files:</p>\n<ul>\n<li><code dir=\"ltr\">~/.ssh/id_ed25519</code>, the <strong>private key</strong>. It never leaves this machine.</li>\n<li><code dir=\"ltr\">~/.ssh/id_ed25519.pub</code>, the <strong>public key</strong>. Safe to share; this is what GitHub gets.</li>\n</ul>\n<p>Load the key into the SSH agent so you type the passphrase once per session, not on every push:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">eval</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"$(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">ssh-agent</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -s</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)\"</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">ssh-add</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> ~/.ssh/id_ed25519</span></span></code></pre></div>\n<p>On macOS, <code dir=\"ltr\">ssh-add --apple-use-keychain ~/.ssh/id_ed25519</code> stores the passphrase in your Keychain; GitHub’s guide shows the matching <code dir=\"ltr\">~/.ssh/config</code> lines. Then copy the public key (<code dir=\"ltr\">pbcopy &lt; ~/.ssh/id_ed25519.pub</code> on macOS, or open the file and copy its single line), and on GitHub go to Settings, SSH and GPG keys, New SSH key, and paste it.</p>\n<p>Test the connection:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">ssh</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -T</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> git@github.com</span></span></code></pre></div>\n<p>The first time, SSH asks you to confirm GitHub’s host fingerprint; compare it with the fingerprints GitHub publishes in its docs before typing <code dir=\"ltr\">yes</code>. Success looks like this:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Hi maya-okafor! You've successfully authenticated, but GitHub does not provide shell access.</code></pre></div>\n<p>That “does not provide shell access” line is normal; you’re not logging in to a server, only proving who you are.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Sharing the wrong key</span></p>\n<p>The file without <code dir=\"ltr\">.pub</code> is your private key. Pasting it into GitHub, a chat or an issue is like posting your house key online: delete that key from everywhere, generate a new pair, and add the new public key. If the file you’re about to share doesn’t end in <code dir=\"ltr\">.pub</code>, stop.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">Pointing the remote at the right protocol</h2>\n<p>Authentication follows the URL. If you set up SSH but cloned with HTTPS, Git keeps using HTTPS. Check and switch:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"6\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> remote</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -v</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> remote</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> set-url</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> git@github.com:maya-okafor/portfolio.git</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> push</span></span></code></pre></div>\n<p>Going the other way is the same command with the <code dir=\"ltr\">https://</code> URL.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>One key per machine</span></p>\n<p>Generate a separate key on each computer you use and give each a clear title on GitHub (“Maya MacBook 2026”). If a laptop is lost, you delete that one key from GitHub and nothing else breaks.</p>\n</aside>\n<p>With pushing sorted, you can start working the way teams do on GitHub: through pull requests.</p>\n","toc":[{"id":"sec-1","text":"The two protocols","level":2},{"id":"sec-2","text":"Option 1: HTTPS with a credential manager","level":2},{"id":"sec-3","text":"Option 2: SSH keys","level":2},{"id":"sec-4","text":"Pointing the remote at the right protocol","level":2}],"takeaways":["GitHub doesn’t accept your account password for Git operations; use a credential manager, a personal access token, or an SSH key.","Git Credential Manager (or <code dir=\"ltr\">gh auth login</code>) signs you in through the browser once and stores the credential securely.","An SSH key pair has a private half that never leaves your machine and a public <code dir=\"ltr\">.pub</code> half that you add to GitHub.","<code dir=\"ltr\">ssh -T git@github.com</code> tests SSH authentication and greets you by username when it works.","The remote URL decides the protocol: <code dir=\"ltr\">https://github.com/…</code> uses HTTPS, <code dir=\"ltr\">git@github.com:…</code> uses SSH; switch with <code dir=\"ltr\">git remote set-url</code>."],"further":[{"title":"About authentication to GitHub","url":"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github"},{"title":"Caching your GitHub credentials in Git","url":"https://docs.github.com/en/get-started/git-basics/caching-your-github-credentials-in-git"},{"title":"Generating a new SSH key and adding it to the ssh-agent","url":"https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent"},{"title":"Testing your SSH connection","url":"https://docs.github.com/en/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection"}],"quiz":[{"q":"<p><code dir=\"ltr\">git push</code> over HTTPS asks for a password. You type your GitHub password and get <code dir=\"ltr\">Support for password authentication was removed</code>. What should you do?</p>","options":[{"text":"Reset your GitHub password; the old one must have expired.","why":"The password is fine for the website. GitHub simply doesn’t accept account passwords for Git operations at all."},{"text":"Switch the remote to <code dir=\"ltr\">http://</code> instead of <code dir=\"ltr\">https://</code>.","why":"GitHub doesn’t serve Git over plain HTTP, and sending credentials unencrypted would be worse, not better."},{"text":"Turn off two-factor authentication so the password works.","why":"Two-factor isn’t the cause, and turning it off makes your account easier to take over. Git still wouldn’t accept the password."},{"text":"Set up a credential manager (or <code dir=\"ltr\">gh auth login</code>) that signs you in through the browser, or use a personal access token.","why":"Correct. GitHub requires a token-based credential over HTTPS; the credential manager obtains and stores one for you."}],"answer":3},{"q":"<p>After running <code dir=\"ltr\">ssh-keygen -t ed25519</code>, which file do you paste into GitHub’s “New SSH key” form?</p>","options":[{"text":"<code dir=\"ltr\">~/.ssh/id_ed25519</code>, the key file itself.","why":"That’s the private key. Anyone holding it can act as you. It never leaves your machine."},{"text":"<code dir=\"ltr\">~/.ssh/known_hosts</code>","why":"That file records servers you’ve connected to. It isn’t a key of yours."},{"text":"<code dir=\"ltr\">~/.ssh/id_ed25519.pub</code>, the public key.","why":"Correct. The public half is safe to share; GitHub uses it to check that you hold the matching private key."},{"text":"Both files, so GitHub can verify them against each other.","why":"GitHub only ever needs the public key. Uploading the private key anywhere means you should generate a new pair."}],"answer":2},{"q":"<p>SSH works (<code dir=\"ltr\">ssh -T git@github.com</code> greets you), but <code dir=\"ltr\">git push</code> still asks for a username and password. Why?</p>","options":[{"text":"The remote URL is still HTTPS; switch it with <code dir=\"ltr\">git remote set-url origin git@github.com:maya-okafor/portfolio.git</code>.","why":"Correct. Git uses whatever protocol the remote URL names. An <code dir=\"ltr\">https://</code> URL never touches your SSH key."},{"text":"The SSH key needs to be added to the repository’s settings rather than your account.","why":"Keys on your account work for every repository you can access. Repository deploy keys are for servers, not you."},{"text":"You need to run <code dir=\"ltr\">ssh-keygen</code> again inside the repository.","why":"Keys belong to your user, not to a repository. Generating another one won’t change which protocol Git uses."}],"answer":0},{"q":"<p>You’re on a network that blocks port 22. Which option keeps Git working with the least fuss?</p>","options":[{"text":"Generate an RSA key instead of ed25519.","why":"The key type has no effect on which port the connection uses."},{"text":"Use HTTPS with a credential manager, which runs over port 443 like normal web traffic.","why":"Correct. HTTPS goes wherever the web goes. (GitHub also offers SSH over port 443 at <code dir=\"ltr\">ssh.github.com</code> if you prefer SSH.)"},{"text":"Push through GitHub’s website by uploading files one at a time.","why":"That works for a quick edit, but it bypasses your local history and isn’t a way to keep using Git."},{"text":"Disable the firewall on your laptop.","why":"The block is on the network, not your laptop, and switching off your own firewall only adds risk."}],"answer":1}],"exercise":{"kind":"order","items":["keygen","agent","copy","github","test","seturl"],"title":"Set up SSH for the portfolio","prompt":"<p>You’ve decided to push the portfolio over SSH. Put the setup steps in the order that works, from no key at all to a successful <code dir=\"ltr\">git push</code> over SSH. Finish all the local key work (create the key, load it into the agent) before you touch GitHub, and prove SSH works before you change the repository.</p>\n","hints":["You can’t add or copy a key that doesn’t exist yet.","GitHub must know your public key before the connection test can greet you.","Changing the remote URL is the last step, once you know SSH works."],"explanation":"<p>First generate the pair with <code dir=\"ltr\">ssh-keygen -t ed25519</code>, then load the private key into the agent so you don’t retype the passphrase. Copy the public key (the <code dir=\"ltr\">.pub</code> file) and add it to your GitHub account. <code dir=\"ltr\">ssh -T git@github.com</code> proves the setup works before you change anything in the repository. Finally, <code dir=\"ltr\">git remote set-url</code> switches <code dir=\"ltr\">origin</code> to the SSH URL so Git uses the key on the next push.</p>\n","checkLabels":{},"itemText":{"keygen":"Run <code dir=\"ltr\">ssh-keygen -t ed25519 -C &quot;maya.okafor@example.com&quot;</code> and set a passphrase.","agent":"Start the agent and run <code dir=\"ltr\">ssh-add ~/.ssh/id_ed25519</code>.","copy":"Copy the contents of <code dir=\"ltr\">~/.ssh/id_ed25519.pub</code>.","github":"On GitHub, open Settings, SSH and GPG keys, New SSH key, and paste it.","test":"Run <code dir=\"ltr\">ssh -T git@github.com</code> and check it greets you by username.","seturl":"Run <code dir=\"ltr\">git remote set-url origin git@github.com:maya-okafor/portfolio.git</code>, then <code dir=\"ltr\">git push</code>."}},"words":704,"readMinutes":3}