feat: support in-memory authentication

This commit is contained in:
2026-07-22 15:59:17 +08:00
parent 6d7f08bedd
commit 12ba947f9b
3 changed files with 359 additions and 32 deletions
+44 -6
View File
@@ -24,7 +24,10 @@ const (
availabilityTTL = 60 * time.Second
)
type Option func(*Client) error
type ClientOption func(*Client) error
// Option is kept as an alias for compatibility with existing callers.
type Option = ClientOption
// WithHTTPClient configures the HTTP transport used by the client.
func WithHTTPClient(httpClient *http.Client) Option {
@@ -58,6 +61,7 @@ func WithQRWriter(writer io.Writer) Option {
type Client struct {
authPath string
authDataChanged func(AuthData) error
authMu sync.RWMutex
authData AuthData
loginMu sync.Mutex
@@ -80,8 +84,34 @@ func NewClient(authPath string, options ...Option) (*Client, error) {
if err != nil {
return nil, err
}
client, err := newClient(options...)
if err != nil {
return nil, err
}
client.authPath = resolvedPath
if err := client.loadAuthData(); err != nil {
return nil, err
}
client.ensureIdentity()
return client, nil
}
// NewClientWithAuthData creates a client whose authentication state is kept in memory.
// Zero AuthData is accepted for QR login; non-zero AuthData must be complete.
func NewClientWithAuthData(authData AuthData, options ...ClientOption) (*Client, error) {
if !authData.zero() && !authData.complete() {
return nil, fmt.Errorf("incomplete auth data")
}
client, err := newClient(options...)
if err != nil {
return nil, err
}
client.setAuthData(authData)
return client, nil
}
func newClient(options ...ClientOption) (*Client, error) {
client := &Client{
authPath: resolvedPath,
httpClient: http.DefaultClient,
baseURL: defaultBaseURL,
loginURL: defaultLoginURL,
@@ -97,14 +127,22 @@ func NewClient(authPath string, options ...Option) (*Client, error) {
return nil, err
}
}
if err := client.loadAuthData(); err != nil {
return nil, err
}
client.ensureIdentity()
client.initSession()
return client, nil
}
// WithAuthDataChanged configures serialized persistence for in-memory auth updates.
// The callback runs under login serialization, but never while the auth data lock is held.
func WithAuthDataChanged(callback func(AuthData) error) ClientOption {
return func(client *Client) error {
if callback == nil {
return fmt.Errorf("auth data changed callback must not be nil")
}
client.authDataChanged = callback
return nil
}
}
func resolveAuthPath(authPath string) (string, error) {
if authPath == "" {
home, err := os.UserHomeDir()