mirror of
https://gitlab.com/TECHNOFAB/go-copilot-proxy.git
synced 2025-12-11 22:10:06 +01:00
chore: initial commit
This commit is contained in:
commit
595200836c
16 changed files with 1571 additions and 0 deletions
142
internal/auth/oauth.go
Normal file
142
internal/auth/oauth.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/atotto/clipboard"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"gitlab.com/technofab/go-copilot-proxy/internal/config"
|
||||
)
|
||||
|
||||
type DeviceCodeResponse struct {
|
||||
DeviceCode string `json:"device_code"`
|
||||
UserCode string `json:"user_code"`
|
||||
VerificationURI string `json:"verification_uri"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
||||
type AccessTokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
Scope string `json:"scope"`
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
func RequestDeviceCode() (*DeviceCodeResponse, error) {
|
||||
payload := strings.NewReader(fmt.Sprintf(`{"client_id":"%s","scope":"%s"}`, config.GHClientID, config.GHScope))
|
||||
req, err := http.NewRequest("POST", config.GHDeviceCodeURL, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("User-Agent", config.UserAgent)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("bad response from GitHub: %s", resp.Status)
|
||||
}
|
||||
|
||||
var data DeviceCodeResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func PollForAccessToken(deviceCodeInfo *DeviceCodeResponse) (string, error) {
|
||||
interval := time.Duration(deviceCodeInfo.Interval) * time.Second
|
||||
if interval == 0 {
|
||||
interval = 5 * time.Second
|
||||
}
|
||||
|
||||
log.Info().Msg("Waiting for you to authorize in the browser...")
|
||||
|
||||
for {
|
||||
time.Sleep(interval)
|
||||
fmt.Print(".")
|
||||
|
||||
payload := strings.NewReader(fmt.Sprintf(
|
||||
`{"client_id":"%s","device_code":"%s","grant_type":"urn:ietf:params:oauth:grant-type:device_code"}`,
|
||||
config.GHClientID, deviceCodeInfo.DeviceCode,
|
||||
))
|
||||
req, err := http.NewRequest("POST", config.GHOauthTokenURL, payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("User-Agent", config.UserAgent)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var data AccessTokenResponse
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
resp.Body.Close()
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if data.AccessToken != "" {
|
||||
fmt.Println()
|
||||
return data.AccessToken, nil
|
||||
}
|
||||
|
||||
if data.Error == "authorization_pending" {
|
||||
continue
|
||||
}
|
||||
|
||||
if data.Error != "" {
|
||||
return "", fmt.Errorf("authentication failed: %s - %s", data.Error, data.ErrorDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
func SaveOAuthToken(token string) error {
|
||||
path, err := getConfigPath("config.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tokenData := map[string]string{
|
||||
"oauth_token": token,
|
||||
}
|
||||
|
||||
jsonData, err := json.MarshalIndent(tokenData, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal token data: %w", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
return os.WriteFile(path, jsonData, 0644)
|
||||
}
|
||||
|
||||
func PromptUserForAuth(deviceCodeResp *DeviceCodeResponse) {
|
||||
log.Info().Msgf("Please open this URL in your browser: %s", deviceCodeResp.VerificationURI)
|
||||
log.Info().Msgf("And enter this code: %s", deviceCodeResp.UserCode)
|
||||
|
||||
if err := clipboard.WriteAll(deviceCodeResp.UserCode); err == nil {
|
||||
log.Info().Msg("(The code has been copied to your clipboard!)")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue