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.

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 credentialAi quản lý vòng đời?Dùng ở đâu?Điểm cần nhớ
Credential mặc định hiện tại của ghNgười dùng thu hồi khi cầnMáy developer tương tácKhông tự hết hạn; opt in chưa làm thay đổi sign-in cũ
Expiring credentialgh refresh khi cầnCLI tương tác và Git HTTPS có helper phù hợpConsumer bên ngoài phải lấy giá trị mới, không cache token cũ
GH_TOKEN, PAT hoặc GITHUB_TOKENMôi trường cung cấp tokenScript, CI và GitHub Actionsgh 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.

Sơ đồ GitHub CLI, credential helper và GitHub host trong luồng refresh access token
Credential nên đi qua gh và helper đúng lúc; token đã copy sang cache riêng sẽ không tự quay về gh để refresh.

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.

Bảng audit ba nhóm consumer gồm CLI tương tác, Git HTTPS và script CI
CLI, Git HTTPS và automation cần ba quyết định credential khác nhau trước khi opt in.

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.