Summary
GetStoredToken currently converts every credential-storage or decoding error
into a nil token. As a result, a stored user credential can be reported as
missing when the actual problem is that the process cannot access the macOS
Keychain-backed master key, decryption failed, or the stored JSON is malformed.
In a headless/background process this surfaced as:
lark-cli auth status --json --verify reporting the user identity as
missing
- user-authenticated commands failing with
token_missing
The encrypted credential file still existed and was valid. The credential was
not absent; its master key was unavailable to that process.
Environment
- macOS 12.7.6, Intel x64
- Observed with
@larksuite/cli 1.0.70
- The relevant implementation is unchanged in the current v1.0.71 release
- Reproduced in fresh non-GUI/background processes, including a
launchd
process and agent/automation runners
Reproduction context
These steps describe the original failure. The affected machine has since
applied the supported workaround below and was not reverted solely to reproduce
the failure again.
-
Log in interactively using the default macOS Keychain-backed credential
storage.
-
Confirm that lark-cli auth status --verify reports the user as ready.
-
Confirm that the encrypted credential file exists under
~/Library/Application Support/lark-cli/ with restrictive permissions.
-
Start a fresh headless/background process in a context that cannot access
the macOS Keychain.
-
Run:
lark-cli auth status --json --verify
or another command requiring user authentication.
Actual behavior
The CLI reports the stored user credential as missing and downstream commands
return token_missing.
Expected behavior
The CLI should distinguish at least these cases:
- No stored credential exists
- macOS Keychain is unavailable or access is denied
- Credential decryption fails
- Stored credential JSON is malformed or corrupted
Only the first case should be reported as missing.
Root cause
In v1.0.71, GetStoredToken
returns nil for both any keychain.Get error and any json.Unmarshal error:
jsonStr, err := keychain.Get(keychain.LarkCliService, accountKey(appId, userOpenId))
if err != nil || jsonStr == "" {
return nil
}
var token StoredUAToken
if err := json.Unmarshal([]byte(jsonStr), &token); err != nil {
return nil
}
This collapses not-found, operational, decryption, and corruption errors into
the same downstream missing result.
Issue #1038 and PR #1085 already cover the underlying macOS automation/Keychain
compatibility problem and provide config keychain-downgrade. This issue is
specifically about preserving and reporting the real storage error instead of
misclassifying it as a missing credential.
Suggested fix
-
Return a distinct not-found result only when the credential truly does not
exist.
-
Propagate or classify Keychain, decryption, and JSON-decoding errors.
-
Do not map non-missing storage errors to user.status=missing or
token_missing.
-
Include the existing recovery hint for affected headless environments:
lark-cli config keychain-downgrade
-
Never include tokens, decrypted credential content, or key material in logs
or error messages.
Suggested tests
Add automated coverage for:
- Credential file absent: reports
missing
- Keychain retrieval denied/unavailable: reports a Keychain/storage error
- Decryption failure: reports a decryption/corruption error
- Malformed stored JSON: reports a decoding/corruption error
- Headless/background execution with the supported file-backed master-key mode
- Verification that errors and logs never expose token or key material
Workaround verified
Running the supported command below moves master-key access to the file-backed
mode:
lark-cli config keychain-downgrade
After applying it, authentication succeeded from multiple fresh background
processes and a launchd process, and user-authenticated API commands worked
without another OAuth login.
Summary
GetStoredTokencurrently converts every credential-storage or decoding errorinto a
niltoken. As a result, a stored user credential can be reported asmissingwhen the actual problem is that the process cannot access the macOSKeychain-backed master key, decryption failed, or the stored JSON is malformed.
In a headless/background process this surfaced as:
lark-cli auth status --json --verifyreporting the user identity asmissingtoken_missingThe encrypted credential file still existed and was valid. The credential was
not absent; its master key was unavailable to that process.
Environment
@larksuite/cli1.0.70launchdprocess and agent/automation runners
Reproduction context
These steps describe the original failure. The affected machine has since
applied the supported workaround below and was not reverted solely to reproduce
the failure again.
Log in interactively using the default macOS Keychain-backed credential
storage.
Confirm that
lark-cli auth status --verifyreports the user as ready.Confirm that the encrypted credential file exists under
~/Library/Application Support/lark-cli/with restrictive permissions.Start a fresh headless/background process in a context that cannot access
the macOS Keychain.
Run:
or another command requiring user authentication.
Actual behavior
The CLI reports the stored user credential as missing and downstream commands
return
token_missing.Expected behavior
The CLI should distinguish at least these cases:
Only the first case should be reported as
missing.Root cause
In v1.0.71,
GetStoredTokenreturns
nilfor both anykeychain.Geterror and anyjson.Unmarshalerror:This collapses not-found, operational, decryption, and corruption errors into
the same downstream
missingresult.Issue #1038 and PR #1085 already cover the underlying macOS automation/Keychain
compatibility problem and provide
config keychain-downgrade. This issue isspecifically about preserving and reporting the real storage error instead of
misclassifying it as a missing credential.
Suggested fix
Return a distinct not-found result only when the credential truly does not
exist.
Propagate or classify Keychain, decryption, and JSON-decoding errors.
Do not map non-missing storage errors to
user.status=missingortoken_missing.Include the existing recovery hint for affected headless environments:
Never include tokens, decrypted credential content, or key material in logs
or error messages.
Suggested tests
Add automated coverage for:
missingWorkaround verified
Running the supported command below moves master-key access to the file-backed
mode:
After applying it, authentication succeeded from multiple fresh background
processes and a
launchdprocess, and user-authenticated API commands workedwithout another OAuth login.