Documentation
¶
Overview ¶
identity.go manages agent instance identification for tracking.
Each agent installation gets a unique agentId (UUID v4) that persists across version upgrades but regenerates on reinstall. This identity is transparently injected into MCP HTTP headers for gateway-side data collection.
Index ¶
- Constants
- Variables
- func ClientID() string
- func ClientSecret() string
- func DeleteAppConfig(configDir string) error
- func DeleteAppTokenData(clientID string) error
- func DeleteClientSecret(clientID string) error
- func DeleteSecureData(configDir string) error
- func DeleteTokenData(configDir string) error
- func DeleteTokenDataKeychain() error
- func DeleteTokenMarker(configDir string) error
- func EnsureMigration(configDir string, logger *slog.Logger)
- func ExportPortableAuthBundle(configDir string, w io.Writer) error
- func FetchAppToken(ctx context.Context, appKey, appSecret string) (token string, expiresIn int64, err error)
- func FetchClientIDFromMCP(ctx context.Context) (string, error)
- func GetAppConfigPath(configDir string) string
- func GetDeveloperSettingsURL() string
- func GetMCPBaseURL() string
- func GetRefreshTokenURL() string
- func GetRevokeTokenURL() string
- func GetTerminalBaseURL() string
- func GetUserAccessTokenURL() string
- func HasAppConfig(configDir string) bool
- func HasValidClientSecret() bool
- func HostOwnsPATFlow() bool
- func IsClientIDFromMCP() bool
- func IsMigrationDone() bool
- func LoadClientSecret(clientID string) string
- func MarkAccessTokenStale(configDir string) error
- func ParseDeviceFlowStatus(rawStatus string, success bool) string
- func PortableAuthSourceReady() bool
- func PortableAuthTargetPopulated(configDir string) bool
- func PortableExportSupported() bool
- func RemoveSecretStore(input SecretInput)
- func ResolveAppCredentials(configDir string) (clientID, clientSecret string)
- func ResolveSecret(input SecretInput) (string, error)
- func RevokeTokenRemote(ctx context.Context) error
- func SaveAppConfig(configDir string, config *AppConfig) error
- func SaveAppTokenData(data *AppTokenData) error
- func SaveClientSecret(clientID, clientSecret string) error
- func SaveSecureTokenData(configDir string, data *TokenData) error
- func SaveTokenData(configDir string, data *TokenData) error
- func SaveTokenDataKeychain(data *TokenData) error
- func SecureDataExists(configDir string) bool
- func SetClientID(id string)
- func SetClientIDFromMCP(id string)
- func SetClientSecret(secret string)
- func TokenDataExistsKeychain() bool
- func WriteTokenMarker(configDir string) error
- type AppConfig
- type AppTokenData
- type AppTokenProvider
- type CLIAuthResult
- type CLIAuthStatus
- type ClientIDResponse
- type DeviceAuthResponse
- type DeviceFlowProvider
- type DevicePollData
- type DevicePollResponse
- type DeviceTokenResponse
- type DualLock
- type Identity
- type Manager
- func (m *Manager) DeleteToken() error
- func (m *Manager) GetMCPURL() (string, error)
- func (m *Manager) GetToken() (string, string, error)
- func (m *Manager) IsAuthenticated() bool
- func (m *Manager) SaveMCPURL(url string) error
- func (m *Manager) SaveToken(token string) error
- func (m *Manager) Status() (authenticated bool, source string, maskedToken string)
- type OAuthProvider
- func (p *OAuthProvider) CheckCLIAuthEnabled(ctx context.Context, accessToken string) (*CLIAuthStatus, error)
- func (p *OAuthProvider) ExchangeAuthCode(ctx context.Context, authCode, uid string) (*TokenData, error)
- func (p *OAuthProvider) GetAccessToken(ctx context.Context) (string, error)
- func (p *OAuthProvider) Login(ctx context.Context, force bool) (*TokenData, error)
- func (p *OAuthProvider) Logout() error
- func (p *OAuthProvider) Status() (*TokenData, error)
- type PortableImportReport
- type SecretInput
- type SecretRef
- type SendApplyResponse
- type SuperAdmin
- type SuperAdminResponse
- type TokenData
- type TokenMarker
Constants ¶
const ( StatusPending = "PENDING" StatusApproved = "APPROVED" StatusRejected = "REJECTED" StatusExpired = "EXPIRED" StatusCancelled = "CANCELLED" )
Device flow authorization status constants. Shared across device_flow.go and pat_auth_retry.go to avoid maintaining string literals in multiple places.
const ( // AuthorizeURL is the DingTalk OAuth authorization page. AuthorizeURL = "https://login.dingtalk.com/oauth2/auth" // UserAccessTokenURL exchanges an authorization code for user tokens. UserAccessTokenURL = "https://api.dingtalk.com/v1.0/oauth2/userAccessToken" // UserInfoURL fetches the authenticated user's profile. UserInfoURL = "https://api.dingtalk.com/v1.0/contact/users/me" // DefaultClientID is the CLI's built-in OAuth client ID (DingTalk AppKey). // TODO: Replace <YOUR_CLIENT_ID> with your actual DingTalk AppKey before building. DefaultClientID = "<YOUR_CLIENT_ID>" // DefaultClientSecret is the CLI's built-in OAuth client secret (DingTalk AppSecret). // TODO: Replace <YOUR_CLIENT_SECRET> with your actual DingTalk AppSecret before building. DefaultClientSecret = "<YOUR_CLIENT_SECRET>" // CallbackPath is the localhost callback endpoint for OAuth redirect. CallbackPath = "/callback" // DefaultScopes are the OAuth scopes requested by the CLI. DefaultScopes = "openid corpid" // DefaultDeviceBaseURL is the login server base URL for device flow. DefaultDeviceBaseURL = "https://login.dingtalk.com" // DeviceCodePath requests a device_code and user_code. DeviceCodePath = "/oauth2/device/code.json" // DeviceTokenPath polls for authorization completion. DeviceTokenPath = "/oauth2/device/token.json" // DeviceGrantType is the grant_type value defined by RFC 8628. DeviceGrantType = "urn:ietf:params:oauth:grant-type:device_code" // Terminal API base URL for developer settings page. DefaultTerminalBaseURL = "https://open-dev.dingtalk.com" // DevicePollPath is the device flow polling path (used with MCP base URL). DevicePollPath = "/cli/oauth/device/poll" // DeveloperSettingsPath is the path to the organization developer settings page. DeveloperSettingsPath = "/fe/old#/developerSettings" LogoutURL = "https://login.dingtalk.com/oauth2/logout" LogoutContinueURL = "https://login.dingtalk.com" // MCP API endpoints for CLI authorization management. DefaultMCPBaseURL = config.DefaultMCPBaseURL CLIAuthEnabledPath = "/cli/cliAuthEnabled" SuperAdminPath = "/cli/superAdmin" SendCliAuthApplyPath = "/cli/sendCliAuthApply" ClientIDPath = "/cli/clientId" // MCP OAuth endpoints (used when clientId is fetched from MCP). MCPOAuthTokenPath = "/oauth2/getToken" MCPRefreshTokenPath = "/oauth2/refreshToken" MCPRevokeTokenPath = "/oauth2/revokeToken" // AppAccessTokenURL is the unified app-level access token endpoint. // POST with {"appKey":"X","appSecret":"X"} → {"accessToken":"...","expireIn":7200} AppAccessTokenURL = "https://api.dingtalk.com/v1.0/oauth2/accessToken" )
const ( // AgentCodeEnv is the sole per-spawn environment variable the host injects // to declare "this process is driven by a third-party Agent host, render // authorization UI yourselves". AgentCodeEnv = "DINGTALK_DWS_AGENTCODE" )
Variables ¶
var ErrTokenDecryption = errors.New("token decryption failed")
ErrTokenDecryption indicates that token decryption failed, typically due to a device mismatch or corrupted data file. Callers can check this with errors.Is to distinguish decryption failures from other I/O or parsing errors.
var ValidSecretSources = map[string]bool{ "file": true, "keychain": true, }
ValidSecretSources is the set of recognized SecretRef sources.
Functions ¶
func ClientID ¶
func ClientID() string
ClientID returns the OAuth client ID with priority: 1. Runtime override (CLI flag --client-id) 2. Persisted app config (from previous login) 3. Environment variable (DWS_CLIENT_ID) 4. Default hardcoded value (if not a placeholder) Returns empty string if no valid client ID is available. Note: MCP server fetch (priority 4 in the full flow) is handled in OAuthProvider.Login()
func ClientSecret ¶
func ClientSecret() string
ClientSecret returns the OAuth client secret with priority: 1. Runtime override (CLI flag --client-secret) 2. Persisted app config (from previous login, stored in keychain) 3. Environment variable (DWS_CLIENT_SECRET) 4. Default hardcoded value
func DeleteAppConfig ¶ added in v1.0.4
DeleteAppConfig removes the app configuration and associated keychain secrets.
func DeleteAppTokenData ¶ added in v1.0.18
DeleteAppTokenData removes AppTokenData from keychain for the given clientID.
func DeleteClientSecret ¶ added in v1.0.6
DeleteClientSecret removes the stored client secret for a specific client ID.
func DeleteSecureData ¶
DeleteSecureData removes .data file from configDir.
func DeleteTokenData ¶
DeleteTokenData removes token data. When an edition hook (DeleteToken) is registered, it delegates entirely to the hook; otherwise it falls back to keychain + legacy cleanup.
func DeleteTokenDataKeychain ¶
func DeleteTokenDataKeychain() error
DeleteTokenDataKeychain removes TokenData from the platform keychain.
func DeleteTokenMarker ¶ added in v1.0.9
DeleteTokenMarker removes the token.json marker file.
func EnsureMigration ¶
EnsureMigration performs one-time migration from legacy .data to keychain. This should be called early in the auth flow (e.g., during GetAccessToken). The migration is idempotent and thread-safe.
func ExportPortableAuthBundle ¶ added in v1.0.33
ExportPortableAuthBundle writes a portable auth bundle as tar.gz. It copies the encrypted keychain files plus the small config files needed to refresh tokens in another Linux sandbox.
func FetchAppToken ¶ added in v1.0.18
func FetchAppToken(ctx context.Context, appKey, appSecret string) (token string, expiresIn int64, err error)
FetchAppToken obtains an app-level access token from the unified endpoint:
POST https://api.dingtalk.com/v1.0/oauth2/accessToken
Body: {"appKey":"X","appSecret":"X"}
Response: {"accessToken":"xxx","expireIn":7200}
The same token works for both api.dingtalk.com and oapi.dingtalk.com.
func FetchClientIDFromMCP ¶ added in v1.0.5
FetchClientIDFromMCP fetches the CLI client ID from MCP server. This is used when no client ID is provided via flags, config, or env vars. It retries up to mcpRequestMaxRetries times on transient errors.
func GetAppConfigPath ¶ added in v1.0.4
GetAppConfigPath returns the path to the app config file for the currently-active edition. The filename is partitioned by edition so that two dws binaries from different editions sharing the same configDir (typically ~/.dws or DWS_CONFIG_DIR) cannot read or overwrite each other's credentials. Open-source stays on "app.json" for backwards compatibility; sibling editions land on "app-<edition>.json".
func GetDeveloperSettingsURL ¶ added in v1.0.11
func GetDeveloperSettingsURL() string
GetDeveloperSettingsURL returns the full URL to the organization developer settings page, derived from the terminal base URL.
func GetMCPBaseURL ¶ added in v1.0.5
func GetMCPBaseURL() string
GetMCPBaseURL returns the MCP base URL with priority: 1. ~/.dws/mcp_url file content (for pre-release environment) 2. Default value (https://mcp.dingtalk.com)
func GetRefreshTokenURL ¶ added in v1.0.5
func GetRefreshTokenURL() string
GetRefreshTokenURL returns the appropriate token refresh URL. Uses MCP endpoint when clientID is from MCP, otherwise uses direct DingTalk API.
func GetRevokeTokenURL ¶ added in v1.0.5
func GetRevokeTokenURL() string
GetRevokeTokenURL returns the token revocation URL (MCP only). Returns empty string if not using MCP mode.
func GetTerminalBaseURL ¶ added in v1.0.11
func GetTerminalBaseURL() string
GetTerminalBaseURL returns the terminal base URL with priority: 1. ~/.dws/terminal_url file content (for pre-release environment) 2. Default value (https://open-dev.dingtalk.com)
func GetUserAccessTokenURL ¶ added in v1.0.5
func GetUserAccessTokenURL() string
GetUserAccessTokenURL returns the appropriate token exchange URL. Uses MCP endpoint when clientID is from MCP, otherwise uses direct DingTalk API.
func HasAppConfig ¶ added in v1.0.4
HasAppConfig returns true if an app configuration file exists.
func HasValidClientSecret ¶ added in v1.0.5
func HasValidClientSecret() bool
HasValidClientSecret returns true if a valid client secret is available. A valid secret is one that is not a placeholder (e.g., <YOUR_CLIENT_SECRET>).
func HostOwnsPATFlow ¶ added in v1.0.18
func HostOwnsPATFlow() bool
HostOwnsPATFlow reports whether the current process is running under a third-party Agent host that will render the PAT authorization card itself. The sole trigger is AgentCodeEnv (DINGTALK_DWS_AGENTCODE) being non-empty. The CLI deliberately does not consult any other signal (DINGTALK_AGENT / DWS_CHANNEL / the wire claw-type header) for this decision so that server-side routing tags and the host-owned UI contract remain independent concerns.
func IsClientIDFromMCP ¶ added in v1.0.5
func IsClientIDFromMCP() bool
IsClientIDFromMCP returns true if the current clientID was fetched from MCP server.
func IsMigrationDone ¶
func IsMigrationDone() bool
IsMigrationDone returns true if migration has been attempted.
func LoadClientSecret ¶ added in v1.0.6
LoadClientSecret retrieves the stored client secret for a specific client ID. Returns empty string if not found.
func MarkAccessTokenStale ¶ added in v1.0.16
MarkAccessTokenStale loads the persisted TokenData, sets ExpiresAt to a past instant (preserving access_token and refresh_token), and writes it back. The next OAuthProvider.GetAccessToken call will see IsAccessTokenValid() == false and proceed to lockedRefresh, exchanging the refresh_token for a fresh access_token.
Use this only when the server has rejected the current access_token but the local expiry has not yet elapsed (zombie token scenario). It does not delete any token material and is safe to call concurrently — actual refresh is serialized by lockedRefresh's dual-layer locking.
Returns the original load error when there is no usable token on disk; a nil error when there is no access_token to invalidate (no-op).
func ParseDeviceFlowStatus ¶ added in v1.0.11
ParseDeviceFlowStatus normalizes a raw status string from the device flow poll response into a canonical status constant. When the server returns an empty status with success=false, it falls back to StatusExpired (server error / flow not found).
func PortableAuthSourceReady ¶ added in v1.0.33
func PortableAuthSourceReady() bool
PortableAuthSourceReady reports whether encrypted auth token exists for export.
func PortableAuthTargetPopulated ¶ added in v1.0.33
PortableAuthTargetPopulated reports whether local auth files would be overwritten by a portable import.
func PortableExportSupported ¶ added in v1.0.33
func PortableExportSupported() bool
PortableExportSupported reports whether the current platform can produce a bundle that includes the file-based DEK required for import elsewhere.
func RemoveSecretStore ¶ added in v1.0.4
func RemoveSecretStore(input SecretInput)
RemoveSecretStore cleans up keychain entries when an app is removed. Errors are intentionally ignored — cleanup is best-effort.
func ResolveAppCredentials ¶ added in v1.0.4
ResolveAppCredentials resolves the client ID and secret from the app config. Results are cached to avoid repeated keychain access. Returns empty strings if the config doesn't exist or resolution fails.
func ResolveSecret ¶ added in v1.0.4
func ResolveSecret(input SecretInput) (string, error)
ResolveSecret resolves a SecretInput to a plain string. SecretRef objects are resolved by source (file / keychain).
func RevokeTokenRemote ¶
RevokeTokenRemote calls the appropriate logout/revoke endpoint to invalidate the access token. Uses MCP revoke endpoint when clientID is from MCP, otherwise uses DingTalk logout. This should be called before deleting local token data. The function is best-effort: errors are returned but callers may choose to ignore them.
func SaveAppConfig ¶ added in v1.0.4
SaveAppConfig saves the app configuration to disk. If the client secret is a plain string, it will be stored in keychain and the config file will contain a reference to it.
func SaveAppTokenData ¶ added in v1.0.18
func SaveAppTokenData(data *AppTokenData) error
SaveAppTokenData persists AppTokenData to keychain, keyed by clientID.
func SaveClientSecret ¶ added in v1.0.6
SaveClientSecret stores the client secret for a specific client ID. This is called during login to snapshot the credentials used.
func SaveSecureTokenData ¶
SaveSecureTokenData encrypts and saves TokenData to .data file. The data is encrypted using AES-256-GCM with a key derived from the device MAC address. Uses atomic write (write .tmp then rename) to prevent corruption.
Concurrency: callers that involve token refresh MUST hold the business-level file lock (via acquireTokenLock) to prevent two processes from refreshing simultaneously. See OAuthProvider.lockedRefresh().
func SaveTokenData ¶
SaveTokenData persists TokenData. When an edition hook (SaveToken) is registered, it delegates entirely to the hook; otherwise it falls back to the default keychain-based storage.
func SaveTokenDataKeychain ¶
SaveTokenDataKeychain saves TokenData to the platform keychain. This is the new secure storage method using random master key.
func SecureDataExists ¶
SecureDataExists checks if the secure .data file exists in the given directory.
func SetClientID ¶
func SetClientID(id string)
SetClientID allows runtime override of the client ID (e.g., from CLI flags).
func SetClientIDFromMCP ¶ added in v1.0.5
func SetClientIDFromMCP(id string)
SetClientIDFromMCP sets the clientID fetched from MCP server and marks it as MCP-sourced.
func SetClientSecret ¶
func SetClientSecret(secret string)
SetClientSecret allows runtime override of the client secret (e.g., from CLI flags).
func TokenDataExistsKeychain ¶
func TokenDataExistsKeychain() bool
TokenDataExistsKeychain checks if token data exists in keychain.
func WriteTokenMarker ¶ added in v1.0.9
WriteTokenMarker writes a token.json marker containing only an updated_at timestamp. The host application uses this file's presence and mtime to decide whether it needs to trigger a new auth exchange.
Types ¶
type AppConfig ¶ added in v1.0.4
type AppConfig struct {
ClientID string `json:"clientId"`
ClientSecret SecretInput `json:"clientSecret"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
}
AppConfig represents the application credentials configuration. This is stored in the edition-specific app config file, with the client secret securely stored in keychain when present.
func GetCachedAppConfig ¶ added in v1.0.4
GetCachedAppConfig returns the cached app configuration. It loads from disk on first call and caches the result. Returns nil if no configuration exists or loading fails.
func LoadAppConfig ¶ added in v1.0.4
LoadAppConfig loads the app configuration from disk. Returns nil, nil if the config file does not exist.
func ReloadAppConfig ¶ added in v1.0.4
ReloadAppConfig forces a reload of the app configuration from disk. This should be called after SaveAppConfig to ensure the cache is updated.
type AppTokenData ¶ added in v1.0.18
type AppTokenData struct {
AccessToken string `json:"access_token,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
// Associated app credentials
ClientID string `json:"client_id"`
UpdatedAt time.Time `json:"updated_at"`
}
AppTokenData stores the app-level access token obtained from the unified POST /v1.0/oauth2/accessToken endpoint. It works for both new-style (api.dingtalk.com) and legacy (oapi.dingtalk.com) APIs — the auth method (header vs query param) is chosen by the caller based on the target host.
func LoadAppTokenData ¶ added in v1.0.18
func LoadAppTokenData(clientID string) (*AppTokenData, error)
LoadAppTokenData loads AppTokenData from keychain for the given clientID. Returns nil, nil if no data exists.
func (*AppTokenData) IsTokenValid ¶ added in v1.0.18
func (d *AppTokenData) IsTokenValid() bool
IsTokenValid returns true if the access token has not expired.
type AppTokenProvider ¶ added in v1.0.18
type AppTokenProvider struct {
ConfigDir string
AppKey string
AppSecret string
HTTPClient *http.Client // injectable for testing; nil uses default
}
AppTokenProvider manages app-level token acquisition, caching and auto-refresh.
type CLIAuthResult ¶ added in v1.0.11
type CLIAuthResult struct {
CLIAuthEnabled bool `json:"cliAuthEnabled"`
UserScope string `json:"userScope,omitempty"` // "all" | "specified" | "forbidden"
AllowedUsers []string `json:"allowedUsers,omitempty"` // staffId list when userScope="specified"
ChannelScope string `json:"channelScope,omitempty"` // "all" | "specified"
AllowedChannels []string `json:"allowedChannels,omitempty"` // channelCode list when channelScope="specified"
ChannelConfigEnabled bool `json:"channelConfigEnabled,omitempty"` // whether org has any channel restriction configured
}
CLIAuthResult holds the business data returned by /cli/cliAuthEnabled. The server computes cliAuthEnabled by considering the org switch, userScope, and channelScope together; the CLI uses it as-is.
type CLIAuthStatus ¶ added in v1.0.5
type CLIAuthStatus struct {
Success bool `json:"success"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMsg string `json:"errorMsg,omitempty"`
Result *CLIAuthResult `json:"result"`
}
CLIAuthStatus represents the response from /cli/cliAuthEnabled API.
type ClientIDResponse ¶ added in v1.0.5
type ClientIDResponse struct {
Success bool `json:"success"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMsg string `json:"errorMsg,omitempty"`
Result string `json:"result"`
}
ClientIDResponse represents the response from /cli/clientId API.
type DeviceAuthResponse ¶
type DeviceAuthResponse struct {
DeviceCode string `json:"deviceCode"`
UserCode string `json:"userCode"`
VerificationURI string `json:"verificationUri"`
VerificationURIComplete string `json:"verificationUriComplete"`
ExpiresIn int `json:"expiresIn"`
Interval int `json:"interval"`
FlowID string `json:"flowId"`
}
type DeviceFlowProvider ¶
func NewDeviceFlowProvider ¶
func NewDeviceFlowProvider(configDir string, logger *slog.Logger) *DeviceFlowProvider
func (*DeviceFlowProvider) Login ¶
func (p *DeviceFlowProvider) Login(ctx context.Context) (*TokenData, error)
func (*DeviceFlowProvider) SetBaseURL ¶
func (p *DeviceFlowProvider) SetBaseURL(baseURL string)
func (*DeviceFlowProvider) SetScope ¶ added in v1.0.11
func (p *DeviceFlowProvider) SetScope(scope string)
SetScope overrides the OAuth scope for the device flow.
func (*DeviceFlowProvider) SetTerminalBaseURL ¶ added in v1.0.11
func (p *DeviceFlowProvider) SetTerminalBaseURL(baseURL string)
SetTerminalBaseURL sets the terminal API base URL for device flow polling.
type DevicePollData ¶ added in v1.0.11
type DevicePollResponse ¶ added in v1.0.11
type DevicePollResponse struct {
Success bool `json:"success"`
Code string `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Data DevicePollData `json:"data"`
// Result is an alternate envelope some service versions return instead of
// (or alongside) Data. Always read poll fields via EffectiveData() rather
// than touching Data/Result directly.
Result DevicePollData `json:"result"`
}
DevicePollResponse represents the response from the terminal API poll endpoint.
func (DevicePollResponse) EffectiveData ¶ added in v1.0.18
func (r DevicePollResponse) EffectiveData() DevicePollData
EffectiveData normalizes terminal poll responses that may carry payload fields under either `data` or `result`.
Semantics are envelope-level rather than field-level: when Data includes a non-empty status, treat Data as the authoritative payload and return it unchanged; otherwise fall back to Result. This avoids mixing fields from two disagreeing envelopes into a Frankenstein result.
type DeviceTokenResponse ¶
type DualLock ¶
type DualLock struct {
Waited bool // true if we waited for another goroutine/process
// contains filtered or unexported fields
}
DualLock holds both process-level and file-level locks.
func AcquireDualLock ¶
AcquireDualLock acquires both process-level and file-level locks. This provides comprehensive protection against: 1. Multiple goroutines in the same process (sync.Map) 2. Multiple CLI processes (file lock)
The caller MUST call Release() when done.
type Identity ¶
type Identity struct {
AgentID string `json:"agentId"` // UUID v4, generated at install time
Source string `json:"source"` // data source, default "dws"
}
Identity holds the agent instance identification fields.
func EnsureExists ¶
EnsureExists loads existing identity or creates a new one if not present.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
func (*Manager) DeleteToken ¶
func (*Manager) IsAuthenticated ¶
func (*Manager) SaveMCPURL ¶
type OAuthProvider ¶
OAuthProvider handles the DingTalk OAuth 2.0 authorization code flow.
func NewOAuthProvider ¶
func NewOAuthProvider(configDir string, logger *slog.Logger) *OAuthProvider
NewOAuthProvider creates a new OAuth provider.
func (*OAuthProvider) CheckCLIAuthEnabled ¶ added in v1.0.5
func (p *OAuthProvider) CheckCLIAuthEnabled(ctx context.Context, accessToken string) (*CLIAuthStatus, error)
CheckCLIAuthEnabled checks if CLI authorization is enabled for the current corp. It retries up to mcpRequestMaxRetries times on transient errors to avoid false negatives caused by momentary network issues.
func (*OAuthProvider) ExchangeAuthCode ¶
func (p *OAuthProvider) ExchangeAuthCode(ctx context.Context, authCode, uid string) (*TokenData, error)
ExchangeAuthCode takes an AuthCode and an optional UserID provided by an external host, exchanges it for tokens, and persists them.
func (*OAuthProvider) GetAccessToken ¶
func (p *OAuthProvider) GetAccessToken(ctx context.Context) (string, error)
GetAccessToken returns a valid access token, auto-refreshing if needed. Uses a file lock with double-check pattern to prevent concurrent refresh from multiple CLI processes.
func (*OAuthProvider) Login ¶
Login performs authentication with smart degradation: 1. If force=false, try silent token refresh first (refresh_token) 2. If all silent methods fail (or force=true), fall back to browser OAuth flow
func (*OAuthProvider) Logout ¶
func (p *OAuthProvider) Logout() error
Logout clears all stored credentials.
func (*OAuthProvider) Status ¶
func (p *OAuthProvider) Status() (*TokenData, error)
Status returns the current auth status.
type PortableImportReport ¶ added in v1.0.33
PortableImportReport summarizes bundle metadata consumed during import.
func ImportPortableAuthBundle ¶ added in v1.0.33
func ImportPortableAuthBundle(configDir string, r io.Reader) (PortableImportReport, error)
ImportPortableAuthBundle extracts a tar.gz auth bundle into the current config and keychain locations.
type SecretInput ¶ added in v1.0.4
type SecretInput struct {
Plain string // non-empty for plain string values
Ref *SecretRef // non-nil for SecretRef values
}
SecretInput represents a secret value: either a plain string or a SecretRef object.
func PlainSecret ¶ added in v1.0.4
func PlainSecret(s string) SecretInput
PlainSecret creates a SecretInput from a plain string.
func StoreSecret ¶ added in v1.0.4
func StoreSecret(clientID string, input SecretInput) (SecretInput, error)
StoreSecret stores a plain text secret in keychain and returns a SecretRef. If the input is already a SecretRef, it is returned as-is. Returns error if keychain is unavailable.
func (SecretInput) IsPlain ¶ added in v1.0.4
func (s SecretInput) IsPlain() bool
IsPlain returns true if this is a plain text string (not a SecretRef).
func (SecretInput) IsSecretRef ¶ added in v1.0.4
func (s SecretInput) IsSecretRef() bool
IsSecretRef returns true if this is a SecretRef object.
func (SecretInput) IsZero ¶ added in v1.0.4
func (s SecretInput) IsZero() bool
IsZero returns true if the SecretInput has no value.
func (SecretInput) MarshalJSON ¶ added in v1.0.4
func (s SecretInput) MarshalJSON() ([]byte, error)
MarshalJSON serializes SecretInput: plain string → JSON string, SecretRef → JSON object.
func (*SecretInput) UnmarshalJSON ¶ added in v1.0.4
func (s *SecretInput) UnmarshalJSON(data []byte) error
UnmarshalJSON deserializes SecretInput from either a JSON string or a SecretRef object.
type SecretRef ¶ added in v1.0.4
type SecretRef struct {
Source string `json:"source"` // "keychain" | "file"
ID string `json:"id"` // keychain key or file path
}
SecretRef references a secret stored externally.
type SendApplyResponse ¶ added in v1.0.5
type SendApplyResponse struct {
Success bool `json:"success"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMsg string `json:"errorMsg,omitempty"`
Result bool `json:"result"`
}
SendApplyResponse represents the response from /cli/sendCliAuthApply API.
func SendCliAuthApply ¶ added in v1.0.5
func SendCliAuthApply(ctx context.Context, accessToken, adminStaffID string) (*SendApplyResponse, error)
SendCliAuthApply sends a CLI auth apply request to the specified admin. It retries up to mcpRequestMaxRetries times on transient errors.
type SuperAdmin ¶ added in v1.0.5
SuperAdmin represents a corp super admin.
type SuperAdminResponse ¶ added in v1.0.5
type SuperAdminResponse struct {
Success bool `json:"success"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMsg string `json:"errorMsg,omitempty"`
Result []SuperAdmin `json:"result"`
}
SuperAdminResponse represents the response from /cli/superAdmin API.
func GetSuperAdmins ¶ added in v1.0.5
func GetSuperAdmins(ctx context.Context, accessToken string) (*SuperAdminResponse, error)
GetSuperAdmins fetches the list of corp super admins. It retries up to mcpRequestMaxRetries times on transient errors.
type TokenData ¶
type TokenData struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
PersistentCode string `json:"persistent_code"`
ExpiresAt time.Time `json:"expires_at"`
RefreshExpAt time.Time `json:"refresh_expires_at"`
CorpID string `json:"corp_id"`
UserID string `json:"user_id,omitempty"`
UserName string `json:"user_name,omitempty"`
CorpName string `json:"corp_name,omitempty"`
ClientID string `json:"client_id,omitempty"` // Associated app client ID for refresh
UpdatedAt string `json:"updated_at,omitempty"`
Source string `json:"source,omitempty"`
}
TokenData holds the OAuth token set persisted to disk.
func ExchangeCodeForToken ¶ added in v1.0.11
ExchangeCodeForToken exchanges an authorization code for token data using the currently configured client credentials. This is a convenience wrapper around OAuthProvider.exchangeCode for callers outside the auth package.
func LoadSecureTokenData ¶
LoadSecureTokenData decrypts and loads TokenData from .data file. Reads are safe without locking because SaveSecureTokenData uses atomic rename.
func LoadTokenData ¶
LoadTokenData reads TokenData. When an edition hook (LoadToken) is registered, it delegates entirely to the hook; otherwise it falls back to keychain with legacy .data migration.
func LoadTokenDataKeychain ¶
LoadTokenDataKeychain loads TokenData from the platform keychain.
func (*TokenData) HasPersistentCode ¶
HasPersistentCode returns true if a persistent code is available.
func (*TokenData) IsAccessTokenValid ¶
IsAccessTokenValid returns true if the access token has not expired.
func (*TokenData) IsRefreshTokenValid ¶
IsRefreshTokenValid returns true if the refresh token has not expired.
type TokenMarker ¶ added in v1.0.9
type TokenMarker struct {
UpdatedAt string `json:"updated_at"`
}
TokenMarker is a lightweight file the host application reads to detect whether the CLI has a valid token without accessing the keychain.