Personal access tokens and other tokens
Intermediate GitLab UI GitHub UI Git CLI
Why this matters
Tokens are how HTTPS Git works now, and they are also the thing that silently stops working a year after you create it. Understanding scopes and expiry turns a mysterious Authentication failed on a Tuesday morning into a thirty-second fix.
What a token is
Three properties matter, and all three are improvements on a password:
- Scopes. A token can be allowed to read repositories and nothing else. A password can do everything you can do.
- Expiry. A forgotten token stops working; a forgotten password does not.
- Revocation. Deleting a token on the website is instant and affects nothing else you use.
Scopes: grant the least that works
| Platform | For cloning and pulling | For pushing | Avoid unless needed |
|---|---|---|---|
| GitLab | read_repository |
write_repository |
api (full account access through the API) |
| GitHub, fine-grained | Contents: Read | Contents: Read and write | organization-wide or all-repository access |
| GitHub, classic | repo (there is no narrower option) |
repo |
delete_repo, admin:* |
For everyday Git work on GitLab, the two repository scopes are enough; api is only needed by tools that read issues and merge requests, such as the GitLab Workflow extension for VS Code. On GitHub, prefer fine-grained tokens, which are limited to the repositories you select and to named permissions; classic tokens grant their scopes across every repository you can reach.
Creating one
Tokens are created on the website; the terminal only consumes them. When Git asks:
$ git pushUsername for 'https://gitlab.com': ana
Password for 'https://ana@gitlab.com':Type your username and paste the token as the password. Nothing is echoed while you paste. Your credential helper stores it afterwards (lesson 8.4).
To check which host a stored credential belongs to, and to remove it, see that lesson; there is no git token command.
For GitHub, VS Code can avoid tokens entirely: the Accounts icon at the bottom of the Activity Bar → Sign in with GitHub opens a browser and stores an OAuth credential in the system keychain. Cloning, pushing and the pull request extension then work with no token.
For GitLab, install the GitLab Workflow extension and add a token in its settings; that token needs the api scope, because the extension reads issues and merge requests, not only the repository.
Settings → Version Control → GitHub (or GitLab) → +. For GitHub choose Log In with GitHub… for the browser flow, or Log In with Token… to paste one. IntelliJ stores it in its password safe.
The token IntelliJ wants for its GitHub or GitLab features needs API access, not only repository access, because it lists pull requests and merge requests.
- Your avatar → Edit profile → Access → Personal access tokens.
- Generate token (older versions: Add new token).
- Fill in Token name (name the machine or purpose: "work laptop"), and Token description if your version offers it.
- Set an Expiration date. GitLab defaults to 365 days from today, which is also the maximum unless an administrator has raised it.
- Tick the scopes:
read_repositoryandwrite_repositoryfor Git; addapionly if a tool needs it. - Create, and copy the token immediately — it is shown once.
The same page lists your existing tokens with their scopes, expiry and last-used date, and revokes them with one click. Projects and groups have their own tokens under Settings → Access tokens, for automation that should not be tied to a person.
- Avatar → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
- Fill in Token name, Expiration and optionally Description.
- Choose the Resource owner: yourself, or an organization (which may require approval).
- Under Repository access, choose Only select repositories and pick the ones you need.
- Under Permissions → Repository permissions, set Contents to Read and write for pushing. Add others only as needed.
- Generate token, then copy it immediately.
Tokens (classic) on the same page are the older, broader kind; use them only when something requires a scope fine-grained tokens do not offer yet.
Expiry: the Tuesday-morning failure
A token that has expired produces exactly the same message as a wrong one:
remote: HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password.
fatal: Authentication failed for 'https://gitlab.com/northwind-trails/trailguide.git/'Nothing about your setup changed; the date did. Both platforms email you before a token expires, and both show the expiry date on the token page. When it happens:
- Create a new token with the same scopes.
- Remove the stored old one so the helper stops answering with it (lesson 8.4).
- Push; paste the new token when asked.
The other kinds of token
| Kind | Belongs to | Use it for |
|---|---|---|
| Personal access token | you | your own machines and scripts |
| Project access token (GitLab) | one project | automation for that project, independent of any person |
| Group access token (GitLab) | a group | automation across a group's projects |
| Deploy token (GitLab) / Deploy key (both) | one project | a server that only needs to clone; read-only by default |
CI/CD job token (GitLab) / GITHUB_TOKEN |
the pipeline | automatic, short-lived, scoped to the running job |
The rule behind the table: a token should belong to the smallest thing that needs it. A pipeline should not use your personal token, because it then stops working when you leave, and because its logs are read by more people than your laptop is.
Common mistakes
- Typing your account password. Never accepted; the error text even says so on GitLab.
- A token with only read scopes. Cloning works, pushing fails. Check the scopes on the token page.
- The helper answering with an old token. The error repeats no matter how many new tokens you make; remove the stored entry (lesson 8.4).
- One token for everything. Revoking it then breaks every machine at once.
- A classic GitHub token with
repofor a single project. It grants access to every repository you can reach; use a fine-grained token.
Try it yourself
Goal: audit your own tokens and understand what each can do.
- Open the token page for the platform you use.
- For each token, note its name, scopes, expiry and last-used date.
- Delete any that you cannot account for, or that no longer have a machine behind them.
- Check that the token your laptop uses has the narrowest scopes that still let you push.
Expected result: a short list of tokens, each with a purpose you can name, each expiring within a year, and none with api or repo unless something specific needs it.
Show solution
Unaccounted tokens are the point of the audit: a token from an old laptop or an experiment is a credential in the world with nobody watching it. Deleting one is safe — if something was using it, that thing asks for a new credential and you will know immediately what it was.