mirror of
https://github.com/larksuite/cli.git
synced 2026-07-06 00:06:28 +08:00
Compare commits
1 Commits
feat/batch
...
agent/task
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f0d77c653 |
@@ -65,6 +65,14 @@ Both notices recommend the same fix command: `lark-cli update`. The skills notic
|
||||
| `internal/vfs/` | Filesystem abstraction (use `vfs.*` instead of `os.*`) |
|
||||
| `internal/validate/path.go` | Path safety validation |
|
||||
|
||||
## Auth / TAT operator notes
|
||||
|
||||
- The current TAT path is documented inline in `internal/credential/`, `cmd/config/init_probe.go`, `cmd/auth/status.go`, and `internal/identitydiag/diagnostics.go`. Historical materials that talk about `SEC_AUTH` or `cmd/sec/*` should be treated as drift, not as current-tree entry points.
|
||||
- `config init` only performs a best-effort post-save probe. A typed auth error means the credentials were deterministically rejected; transport / timeout / parse failures are intentionally treated as ambiguous noise and do not prove the config is bad.
|
||||
- `auth status --verify` and runtime bot commands share the same credential chain. A successful bot verify confirms the current token source can call `/open-apis/bot/v3/info`, but it does not prove every downstream bot scope or API path will work.
|
||||
- Historical reports still matter: sandboxed runners may fail to read OS keychain state, and users can complete browser authorization while local state remains missing or stale. Treat those as environment / local-state risks to inspect, not proof that the remote authorization page never succeeded.
|
||||
- The notes above are derived from current mainline code plus historical reports. They are intentionally not a live verification guarantee; real token usability still depends on valid app config, local state, and network reachability.
|
||||
|
||||
## Who Uses This CLI
|
||||
|
||||
This CLI's primary consumers include AI agents (Claude Code, Cursor, Gemini CLI). Your code is read by machines — error messages, output format, and flag design all directly affect agent success rates.
|
||||
|
||||
@@ -40,6 +40,10 @@ func NewCmdAuthStatus(f *cmdutil.Factory, runF func(*StatusOptions) error) *cobr
|
||||
return cmd
|
||||
}
|
||||
|
||||
// authStatusRun reports the configured identities. When --verify is enabled,
|
||||
// the bot branch does not mint a token independently — it reuses the shared
|
||||
// credential resolution path, so `auth status --verify` exercises the same TAT
|
||||
// source selection logic as normal bot API calls.
|
||||
func authStatusRun(opts *StatusOptions) error {
|
||||
f := opts.Factory
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ const probeTimeout = 3 * time.Second
|
||||
// so that valid configurations and transient/upstream noise never block the
|
||||
// command.
|
||||
//
|
||||
// This is one of the repository's three main TAT entry points: config init
|
||||
// validates freshly-saved app credentials here, while runtime bot calls and
|
||||
// `auth status --verify` go through credential.ResolveToken.
|
||||
//
|
||||
// The function performs up to two HTTP calls in series, bounded by
|
||||
// probeTimeout:
|
||||
//
|
||||
|
||||
@@ -322,7 +322,10 @@ func sanitizeOutputDir(dir string) (string, error) {
|
||||
return safe, nil
|
||||
}
|
||||
|
||||
// resolveTenantToken fetches the app's tenant access token.
|
||||
// resolveTenantToken fetches the app's tenant access token for event consume.
|
||||
// This is a concrete runtime consumer of the shared bot-token path: event
|
||||
// consume does not own a separate exchange flow, it asks the credential layer
|
||||
// for the same TAT that other bot-mode commands use.
|
||||
func resolveTenantToken(ctx context.Context, f *cmdutil.Factory, appID string) (string, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
|
||||
3
extension/credential/env/env.go
vendored
3
extension/credential/env/env.go
vendored
@@ -94,6 +94,9 @@ func (p *Provider) ResolveAccount(ctx context.Context) (*credential.Account, err
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
// ResolveToken returns a token directly from environment variables. For bot
|
||||
// identity this is a straight TAT override that bypasses the built-in
|
||||
// app_id/app_secret mint path entirely.
|
||||
func (p *Provider) ResolveToken(ctx context.Context, req credential.TokenSpec) (*credential.Token, error) {
|
||||
var envKey string
|
||||
switch req.Type {
|
||||
|
||||
@@ -103,6 +103,8 @@ func (p *Provider) ResolveAccount(ctx context.Context) (*credential.Account, err
|
||||
// ResolveToken returns a sentinel token whose value encodes the token type.
|
||||
// The transport interceptor reads this sentinel to determine the identity
|
||||
// (user vs bot), strips it, and the sidecar injects the real token.
|
||||
// In other words, sidecar mode proxies the normal UAT/TAT contract; it does
|
||||
// not introduce a new open-platform token-exchange protocol inside this repo.
|
||||
// Returns nil, nil when sidecar mode is not active.
|
||||
func (p *Provider) ResolveToken(ctx context.Context, req credential.TokenSpec) (*credential.Token, error) {
|
||||
if os.Getenv(envvars.CliAuthProxy) == "" {
|
||||
|
||||
@@ -64,6 +64,9 @@ type Interceptor struct {
|
||||
// Supports two auth patterns:
|
||||
// - Standard OpenAPI: Authorization: Bearer <sentinel>
|
||||
// - MCP protocol: X-Lark-MCP-UAT/TAT: <sentinel>
|
||||
//
|
||||
// For bot traffic the sentinel stands in for TAT only long enough for the
|
||||
// proxy hop; the real tenant access token is injected by the sidecar.
|
||||
func (i *Interceptor) PreRoundTrip(req *http.Request) func(resp *http.Response, err error) {
|
||||
identity, authHeader := detectSentinel(req)
|
||||
if identity == "" {
|
||||
|
||||
@@ -301,6 +301,16 @@ func (p *CredentialProvider) doResolveIdentityHint(ctx context.Context) (*Identi
|
||||
}
|
||||
|
||||
// ResolveToken resolves an access token.
|
||||
//
|
||||
// For bot identity this is the runtime TAT entry point used across the CLI:
|
||||
// 1. an active credential source (for example env or sidecar) gets first
|
||||
// chance to return a token;
|
||||
// 2. extension providers are tried next;
|
||||
// 3. the built-in default provider falls back to FetchTAT(app_id, app_secret)
|
||||
// and caches that tenant access token with sync.Once for the process.
|
||||
//
|
||||
// The env provider may supply a real TAT directly; the sidecar provider returns
|
||||
// a sentinel token that the transport layer rewrites into a proxied request.
|
||||
func (p *CredentialProvider) ResolveToken(ctx context.Context, req TokenSpec) (*TokenResult, error) {
|
||||
source, err := p.selectedCredentialSource(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -147,6 +147,8 @@ func (p *DefaultTokenProvider) resolveUAT(ctx context.Context) (*TokenResult, er
|
||||
}
|
||||
|
||||
// resolveTAT resolves a tenant access token. Result is cached after first call.
|
||||
// This is the built-in runtime path behind bot token resolution when no
|
||||
// external credential source overrides it.
|
||||
// NOTE: Uses sync.Once — only the context from the first call is used.
|
||||
func (p *DefaultTokenProvider) resolveTAT(ctx context.Context) (*TokenResult, error) {
|
||||
p.tatOnce.Do(func() {
|
||||
|
||||
@@ -18,6 +18,11 @@ import (
|
||||
// that already hold plaintext credentials (e.g. the post-`config init` probe)
|
||||
// can validate them without a second keychain round-trip.
|
||||
//
|
||||
// This is the built-in TAT mint path for the current repository:
|
||||
// app_id + app_secret -> /open-apis/auth/v3/tenant_access_token/internal.
|
||||
// It is reused by both the post-config probe and the default runtime bot-token
|
||||
// resolver. The tree does not contain a separate app_ticket-based TAT exchange.
|
||||
//
|
||||
// A non-zero TAT response code means the server inspected the payload and
|
||||
// rejected the credentials; FetchTAT returns the canonical typed error from
|
||||
// classifyTATResponseCode — the SAME classification doResolveTAT (and thus
|
||||
|
||||
@@ -97,6 +97,10 @@ type AccountProvider interface {
|
||||
}
|
||||
|
||||
// TokenType distinguishes UAT from TAT.
|
||||
// In this repository, TAT always means "tenant access token" for bot identity:
|
||||
// either minted from app_id + app_secret by the built-in provider, supplied
|
||||
// directly by an extension provider, or proxied through authsidecar. There is
|
||||
// no app_ticket -> tenant_access_token exchange implementation in this tree.
|
||||
// Uses string constants matching extension/credential.TokenType for zero-cost conversion.
|
||||
type TokenType string
|
||||
|
||||
|
||||
@@ -217,6 +217,10 @@ func diagnoseUser(ctx context.Context, f *cmdutil.Factory, cfg *core.CliConfig,
|
||||
return id
|
||||
}
|
||||
|
||||
// resolveBotToken is the bot-verification consumer of the shared credential
|
||||
// chain. It intentionally reuses ResolveToken(core.AsBot, appID) so auth
|
||||
// diagnostics observe the same env-provider, sidecar, strict-mode, and
|
||||
// built-in FetchTAT behavior as normal runtime bot commands.
|
||||
func resolveBotToken(ctx context.Context, f *cmdutil.Factory, cfg *core.CliConfig) (string, error) {
|
||||
if f == nil || f.Credential == nil {
|
||||
return "", &credential.TokenUnavailableError{Type: credential.TokenTypeTAT}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// compiled in under that tag; deploying the plain build into an environment
|
||||
// that expects sidecar isolation would silently fall back to direct env
|
||||
// credential use — exactly the failure mode the feature is meant to prevent.
|
||||
// Without the authsidecar build tag, the binary cannot safely proxy either UAT
|
||||
// or TAT through the sidecar contract at all.
|
||||
//
|
||||
// When LARKSUITE_CLI_AUTH_PROXY is set, we refuse to run rather than ignore
|
||||
// the variable. The operator either rebuilt without realizing (wrong
|
||||
|
||||
Reference in New Issue
Block a user