mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 04:10:51 +08:00
- Introduced `RequestKimiToken` API for Kimi authentication flow. - Integrated device ID management throughout Kimi-related components. - Enhanced header management for Kimi API requests with device ID context.
124 lines
3.5 KiB
Go
124 lines
3.5 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/kimi"
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/browser"
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
|
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// kimiRefreshLead is the duration before token expiry when refresh should occur.
|
|
var kimiRefreshLead = 5 * time.Minute
|
|
|
|
// KimiAuthenticator implements the OAuth device flow login for Kimi (Moonshot AI).
|
|
type KimiAuthenticator struct{}
|
|
|
|
// NewKimiAuthenticator constructs a new Kimi authenticator.
|
|
func NewKimiAuthenticator() Authenticator {
|
|
return &KimiAuthenticator{}
|
|
}
|
|
|
|
// Provider returns the provider key for kimi.
|
|
func (KimiAuthenticator) Provider() string {
|
|
return "kimi"
|
|
}
|
|
|
|
// RefreshLead returns the duration before token expiry when refresh should occur.
|
|
// Kimi tokens expire and need to be refreshed before expiry.
|
|
func (KimiAuthenticator) RefreshLead() *time.Duration {
|
|
return &kimiRefreshLead
|
|
}
|
|
|
|
// Login initiates the Kimi device flow authentication.
|
|
func (a KimiAuthenticator) Login(ctx context.Context, cfg *config.Config, opts *LoginOptions) (*coreauth.Auth, error) {
|
|
if cfg == nil {
|
|
return nil, fmt.Errorf("cliproxy auth: configuration is required")
|
|
}
|
|
if opts == nil {
|
|
opts = &LoginOptions{}
|
|
}
|
|
|
|
authSvc := kimi.NewKimiAuth(cfg)
|
|
|
|
// Start the device flow
|
|
fmt.Println("Starting Kimi authentication...")
|
|
deviceCode, err := authSvc.StartDeviceFlow(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("kimi: failed to start device flow: %w", err)
|
|
}
|
|
|
|
// Display the verification URL
|
|
verificationURL := deviceCode.VerificationURIComplete
|
|
if verificationURL == "" {
|
|
verificationURL = deviceCode.VerificationURI
|
|
}
|
|
|
|
fmt.Printf("\nTo authenticate, please visit:\n%s\n\n", verificationURL)
|
|
if deviceCode.UserCode != "" {
|
|
fmt.Printf("User code: %s\n\n", deviceCode.UserCode)
|
|
}
|
|
|
|
// Try to open the browser automatically
|
|
if !opts.NoBrowser {
|
|
if browser.IsAvailable() {
|
|
if errOpen := browser.OpenURL(verificationURL); errOpen != nil {
|
|
log.Warnf("Failed to open browser automatically: %v", errOpen)
|
|
} else {
|
|
fmt.Println("Browser opened automatically.")
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Println("Waiting for authorization...")
|
|
if deviceCode.ExpiresIn > 0 {
|
|
fmt.Printf("(This will timeout in %d seconds if not authorized)\n", deviceCode.ExpiresIn)
|
|
}
|
|
|
|
// Wait for user authorization
|
|
authBundle, err := authSvc.WaitForAuthorization(ctx, deviceCode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("kimi: %w", err)
|
|
}
|
|
|
|
// Create the token storage
|
|
tokenStorage := authSvc.CreateTokenStorage(authBundle)
|
|
|
|
// Build metadata with token information
|
|
metadata := map[string]any{
|
|
"type": "kimi",
|
|
"access_token": authBundle.TokenData.AccessToken,
|
|
"refresh_token": authBundle.TokenData.RefreshToken,
|
|
"token_type": authBundle.TokenData.TokenType,
|
|
"scope": authBundle.TokenData.Scope,
|
|
"timestamp": time.Now().UnixMilli(),
|
|
}
|
|
|
|
if authBundle.TokenData.ExpiresAt > 0 {
|
|
exp := time.Unix(authBundle.TokenData.ExpiresAt, 0).UTC().Format(time.RFC3339)
|
|
metadata["expired"] = exp
|
|
}
|
|
if strings.TrimSpace(authBundle.DeviceID) != "" {
|
|
metadata["device_id"] = strings.TrimSpace(authBundle.DeviceID)
|
|
}
|
|
|
|
// Generate a unique filename
|
|
fileName := fmt.Sprintf("kimi-%d.json", time.Now().UnixMilli())
|
|
|
|
fmt.Println("\nKimi authentication successful!")
|
|
|
|
return &coreauth.Auth{
|
|
ID: fileName,
|
|
Provider: a.Provider(),
|
|
FileName: fileName,
|
|
Label: "Kimi User",
|
|
Storage: tokenStorage,
|
|
Metadata: metadata,
|
|
}, nil
|
|
}
|