mirror of
https://github.com/hanzoskill/mogcli.git
synced 2026-10-30 21:03:34 +00:00
148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/visionik/mogcli/internal/config"
|
|
"github.com/visionik/mogcli/internal/graph"
|
|
)
|
|
|
|
// AuthCmd handles authentication commands.
|
|
type AuthCmd struct {
|
|
Login AuthLoginCmd `cmd:"" help:"Login to Microsoft 365"`
|
|
Status AuthStatusCmd `cmd:"" help:"Show authentication status"`
|
|
Logout AuthLogoutCmd `cmd:"" help:"Logout and clear tokens"`
|
|
}
|
|
|
|
// AuthLoginCmd logs in to Microsoft 365.
|
|
type AuthLoginCmd struct {
|
|
ClientID string `help:"Azure AD client ID" required:"" env:"MOG_CLIENT_ID" name:"client-id"`
|
|
Storage string `help:"Token storage: file or keychain" default:"file" enum:"file,keychain"`
|
|
}
|
|
|
|
// Run executes the auth login command.
|
|
func (c *AuthLoginCmd) Run(root *Root) error {
|
|
// Set storage type
|
|
if c.Storage == "keychain" {
|
|
config.SetStorage(config.StorageKeyring)
|
|
} else {
|
|
config.SetStorage(config.StorageFile)
|
|
}
|
|
|
|
// Save client ID and storage preference
|
|
cfg := &config.Config{ClientID: c.ClientID, Storage: c.Storage}
|
|
if err := config.Save(cfg); err != nil {
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
}
|
|
|
|
// Request device code
|
|
fmt.Println("Requesting device code...")
|
|
dcResp, err := graph.RequestDeviceCode(c.ClientID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to request device code: %w", err)
|
|
}
|
|
|
|
fmt.Println()
|
|
fmt.Println(dcResp.Message)
|
|
fmt.Println()
|
|
|
|
// Try to open browser
|
|
openBrowser(dcResp.VerificationURI)
|
|
|
|
// Poll for token
|
|
fmt.Println("Waiting for authorization...")
|
|
tokens, err := graph.PollForToken(c.ClientID, dcResp.DeviceCode, dcResp.Interval)
|
|
if err != nil {
|
|
return fmt.Errorf("authorization failed: %w", err)
|
|
}
|
|
|
|
// Save tokens using configured storage
|
|
if err := config.SaveTokensAuto(tokens); err != nil {
|
|
return fmt.Errorf("failed to save tokens: %w", err)
|
|
}
|
|
|
|
fmt.Println()
|
|
fmt.Printf("✓ Successfully logged in! (storage: %s)\n", c.Storage)
|
|
return nil
|
|
}
|
|
|
|
// AuthStatusCmd shows authentication status.
|
|
type AuthStatusCmd struct{}
|
|
|
|
// Run executes the auth status command.
|
|
func (c *AuthStatusCmd) Run(root *Root) error {
|
|
// Load config to get storage preference
|
|
cfg, _ := config.Load()
|
|
if cfg != nil && cfg.Storage == "keychain" {
|
|
config.SetStorage(config.StorageKeyring)
|
|
}
|
|
|
|
tokens, err := config.LoadTokensAuto()
|
|
if err != nil {
|
|
fmt.Println("Status: Not logged in")
|
|
return nil
|
|
}
|
|
|
|
fmt.Println("Status: Logged in")
|
|
if cfg != nil && cfg.Storage != "" {
|
|
fmt.Printf("Storage: %s\n", cfg.Storage)
|
|
}
|
|
|
|
if tokens.ExpiresAt > 0 {
|
|
expiresAt := time.Unix(tokens.ExpiresAt, 0)
|
|
remaining := time.Until(expiresAt)
|
|
if remaining > 0 {
|
|
fmt.Printf("Token expires: %s (in %v)\n", expiresAt.Format(time.RFC3339), remaining.Round(time.Minute))
|
|
} else {
|
|
fmt.Println("Token: Expired (will refresh on next request)")
|
|
}
|
|
}
|
|
|
|
if cfg != nil && cfg.ClientID != "" {
|
|
fmt.Printf("Client ID: %s...%s\n", cfg.ClientID[:8], cfg.ClientID[len(cfg.ClientID)-4:])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AuthLogoutCmd logs out and clears tokens.
|
|
type AuthLogoutCmd struct{}
|
|
|
|
// Run executes the auth logout command.
|
|
func (c *AuthLogoutCmd) Run(root *Root) error {
|
|
// Load config to get storage preference
|
|
cfg, _ := config.Load()
|
|
if cfg != nil && cfg.Storage == "keychain" {
|
|
config.SetStorage(config.StorageKeyring)
|
|
}
|
|
|
|
if err := config.DeleteTokensAuto(); err != nil {
|
|
return fmt.Errorf("failed to delete tokens: %w", err)
|
|
}
|
|
|
|
if err := graph.ClearSlugs(); err != nil {
|
|
return fmt.Errorf("failed to clear slugs: %w", err)
|
|
}
|
|
|
|
fmt.Println("✓ Logged out successfully")
|
|
return nil
|
|
}
|
|
|
|
func openBrowser(url string) {
|
|
var cmd *exec.Cmd
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
cmd = exec.Command("open", url)
|
|
case "linux":
|
|
cmd = exec.Command("xdg-open", url)
|
|
case "windows":
|
|
cmd = exec.Command("cmd", "/c", "start", url)
|
|
default:
|
|
return
|
|
}
|
|
_ = cmd.Start()
|
|
}
|