mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-04-07 05:17:23 +00:00
96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codebuddy"
|
|
"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"
|
|
)
|
|
|
|
// CodeBuddyAuthenticator implements the browser OAuth polling flow for CodeBuddy.
|
|
type CodeBuddyAuthenticator struct{}
|
|
|
|
// NewCodeBuddyAuthenticator constructs a new CodeBuddy authenticator.
|
|
func NewCodeBuddyAuthenticator() Authenticator {
|
|
return &CodeBuddyAuthenticator{}
|
|
}
|
|
|
|
// Provider returns the provider key for codebuddy.
|
|
func (CodeBuddyAuthenticator) Provider() string {
|
|
return "codebuddy"
|
|
}
|
|
|
|
// codeBuddyRefreshLead is the duration before token expiry when a refresh should be attempted.
|
|
var codeBuddyRefreshLead = 24 * time.Hour
|
|
|
|
// RefreshLead returns how soon before expiry a refresh should be attempted.
|
|
// CodeBuddy tokens have a long validity period, so we refresh 24 hours before expiry.
|
|
func (CodeBuddyAuthenticator) RefreshLead() *time.Duration {
|
|
return &codeBuddyRefreshLead
|
|
}
|
|
|
|
// Login initiates the browser OAuth flow for CodeBuddy.
|
|
func (a CodeBuddyAuthenticator) Login(ctx context.Context, cfg *config.Config, opts *LoginOptions) (*coreauth.Auth, error) {
|
|
if cfg == nil {
|
|
return nil, fmt.Errorf("codebuddy: configuration is required")
|
|
}
|
|
if opts == nil {
|
|
opts = &LoginOptions{}
|
|
}
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
authSvc := codebuddy.NewCodeBuddyAuth(cfg)
|
|
|
|
authState, err := authSvc.FetchAuthState(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("codebuddy: failed to fetch auth state: %w", err)
|
|
}
|
|
|
|
fmt.Printf("\nPlease open the following URL in your browser to login:\n\n %s\n\n", authState.AuthURL)
|
|
fmt.Println("Waiting for authorization...")
|
|
|
|
if !opts.NoBrowser {
|
|
if browser.IsAvailable() {
|
|
if errOpen := browser.OpenURL(authState.AuthURL); errOpen != nil {
|
|
log.Debugf("codebuddy: failed to open browser: %v", errOpen)
|
|
}
|
|
}
|
|
}
|
|
|
|
storage, err := authSvc.PollForToken(ctx, authState.State)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("codebuddy: %s: %w", codebuddy.GetUserFriendlyMessage(err), err)
|
|
}
|
|
|
|
fmt.Printf("\nSuccessfully logged in! (User ID: %s)\n", storage.UserID)
|
|
|
|
authID := fmt.Sprintf("codebuddy-%s.json", storage.UserID)
|
|
|
|
label := storage.UserID
|
|
if label == "" {
|
|
label = "codebuddy-user"
|
|
}
|
|
|
|
return &coreauth.Auth{
|
|
ID: authID,
|
|
Provider: a.Provider(),
|
|
FileName: authID,
|
|
Label: label,
|
|
Storage: storage,
|
|
Metadata: map[string]any{
|
|
"access_token": storage.AccessToken,
|
|
"refresh_token": storage.RefreshToken,
|
|
"user_id": storage.UserID,
|
|
"domain": storage.Domain,
|
|
"expires_in": storage.ExpiresIn,
|
|
},
|
|
}, nil
|
|
}
|