A Token Is an Identity: Let Cursor Commit as Themselves
๐ A Token Is an Identity: Let Cursor Commit as Themselves
A collaborator joins your project. They work in Cursor, the Artificial Intelligence (AI) code editor, and they want its agent to do the mechanical parts of collaboration for them โ open an issue, push a branch, file a pull request. Reasonable. So they message you: "What token should I use?"
That single question hides a trap. The lazy answer โ hand them a copy of yours โ quietly breaks three things at once: attribution, security, and your ability to ever cleanly revoke access. The correct answer costs five minutes of setup and gets all three right.
This post is the long version of a conversation we actually had while building RescueDogs, a pet-adoption tracker forked from ScriptHammer. A collaborator (GitHub handle schlajo) needed Cursor to open issues and pull requests in his name, on a repository we own. Getting that right is a small masterclass in how GitHub authentication actually works โ and it generalizes to any template fork where more than one human, and more than one agent, touch the same code.
๐ค The Problem: An AI Agent Needs Hands, but Whose?
Cursor's agent can run shell commands and call the GitHub Application Programming Interface (API) on your behalf โ usually through the GitHub Command Line Interface (CLI), the gh tool. To do that, it needs credentials. Those credentials decide who GitHub thinks is acting every time the agent opens an issue or pushes a commit.
There are really only two ways to give an agent those hands:
- Share an existing token. Someone emails or pastes their Personal Access Token (PAT) to the collaborator, who drops it into Cursor. Fast. Also wrong, for reasons we'll unpack.
- Let the collaborator authenticate as themselves. They connect their own GitHub account, and everything the agent does is stamped with their identity.
๐ A Token Is an Identity, Not a Password
Here is the mental model that makes everything else click:
> โ ๏ธ The core idea: A GitHub token does not grant access to a repository. It grants access as a person. Whoever created the token, that is who GitHub believes is acting โ no matter whose repo the token can reach.
Read that twice, because it inverts the way most people think about tokens. We tend to picture a token as a key to a door (the repository). It is really a key to an identity (the account). The repositories it can open are just a consequence of who that identity is and what you scoped the token to.
This distinction became concrete on RescueDogs. Our collaborator's token had two properties that sound contradictory until you hold the model in your head:
- Resource owner:
TortoiseWolfeโ because that is the account that owns the RescueDogs repository. Fine-grained tokens are scoped under the owner of the resources they touch. - Authenticated identity:
schlajoโ because that is the account that created the token. Every issue, commit, and pull request the token produces is attributed toschlajo.
๐๏ธ Where Attribution Actually Lives
It helps to know that "who did this" is recorded in more than one place, and the token drives all of them:
- Git author and committer. Every commit carries an author (who wrote the change) and a committer (who applied it). Locally these come from your
git config user.nameanduser.email. But when the agent pushes through the API, GitHub also links the commit to an account by matching the commit email to a verified email on that account. Get the email wrong and the commit shows up as an anonymous gravatar with no account behind it โ technically present in history, but disconnected from the person. - The actor on issues and pull requests. Issues, comments, reviews, and pull-request actions have no "author email" to match โ they are attributed purely to whichever account the token authenticates as. There is no ambiguity here: the token is the byline.
- The Verified badge. Commits signed with a matching key show a green "Verified" badge. That is a separate layer, but it rests on the same foundation โ an identity GitHub can tie to a real account.
git config user.email is an email verified on that same account (drives commit-to-account linking). Cursor's built-in sign-in handles both for you; the manual token path is where people occasionally get the email half wrong.
Now watch what happens if you take the lazy path and share a token instead.
โ Why Sharing a Token Breaks Everything
Say you hand the collaborator a copy of your token. Three failures cascade:
- Attribution collapses. Because the token authenticates as you, every issue the collaborator's agent opens, every commit it pushes, shows up under your name. Your
git blamelies. Your contributor graph lies. Six months later nobody can tell who actually wrote what. - Two-Factor Authentication (2FA) is bypassed. You almost certainly protect your account with 2FA โ a second factor beyond your password. A token skips it entirely; that is the point of tokens. So the moment your token leaves your machine, anyone holding it acts as you without ever facing your second factor. You have effectively handed out a 2FA-exempt copy of yourself.
- Revocation becomes all-or-nothing. Tokens are not per-person. If you shared one token with a collaborator and later need to cut off just them, you can't โ revoking that token also breaks your own automation that used it. Your only options are "trust them forever" or "break your own setup." Neither is acceptable.
> ๐ก The rule: Never send a token, and never accept one. Credentials are personal. The maintainer never hands one over; the collaborator generates their own.
๐ง Fine-Grained Tokens and Least Privilege
If the collaborator is going to make their own credentials, the next question is: how much power should those credentials carry? The answer is as little as possible while still doing the job โ the principle of least privilege.
GitHub offers two kinds of Personal Access Token, and the difference matters:
- Classic PATs are coarse. A single classic token tends to grant broad scopes across all your repositories at once. If it leaks, the blast radius is your entire account.
- Fine-grained PATs are surgical. You pick exactly which repository (or repositories) the token can touch, and exactly which permissions it has on them. If it leaks, the damage is bounded to that one repo and those few permissions.
| Permission | Access | Why the agent needs it | | ----------------- | ---------------- | -------------------------------------- | | Issues | Read and write | Open and comment on issues | | Contents | Read and write | Push commits to branches | | Pull requests | Read and write | Open and update pull requests | | Metadata | Read (automatic) | Required baseline for everything above |
Notice what is absent: no Administration (can't change repo settings, add collaborators, or delete the repo), no Secrets (can't read your Continuous Integration secrets), and no access to any other repository. Scope the token to the single repo, set those four rows, and leave everything else at "No access."
> โ Best practice: Give the token an expiration โ 90 days is a sane default. A token that expires on its own is one you can never forget to clean up. You can always regenerate it.
๐จ Setup, Two Ways
There are two ways for the collaborator to connect their own GitHub identity to Cursor. Recommend the first; keep the second in your back pocket.
Option A: Cursor's Built-In GitHub Sign-In (Recommended)
This is the least error-prone path because Cursor manages the token and its scopes for the collaborator. No copying secrets, no choosing permission checkboxes.
- Open Cursor โ Settings (the gear icon, or
Ctrl+,). - Find the GitHub or Integrations section.
- Click Sign in with GitHub โ this opens the browser to authorize the collaborator's account.
- Approve the authorization. Cursor now acts as the collaborator for GitHub operations.
Option B: A Fine-Grained Token by Hand (Explicit Control)
Use this when the collaborator prefers to manage the credential directly, or when the built-in sign-in isn't available.
- Go to github.com/settings/personal-access-tokens/new (the path through the UI: Settings โ Developer settings โ Personal access tokens โ Fine-grained tokens โ Generate new token).
- Token name: something descriptive, like
cursor-rescuedogs. - Expiration: a limited window, e.g. 90 days.
- Resource owner: the account that owns the target repo (for us,
TortoiseWolfe). - Repository access โ Only select repositories โ pick the single target repo. Do not grant all repositories.
- Repository permissions: set exactly the four rows from the table above (Issues, Contents, Pull requests each Read and write; Metadata is auto-selected). Everything else stays "No access."
- Generate token, copy it, and paste it into Cursor or into
gh auth login.
๐งช Verify It's Really Them
Setup that you don't verify is setup you don't have. Before trusting the agent with real work, confirm three things: who GitHub thinks the collaborator is, that they can actually push, and that issue creation works end to end. The gh CLI makes all three quick.
# 1. Who am I authenticated as? Must print the collaborator's handle.
gh auth status
gh api user --jq .login # expect: schlajo
# 2. Do I have push access to the target repo (without changing anything)?
gh api repos/TortoiseWolfe/RescueDogs --jq '.permissions'
# expect an object containing: "push": true
# 3. Prove issue creation, then clean up after yourself.
gh issue create -R TortoiseWolfe/RescueDogs \
--title "cursor auth smoke test (delete me)" \
--body "verifying Cursor can open issues as schlajo"
# then immediately close the throwaway:
gh issue close <the-number-it-printed> -R TortoiseWolfe/RescueDogs \
--comment "smoke test passed, closing"
If gh api user --jq .login prints the collaborator's handle (not the maintainer's), push is true, and the throwaway issue opens under their name, the identity is wired correctly. The self-closing smoke test proves the full loop without leaving litter behind.
> ๐ก Tip: The single most important check is gh api user --jq .login. If that ever prints the wrong account, stop โ a shared or mis-configured credential has crept in, and every action from here would be misattributed.
๐ When Verification Goes Sideways
A few failure modes come up often enough to name:
gh api user --jq .loginprints the maintainer, not the collaborator. The machine is still authenticated with an old or shared credential. Rungh auth logout, thengh auth loginagain with the collaborator's own account or token. Never "just proceed" โ every action would be misattributed.pushisfalsein the permissions object. The token was created without Contents: Read and write, or it was scoped to the wrong repository. Regenerate it with the correct permission table, or, on the built-in path, re-authorize and confirm the repo is in scope.- Commits land as an anonymous gravatar with no account link. The token is correct, but the local
git config user.emailis an email that isn't verified on the collaborator's GitHub account. Fix it withgit config user.email "verified-address@example.com"using an email listed under their account's email settings. - The agent's
ghcalls fail with a 404 on a repo that clearly exists. For fine-grained tokens, a 404 (rather than a 403) is GitHub's way of hiding resources the token can't see. It almost always means the repository wasn't selected in the token's Repository access โ not that the repo is missing.
git history archaeology.
๐ The Paste-Ready Prompt: A House Rule
Wiring up identity is half the story. The other half is how you hand work to a collaborator who implements in Cursor. On RescueDogs this became a written house rule, and it is worth stealing.
The rule: every issue that asks for a code change, and every pull-request review that requests changes, must include a fenced code block the collaborator can paste straight into Cursor's chat โ not just a prose description of the fix.
Why bother? Because a collaborator working in Cursor doesn't want to translate your prose into edits โ they want to hand their agent something executable. A ready prompt with byte-exact targets removes the two things that waste the most time: re-deriving what you meant, and whitespace mismatches when the agent tries to find the code you described.
Here is the shape of a good hand-off prompt:
Fix the mobile-responsive auth-form layout. Context: issue #15
(github.com/TortoiseWolfe/RescueDogs/issues/15), PR #13.
The horizontal label rows have no responsive breakpoint, so on phones
the inputs collapse. Make the rows stack on mobile and go horizontal
at sm+, following docs/MOBILE-FIRST.md.
=== src/components/auth/SignUpForm/SignUpForm.tsx ===
Replace:
<div className="flex flex-row items-center gap-x-6">
with:
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-x-6">
After editing, verify in-container:
docker compose exec rescuedogs pnpm run type-check
docker compose exec rescuedogs pnpm run lint
Then check /sign-up at 360px width in the device toolbar.
A few details make these prompts reliable:
- Wrap the prompt in a four-backtick fence on the GitHub side, not three. The prompt itself often contains triple-backtick code fences or nested class strings; four backticks guarantee GitHub renders the whole thing as one copyable block with a working Copy button.
- Open with a context line โ the issue or pull-request number and the repo Uniform Resource Locator (URL) โ so the agent can pull surrounding context.
- Give byte-exact find/replace targets, pulled verbatim from the branch, not paraphrased. Agents match on whitespace.
- End with verify commands (type-check, lint) and one concrete manual check.
> โ
Bonus discipline: When you reject a pull request, use GitHub's request-changes review (gh pr review ) rather than pushing fixup commits onto the collaborator's branch. Pushing to their branch erases their authorship โ the very thing this whole exercise is about protecting.
๐ Revoke, Rotate, Expire
Credentials are not "set and forget." The same properties that make fine-grained, per-person tokens safe only hold if you maintain them.
- Expire by default. We set 90-day expirations above precisely so nobody has to remember to clean up. When a token lapses, the collaborator regenerates it in two minutes โ a small tax that beats an eternal credential drifting around.
- Rotate on any suspicion. If a token might have leaked โ pasted in the wrong window, committed by accident, shown on a screen-share โ revoke it and mint a new one. Because it is fine-grained and per-repo, rotation is cheap and the blast radius was already tiny.
- Revoke cleanly when someone leaves. This is the payoff for doing it right. Because the collaborator authenticated as themselves, off-boarding is a single revocation of their token, from their account, touching nobody else. Compare that to the shared-token world, where "removing one person" means breaking everyone.
Here is the whole discipline as a checklist:
- โ Collaborator uses their own credentials โ never a shared token
- โ Token is fine-grained, scoped to one repository
- โ Permissions limited to Issues, Contents, Pull requests (write) + Metadata (read)
- โ No Administration, Secrets, or other-repo access
- โ A sensible expiration is set
- โ
Identity verified with
gh api user --jq .login - โ Work handed off via paste-ready Cursor prompts
- โ Off-boarding is a single per-person revocation
๐ฏ Takeaways for Template Forkers
If you fork ScriptHammer โ or any template โ and bring on a collaborator who works with an AI agent, the pattern is the same every time:
- Never share credentials. A token is an identity; sharing one erases attribution, bypasses 2FA, and makes clean revocation impossible.
- Let each person authenticate as themselves, ideally through Cursor's built-in GitHub sign-in, or with a fine-grained, single-repo token if they want explicit control.
- Grant least privilege โ the four permissions the job needs, scoped to the one repo, with an expiration.
- Verify the identity before trusting the agent with real work.
- Hand off work as paste-ready prompts, and reject via request-changes rather than rewriting someone's branch.
