Token không hết hạn rất tiện cho đến ngày nó lọt vào một file log, clipboard hoặc máy cũ chưa kịp thu hồi. GitHub CLI 2.103.0 đưa ra một lựa chọn cân bằng hơn: access token có hạn, đi kèm refresh token xoay vòng và được gh làm mới tự động khi cần.
Tính năng này đang ở public preview và vẫn là opt-in. Điểm đáng quan tâm không chỉ là thêm --expiring vào lúc đăng nhập, mà là rà lại Git HTTPS, script và extension nào đang lấy token ra khỏi gh. Nếu một tiến trình copy token một lần rồi dùng nhiều giờ, tới công chuyện khi giá trị đó hết hạn.
Mục lục [Hiển thị]
Public preview thay đổi điều gì?
Theo thông báo GitHub ngày 21/10/2026, từ GitHub CLI 2.103.0 người dùng có thể yêu cầu credential hết hạn và tự refresh. gh giữ access token ngắn hạn cùng rotating refresh token, rồi đổi sang access token mới khi lệnh cần xác thực. Người dùng vẫn đăng nhập liên tục, nhưng một access token bị lộ chỉ còn hữu dụng trong khoảng thời gian giới hạn thay vì tồn tại vô thời hạn.
Đây chưa phải mặc định. Sign-in hiện có, token nhập thủ công và lần đăng nhập không opt in vẫn giữ hành vi cũ. GitHub nói lựa chọn hết hạn sẽ trở thành mặc định cho sign-in mới trong tương lai, nhưng không công bố mốc cụ thể; vì vậy bài này không đoán ngày chuyển đổi.
| Loại credential | Ai quản lý vòng đời? | Dùng ở đâu? | Điểm cần nhớ |
|---|---|---|---|
Credential mặc định hiện tại của gh | Người dùng thu hồi khi cần | Máy developer tương tác | Không tự hết hạn; opt in chưa làm thay đổi sign-in cũ |
| Expiring credential | gh refresh khi cần | CLI tương tác và Git HTTPS có helper phù hợp | Consumer bên ngoài phải lấy giá trị mới, không cache token cũ |
GH_TOKEN, PAT hoặc GITHUB_TOKEN | Môi trường cung cấp token | Script, CI và GitHub Actions | gh không tự refresh token được truyền qua biến môi trường |
Máy kiểm tra của bài dùng Windows x64, Git 2.54.0 và binary từ release GitHub CLI 2.103.0. SHA-256 của file ZIP khớp checksum nhà phát hành. Help của auth login và auth refresh đều có cờ --expiring; không đăng nhập tài khoản, không yêu cầu device code và không đọc token thật.
Opt in trong cấu hình cô lập trước
Một lần đăng nhập mới có thể yêu cầu credential hết hạn bằng lệnh sau:
gh auth login --expiring
Nếu muốn ưu tiên kiểu credential này cho các lần sign-in sau, bật config:
gh config set expiring_credentials enabled
gh config get expiring_credentials
Để không làm bẩn cấu hình GitHub CLI đang dùng, bài chạy lệnh với GH_CONFIG_DIR trỏ vào thư mục tạm. Kết quả đọc lại là enabled. Đây là kiểm tra help và config của binary, không phải bằng chứng refresh thật: không có sandbox account được cấp cho lượt kiểm tra này nên phần OAuth server được đối chiếu từ tài liệu chính thức.
Sau khi đăng nhập thật, dùng gh auth status để xem credential có refresh được hay không và access token hiện tại hết hạn khi nào. Mặc định lệnh này che token. Không thêm --show-token vào screenshot, ticket hoặc log chỉ để chứng minh cấu hình đã chạy.

Host mới là bên quyết định có cấp expiring credential hay không. GitHub.com và GitHub Enterprise Cloud hỗ trợ; GitHub Enterprise Server bắt đầu từ 3.23. Nếu host chưa hỗ trợ, gh rơi về token không hết hạn và hiển thị thông báo, nên không được coi việc đã gõ --expiring là bằng chứng loại credential thực tế.
Git HTTPS cần để gh làm credential helper
Khi Git dùng HTTPS, access token có thể đi qua một credential helper khác. Nếu helper đó cache giá trị quá thời hạn, gh không có cơ hội refresh trước lần git fetch hoặc git push kế tiếp. GitHub khuyến nghị để gh làm helper:
gh auth setup-git --hostname github.com
Đồng thời nên dùng Git 2.46 trở lên. Từ phiên bản này, GitHub CLI có thể đánh dấu credential hết hạn để helper không cache quá lifetime. Git cũ hơn vẫn chạy, nhưng gh sẽ hiện khuyến nghị nâng cấp một lần.
Không nên chạy setup-git mù quáng trên máy automation hoặc máy đang có helper doanh nghiệp. Trước khi đổi, chụp trạng thái theo đúng scope và không in credential:
git --version
git config --show-origin --get-all credential.helper
gh auth status --hostname github.com
Bài chỉ kiểm tra phiên bản Git 2.54.0; không sửa helper hiện tại vì máy chạy automation không phải sandbox đăng nhập. Với một máy thử riêng, hãy ghi lại origin của config trước và sau, rồi xác nhận Git HTTPS vẫn pull được mà không xuất hiện token trong terminal hoặc artifact.
Script nào dễ hỏng khi token bắt đầu hết hạn?
Anti-pattern rõ nhất là gọi gh auth token một lần lúc tiến trình khởi động, lưu vào biến toàn cục hoặc file, rồi tái sử dụng hàng giờ. Access token ban đầu có thể hết hạn trong khi gh vẫn có refresh token hợp lệ. Nhưng vì script không quay lại gh, nó chỉ giữ một giá trị cũ.
# Không nên: snapshot token một lần rồi dùng suốt tiến trình dài
$token = gh auth token
while ($true) {
Invoke-RestMethod -Headers @{ Authorization = "Bearer $token" } ...
}
Ưu tiên đầu tiên là để gh thực hiện request, chẳng hạn dùng gh api. Nếu một HTTP client khác thực sự cần token, lấy sát lúc request, giữ trong bộ nhớ ngắn và không echo hoặc ghi file:
function Invoke-GitHubRequest {
$token = gh auth token
try {
Invoke-RestMethod `
-Headers @{ Authorization = "Bearer $token" } `
-Uri 'https://api.github.com/user'
}
finally {
Remove-Variable token -ErrorAction SilentlyContinue
}
}
Fixture không credential của bài so sánh vị trí gọi provider: kiểu cache-once gọi provider một lần cho hai request, còn kiểu fresh-per-request gọi hai lần và nhận hai giá trị synthetic khác nhau. Fixture chỉ chứng minh control flow, không mô phỏng thời hạn OAuth và không log giá trị token.
Extension cũng cần audit tương tự. Nếu nó gọi gh auth token rồi tự nhét token vào config của một tool khác, tool đó không biết cách dùng refresh token. Tài liệu xác thực GitHub CLI nói rõ: gọi lại gh auth token mỗi khi cần sẽ cho phép gh refresh access token trước khi in giá trị hợp lệ.
Đừng mang credential tương tác vào CI
Expiring OAuth credential phù hợp nhất cho phiên làm việc tương tác trên máy developer. Automation nên nhận credential đúng loại của môi trường. Trong GitHub Actions, truyền token do workflow cấp qua GH_TOKEN; với môi trường khác, cân nhắc GitHub App hoặc PAT có phạm vi và vòng đời được quản lý riêng.
steps:
- name: Liệt kê issue đang mở
env:
GH_TOKEN: ${{ github.token }}
run: gh issue list --repo ${{ github.repository }}
GH_TOKEN và GITHUB_TOKEN không được gh tự refresh. Điều đó không phải lỗi: vòng đời thuộc về runner hoặc secret manager. Đừng copy rotating refresh token của một tài khoản cá nhân sang CI để “tự động hóa” cơ chế dành cho sign-in tương tác.
Nếu pipeline đang phụ thuộc vào tool, version và credential cố định, hãy áp dụng cùng tư duy với việc pin dependency trong GitHub Actions: biết rõ phiên bản nào chạy, credential đến từ đâu, ai xoay vòng và đường rollback là gì. Chỉ thêm flag mà không rà consumer thì chưa phải migration.
Refresh scope và rollback có một chỗ dễ nhầm
gh auth refresh thường được dùng để thêm hoặc bớt scope, khác với lần refresh access token tự động ở nền. Manual gh auth refresh của bản hiện tại ghi rằng nếu không truyền cờ, lệnh giữ loại credential đang có; có thể dùng --expiring để ưu tiên expiring credential hoặc --expiring=false để ưu tiên token không hết hạn. Host vẫn là bên quyết định cuối cùng.
gh auth refresh --expiring --scopes read:project
Sau khi đổi scope, chạy lại gh auth status và một lệnh API vô hại, nhưng không bật hiển thị token. Nếu một script dài chưa thể bỏ cache, rollback hợp lý là tạm hoãn opt in cho workflow đó hoặc cô lập nó với credential automation riêng; không cần tắt expiring credential trên mọi máy.

Ba nhóm cần quyết định riêng: CLI tương tác có thể opt in sớm; Git HTTPS cần kiểm helper và Git ≥2.46; script/CI phải bỏ giả định “token đọc một lần sẽ sống mãi”. Cách audit này gần với việc kiểm soát workflow GitHub Actions đáng ngờ: tìm ranh giới tin cậy và đường đi của credential trước khi bật thêm cơ chế.
Có nên bật ngay không?
Nếu gh chủ yếu chạy tương tác trên máy cá nhân, Git đã đủ mới và Git HTTPS đi qua helper của gh, public preview này đáng thử trên một account hoặc host ít rủi ro trước. Lợi ích là access token bị lộ có cửa sổ sử dụng ngắn hơn mà thao tác hằng ngày gần như không đổi.
Nếu hệ thống có extension cũ, daemon sống lâu hoặc script copy token sang nơi khác, hãy audit các consumer đó trước. Mốc hoàn thành không phải là lệnh login chạy thành công, mà là gh auth status xác nhận đúng loại credential, Git HTTPS vẫn hoạt động và không còn tiến trình nào giữ snapshot token quá lâu.
Nói ngắn gọn: bật --expiring rất dễ; phần đáng làm là trả quyền quản lý vòng đời token về đúng chỗ. Khi CLI, Git helper và automation mỗi bên dùng credential phù hợp, cơ chế refresh mới giảm rủi ro mà không tạo ra những lỗi 401 bí ẩn vài giờ sau.