larksuite / larksuite/cli

Device flow grants offline_access in scope but returns no refresh_token (re-auth required every ~2h)

Open
#2,049 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug domain/auth
Dominant language
Go
Stars
17.3k
Forks
1.4k
Avg merge
2d 4h
Merged PRs (30d)
105

Description

Describe the bug

Every device-flow login succeeds and stores an access token whose granted scope includes offline_access, but the token response never contains a refresh_token. The CLI prints [WARN] device-flow: no refresh_token in response, sets refreshExpiresAt == expiresAt, and silent renewal is therefore impossible — the user has to re-scan a QR code every ~2 hours.

Environment

  • lark-cli 1.0.76 (npm @larksuite/cli), macOS arm64 (Darwin 25.5.0)
  • brand: feishu
  • App: created via the CLI app registration flow (/oauth/v1/app/registration), app_id cli_a8cd685509bd1013
  • Developer console shows the offline_access permission (持续访问已授权的数据, user identity) as 已开通/enabled

What was ruled out before filing

  1. Missing scope in the request — the CLI always auto-appends offline_access to the device authorization scope (internal/auth/device_flow.go:72), and one of the logins below additionally passed an explicit --scope offline_access.
  2. Console permission not enabled — the console shows offline_access as enabled for user identity.
  3. Server not granting the scope — after each login, auth status shows the stored scope ending with offline_access. The stored scope is taken from the token response's scope field (cmd/auth/login.go stores result.Token.Scope), so the server itself reports offline_access as granted.
  4. General device-flow limitation — other users on the same flow clearly receive ~7-day refresh tokens (see the timelines in #738), so this looks app- or tenant-specific.

Docs say refresh_token "仅在请求成功且用户授予 offline_access 权限时返回" — both conditions appear satisfied here, yet the field is absent.

Sanitized log lines (all three token exchanges today)

[lark-cli] auth-response: time=2026-07-24T02:38:26-07:00 path=/open-apis/authen/v2/oauth/token status=200 x-tt-logid=202607241738257A6928EAA624D4DFABFF
[lark-cli] auth-response: time=2026-07-24T17:44:06+08:00 path=/open-apis/authen/v2/oauth/token status=200 x-tt-logid=20260724174405F0388D052CB779DD0C0C
[lark-cli] auth-response: time=2026-07-24T02:51:34-07:00 path=/open-apis/authen/v2/oauth/token status=200 x-tt-logid=20260724175134296A2A4D555E7EDB9237

Each of these logins printed:

[lark-cli] device-flow: token response received
[lark-cli] [WARN] device-flow: no refresh_token in response

and resulted in refreshExpiresAt exactly equal to expiresAt (grantedAt + 2h).

Questions

  1. Under what server-side conditions does /open-apis/authen/v2/oauth/token (grant_type urn:ietf:params:oauth:grant-type:device_code) omit refresh_token while still including offline_access in the returned scope?
  2. Does enabling offline_access in the console require publishing a new app version before the token endpoint starts issuing refresh tokens? If so, could the console/docs/CLI surface this more clearly?
  3. Could this be specific to apps created through the CLI app-registration flow?

Happy to provide the full ~/.lark-cli/logs/auth-*.log privately if useful.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with internal/auth/device_flow.go:72 and cmd/auth/login.go to trace the requested and returned scopes, refresh_token handling, and refreshExpiresAt calculation. Compare the device-flow behavior with the timelines in #738 and inspect the sanitized token responses. Done means the server-side condition or app-registration/configuration cause is identified and the required behavior or documentation change is specified.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, authentication, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.