
GitHub CLI tokens that had no expiration can now be set to expire and auto-renew within a few hours
This page has been translated by machine translation. View original
Hi there! I'm Nishimura Yuji from the Operations Division!
When you logged in to GitHub CLI (gh) with gh auth login, it used to save an OAuth token with no expiration date. While convenient, a leaked token could be used until manually revoked.
GitHub CLI 2.103.0, released on October 21, 2026 (US time), introduced a new authentication method as a Public Preview. It's a mechanism where gh automatically refreshes access tokens that expire within a few hours. This article explains how to enable it and covers the impact on scripts using gh auth token, tokens passed via environment variables, HTTPS git operations, and gh auth refresh.
What I verified locally was the help output, reading and writing settings, and the display at login start using the official GitHub CLI 2.103.1 binary on macOS (arm64), as well as the gh auth status output after re-logging in with an expiring token using GitHub CLI 2.103.2. I did not test the automatic refresh behavior after a token expires. The auto-refresh behavior is described based on the release notes, GitHub Docs, PRs, and the v2.103.1 source code.
Tokens that expire within a few hours and are automatically refreshed by gh
When you choose expiring tokens, gh saves a short-lived access token together with a refresh token that is rotated on each exchange. When an access token is needed, if it is expired or close to expiring, gh uses the refresh token to obtain a new access token. Users do not need to log in again.
GitHub Docs cites as a benefit that even if a token leaks, it becomes unusable within a few hours.
The main specifications are as follows.
| Item | Details |
|---|---|
| Required version | GitHub CLI 2.103.0 or later (latest as of October 24, 2026 is 2.103.2) |
| Availability | Public Preview (GitHub Changelog) |
| Supported hosts | github.com, GHE.com (GitHub Enterprise Cloud with data residency options). GitHub Enterprise Server 3.23 or later |
| When host is unsupported | A regular non-expiring token is issued |
| Default behavior | Disabled (opt-in). Existing logins and manually provided tokens are not affected |
Regarding token validity periods, the GitHub CLI documentation only says "a few hours." The expiring access token specification for OAuth Apps states that access tokens expire after 8 hours and refresh tokens expire after 6 months if unused. The CLI documentation does not state the duration of gh's tokens. When I re-logged in with an expiring token locally (October 26, 2026, github.com), the gh auth status output showed the access token expiring after 8 hours and the refresh token after 6 months, which matched this specification.
The GitHub Changelog (October 21, 2026) also states that a future release plans to make expiring tokens the default for new logins (timing is not yet determined).
Token flow between gh, git, and scripts
The following diagram is one I created myself; it is not an official architecture diagram. gh obtains an access token and refresh token from GitHub and stores them in the credential store. gh commands, git (when gh is set as the credential helper), and scripts calling gh auth token all receive tokens through gh. When a token is close to expiring, gh exchanges it for a new one before passing it along.

If you store the token value somewhere outside gh, you fall outside this refresh flow. Most of the caveats described below stem from this point.
Enabling with --expiring and expiring_credentials
Since GitHub CLI 2.103.0 or later is required, check your version first. If you are using scripts or extensions, reading "Things to Check Before Switching" below first will help you identify scripts that would break after expiration before you make the switch.
gh --version
To use it for a single login only, add --expiring.
gh auth login --expiring
To use it for every subsequent login, enable the expiring_credentials setting.
gh config set expiring_credentials enabled
I verified the behavior of expiring_credentials by pointing GH_CONFIG_DIR to a separate empty directory and using the official GitHub CLI 2.103.1 binary. The default value is disabled, and values other than enabled / disabled are not accepted. The command to enable it displays nothing on success.
$ gh config get expiring_credentials
disabled
$ gh config set expiring_credentials yes
failed to set "expiring_credentials" to "yes": valid values are 'enabled', 'disabled'
$ gh config set expiring_credentials enabled
$ gh config get expiring_credentials
enabled
If the setting is enabled and you want a specific login to use a non-expiring token, add --expiring=false. According to gh auth login --help, an explicitly specified flag takes precedence over the setting.
--expiring is a request for expiring tokens and does not guarantee issuance. The help also notes that supported hosts may issue expiring tokens even without the flag.
I also verified the display at login start by running in a non-TTY state (not connected to a terminal). The one-time code display and authorization page (https://github.com/login/device) were the same regardless of whether --expiring was present.
$ gh auth login --web --expiring -h github.com -p https
! One-time code (XXXX-XXXX) copied to clipboard
Open this URL to continue in your web browser: https://github.com/login/device
Note that in 2.103.1, a one-line hint about expiring tokens is now shown during interactive logins to github.com and similar hosts when --expiring is not specified and the setting is not enabled (PR #14798). According to the PR, it only appears when stderr is connected to a terminal, so it was not shown in the non-TTY run above.
Checking expiration and refresh status
You can use gh auth status to check whether a token is subject to automatic refresh and when the access token expires. Looking at status.go in v2.103.1, it is implemented to output the following lines for expiring tokens.
- Expiring token that gh refreshes automatically
- Token expires: <date/time> (<time remaining>)
- Refresh token expires: <date/time> (<time remaining>)
Immediately after re-logging in with an expiring token on my account, the output was as follows (token value is redacted).
$ gh auth status
github.com
✓ Logged in to github.com account <username> (keyring)
- Active account: true
- Git operations protocol: https
- Token: gho_************************************
- Expiring token that gh refreshes automatically
- Token expires: 2026-10-26T13:25:11Z (in about 8 hours)
- Refresh token expires: 2027-04-26T05:25:11Z (in about 6 months)
- Token scopes: 'delete_repo', 'gist', 'project', 'read:org', 'repo', 'user', 'workflow'
Both gh auth status and gh auth token automatically refresh expired or near-expiring tokens before returning results. To view the current state without triggering a refresh, add --no-refresh to either command.
According to the same source, if the refresh token itself is rejected, the stored expiring credentials are deleted, and gh auth status will prompt you to re-login with gh auth login -h <host>.
Things to Check Before Switching
Scripts that store the value of gh auth token
The most significant impact is on scripts and extensions that store the output of gh auth token in a variable or file for reuse. Since the stored value expires within a few hours, the release notes advise running gh auth token again each time a token is needed. The GitHub Changelog also recommends not opting in yet if you have workflows that assume tokens remain valid for a long time.
# With expiring tokens, obtain a fresh token immediately before each use
curl -H "Authorization: Bearer $(gh auth token)" https://api.github.com/user
Tokens passed via environment variables are not refreshed
According to the release notes, tokens passed via GH_TOKEN or GITHUB_TOKEN, such as in GitHub Actions, are not refreshed by gh.
For HTTPS git, set gh as the credential helper
If you use git over HTTPS, setting gh as git's credential helper allows gh to refresh and provide expiring tokens.
gh auth setup-git
According to GitHub Docs, when you log in with an expiring token, if gh is not already set as the helper, it will ask whether to configure it.
I checked the handling when another credential helper is already in use in git_credential.go of v2.103.1. Since that helper cannot refresh expiring tokens and would keep providing expired tokens, gh asks you to set itself as the helper. When logging in with expiring tokens requested via --expiring or the setting, you are presented with three choices: "Set gh as the helper (recommended)," "Continue without configuring," or "Abort login." Choosing gh configures it as the credential helper for that host, even if another helper exists.
If an expiring token is issued even though you did not request one, you are asked whether to set gh as the helper instead. If you decline, gh does not save the token to the existing helper. It deletes any credentials for that host already stored in the helper and displays a warning advising you to run gh auth setup-git --hostname <host>.
gh auth login --help also recommends git 2.46 or later. With git 2.46 or later, gh marks tokens it passes to git as "do not cache" (ephemeral). This prevents caching helpers like git-credential-cache from retaining expired tokens. In helper.go of v2.103.1, when git is older, gh is implemented to pass the token while outputting a warning to stderr recommending a git update.
When changing scopes with gh auth refresh
The handling when adding scopes with gh auth refresh --scopes differs between the documentation and the CLI help.
- GitHub Docs: If you are using an expiring token, add
--expiringor enable the setting. Without it, the token may be replaced with a non-expiring one. gh auth refresh --help(2.103.0 / 2.103.1): Without the flag, the current token type (expiring or non-expiring) is preserved.
refresh.go in v2.103.1 is also implemented to preserve the current token type when no flag is provided, consistent with the help. Either way, explicitly specifying --expiring as the documentation advises ensures expiring tokens are maintained regardless of which description is accurate.
gh auth refresh --scopes read:project --expiring
Deciding Whether to Enable It
| Use case | Decision |
|---|---|
Using gh interactively on a local machine |
A candidate for enabling. If gh auth setup-git is already done and git is 2.46 or later, no additional steps are needed for daily operations according to the official documentation |
Scripts or extensions that store the value of gh auth token for reuse |
First update them to run gh auth token each time it is needed, then enable it |
| Automation that passes tokens via environment variables, such as GitHub Actions | Not subject to automatic refresh. Keep using the conventional method without changing the setting |
| GitHub Enterprise Server 3.22 or earlier | Expiring tokens are not issued; opting in will result in non-expiring tokens |
Since this is in Public Preview, start by trying gh auth login --expiring on a subset of machines. After confirming that your scripts and extensions continue to work after token expiration, enable expiring_credentials.
Summary
With GitHub CLI 2.103.0 or later, you can switch to an authentication method where gh automatically refreshes access tokens that expire within a few hours. The switch is made with gh auth login --expiring or gh config set expiring_credentials enabled. This reduces the window during which a leaked token can be used.
The three things to check before switching are: whether scripts call gh auth token every time they need a token, whether gh is set as the credential helper for HTTPS git, and whether git is version 2.46 or later. Following the guidance in the release notes and GitHub Docs, having these in place should prevent authentication errors in scripts and git push due to expired tokens (I did not test post-expiration behavior this time). When changing scopes, it is safest to also add --expiring to gh auth refresh.
While in Public Preview, it is recommended to try gh auth login --expiring on a subset of machines, confirm that scripts and extensions continue to work after expiration, and then enable expiring_credentials.
I hope this is helpful to someone.
Reference links:
- GitHub CLI 2.103.0 Release Notes
- GitHub CLI 2.103.1 Release Notes
- GitHub Changelog: GitHub CLI: Opt in to expiring, auto-refreshing OAuth tokens
- GitHub Docs: Authenticating with GitHub CLI
- GitHub Docs: Authorizing OAuth apps - Expiring access tokens
- cli/cli PR #14452: short-lived login, refresh, and git credential path
- cli/cli PR #14798: hint at --expiring when no preference is set