mirror of
https://github.com/larksuite/cli.git
synced 2026-07-07 17:45:15 +08:00
* feat(auth): improve scope handling and output in login flow - Add scope validation to check for missing requested scopes - Implement detailed scope breakdown in login success output - Add new message strings for scope-related output - Refactor login success output to handle both JSON and text formats - Add tests for scope validation and output scenarios * feat(auth): add requested scope caching for device code login Implement caching of requested scopes during device code login flow to ensure proper scope validation after authorization. The cache is stored in JSON files under config directory and automatically cleaned up after successful or failed authorization. Add tests for scope caching functionality and verify proper integration with existing login flow. * docs(auth): add function comments for login scope handling Add detailed doc comments to all functions in login scope cache and result handling files to improve code documentation and maintainability. * refactor(auth): remove pending scopes and improve json output stability - Remove PendingScopes field and related logic as it's no longer needed - Add emptyIfNil helper to ensure nil slices are normalized to empty slices in JSON output - Update tests to verify JSON output stability and fix expected text outputs * refactor(auth): extract device token polling function for testability Move device token polling to a package-level variable to enable mocking in tests Add test case for scope cleanup when token is nil * fix(auth): return JSON write errors instead of ignoring them Previously, JSON write errors were only logged to stderr but not returned, causing tests to pass when they should fail. Now properly propagate these errors to callers and update tests to verify error handling. * refactor(auth): simplify scope handling and improve user messaging remove redundant scope display and consolidate hint messages to focus on actionable guidance * refactor(auth): improve scope handling and messaging in login flow remove ShortHint field and simplify scope hint messages always display missing scopes section with consistent formatting add StatusHint for successful login with no missing scopes update tests to reflect new message structure and content
92 lines
2.7 KiB
Go
92 lines
2.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
|
|
larkauth "github.com/larksuite/cli/internal/auth"
|
|
"github.com/larksuite/cli/internal/core"
|
|
"github.com/larksuite/cli/internal/validate"
|
|
"github.com/larksuite/cli/internal/vfs"
|
|
)
|
|
|
|
var loginScopeCacheSafeChars = regexp.MustCompile(`[^a-zA-Z0-9._-]`)
|
|
|
|
type loginScopeCacheRecord struct {
|
|
RequestedScope string `json:"requested_scope"`
|
|
}
|
|
|
|
// loginScopeCacheDir returns the directory used to persist auth login --no-wait
|
|
// requested scopes keyed by device_code.
|
|
func loginScopeCacheDir() string {
|
|
return filepath.Join(core.GetConfigDir(), "cache", "auth_login_scopes")
|
|
}
|
|
|
|
// loginScopeCachePath returns the cache file path for a given device_code.
|
|
func loginScopeCachePath(deviceCode string) string {
|
|
return filepath.Join(loginScopeCacheDir(), sanitizeLoginScopeCacheKey(deviceCode)+".json")
|
|
}
|
|
|
|
// sanitizeLoginScopeCacheKey converts a device_code into a safe filename token.
|
|
func sanitizeLoginScopeCacheKey(deviceCode string) string {
|
|
sanitized := loginScopeCacheSafeChars.ReplaceAllString(deviceCode, "_")
|
|
if sanitized == "" {
|
|
return "default"
|
|
}
|
|
return sanitized
|
|
}
|
|
|
|
// saveLoginRequestedScope persists the requested scope string for a device_code.
|
|
func saveLoginRequestedScope(deviceCode, requestedScope string) error {
|
|
if err := vfs.MkdirAll(loginScopeCacheDir(), 0700); err != nil {
|
|
return err
|
|
}
|
|
data, err := json.Marshal(loginScopeCacheRecord{RequestedScope: requestedScope})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return validate.AtomicWrite(loginScopeCachePath(deviceCode), data, 0600)
|
|
}
|
|
|
|
// loadLoginRequestedScope loads the cached requested scope string for a device_code.
|
|
// It returns an empty string if no cache entry exists.
|
|
func loadLoginRequestedScope(deviceCode string) (string, error) {
|
|
data, err := vfs.ReadFile(loginScopeCachePath(deviceCode))
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return "", nil
|
|
}
|
|
return "", err
|
|
}
|
|
var record loginScopeCacheRecord
|
|
if err := json.Unmarshal(data, &record); err != nil {
|
|
_ = vfs.Remove(loginScopeCachePath(deviceCode))
|
|
return "", err
|
|
}
|
|
return record.RequestedScope, nil
|
|
}
|
|
|
|
// removeLoginRequestedScope deletes the cache entry for a device_code.
|
|
func removeLoginRequestedScope(deviceCode string) error {
|
|
err := vfs.Remove(loginScopeCachePath(deviceCode))
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// shouldRemoveLoginRequestedScope indicates whether the requested-scope cache
|
|
// should be removed after polling finishes.
|
|
func shouldRemoveLoginRequestedScope(result *larkauth.DeviceFlowResult) bool {
|
|
if result == nil {
|
|
return false
|
|
}
|
|
if result.OK || result.Error == "access_denied" {
|
|
return true
|
|
}
|
|
return result.Error == "expired_token" && result.Message != "Polling was cancelled"
|
|
}
|