kek
Fresh start: Go 1.23 + go-git/v5 compatibility
f606b10
package copilot
import (
"errors"
"fmt"
"net/http"
)
// OAuthError represents an OAuth-specific error.
type OAuthError struct {
// Code is the OAuth error code.
Code string `json:"error"`
// Description is a human-readable description of the error.
Description string `json:"error_description,omitempty"`
// URI is a URI identifying a human-readable web page with information about the error.
URI string `json:"error_uri,omitempty"`
// StatusCode is the HTTP status code associated with the error.
StatusCode int `json:"-"`
}
// Error returns a string representation of the OAuth error.
func (e *OAuthError) Error() string {
if e.Description != "" {
return fmt.Sprintf("OAuth error %s: %s", e.Code, e.Description)
}
return fmt.Sprintf("OAuth error: %s", e.Code)
}
// NewOAuthError creates a new OAuth error with the specified code, description, and status code.
func NewOAuthError(code, description string, statusCode int) *OAuthError {
return &OAuthError{
Code: code,
Description: description,
StatusCode: statusCode,
}
}
// AuthenticationError represents authentication-related errors.
type AuthenticationError struct {
// Type is the type of authentication error.
Type string `json:"type"`
// Message is a human-readable message describing the error.
Message string `json:"message"`
// Code is the HTTP status code associated with the error.
Code int `json:"code"`
// Cause is the underlying error that caused this authentication error.
Cause error `json:"-"`
}
// Error returns a string representation of the authentication error.
func (e *AuthenticationError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("%s: %s (caused by: %v)", e.Type, e.Message, e.Cause)
}
return fmt.Sprintf("%s: %s", e.Type, e.Message)
}
// Unwrap returns the underlying cause of the error.
func (e *AuthenticationError) Unwrap() error {
return e.Cause
}
// Common authentication error types for GitHub Copilot device flow.
var (
// ErrDeviceCodeFailed represents an error when requesting the device code fails.
ErrDeviceCodeFailed = &AuthenticationError{
Type: "device_code_failed",
Message: "Failed to request device code from GitHub",
Code: http.StatusBadRequest,
}
// ErrDeviceCodeExpired represents an error when the device code has expired.
ErrDeviceCodeExpired = &AuthenticationError{
Type: "device_code_expired",
Message: "Device code has expired. Please try again.",
Code: http.StatusGone,
}
// ErrAuthorizationPending represents a pending authorization state (not an error, used for polling).
ErrAuthorizationPending = &AuthenticationError{
Type: "authorization_pending",
Message: "Authorization is pending. Waiting for user to authorize.",
Code: http.StatusAccepted,
}
// ErrSlowDown represents a request to slow down polling.
ErrSlowDown = &AuthenticationError{
Type: "slow_down",
Message: "Polling too frequently. Slowing down.",
Code: http.StatusTooManyRequests,
}
// ErrAccessDenied represents an error when the user denies authorization.
ErrAccessDenied = &AuthenticationError{
Type: "access_denied",
Message: "User denied authorization",
Code: http.StatusForbidden,
}
// ErrTokenExchangeFailed represents an error when token exchange fails.
ErrTokenExchangeFailed = &AuthenticationError{
Type: "token_exchange_failed",
Message: "Failed to exchange device code for access token",
Code: http.StatusBadRequest,
}
// ErrPollingTimeout represents an error when polling times out.
ErrPollingTimeout = &AuthenticationError{
Type: "polling_timeout",
Message: "Timeout waiting for user authorization",
Code: http.StatusRequestTimeout,
}
// ErrUserInfoFailed represents an error when fetching user info fails.
ErrUserInfoFailed = &AuthenticationError{
Type: "user_info_failed",
Message: "Failed to fetch GitHub user information",
Code: http.StatusBadRequest,
}
)
// NewAuthenticationError creates a new authentication error with a cause based on a base error.
func NewAuthenticationError(baseErr *AuthenticationError, cause error) *AuthenticationError {
return &AuthenticationError{
Type: baseErr.Type,
Message: baseErr.Message,
Code: baseErr.Code,
Cause: cause,
}
}
// IsAuthenticationError checks if an error is an authentication error.
func IsAuthenticationError(err error) bool {
var authenticationError *AuthenticationError
ok := errors.As(err, &authenticationError)
return ok
}
// IsOAuthError checks if an error is an OAuth error.
func IsOAuthError(err error) bool {
var oAuthError *OAuthError
ok := errors.As(err, &oAuthError)
return ok
}
// GetUserFriendlyMessage returns a user-friendly error message based on the error type.
func GetUserFriendlyMessage(err error) string {
var authErr *AuthenticationError
if errors.As(err, &authErr) {
switch authErr.Type {
case "device_code_failed":
return "Failed to start GitHub authentication. Please check your network connection and try again."
case "device_code_expired":
return "The authentication code has expired. Please try again."
case "authorization_pending":
return "Waiting for you to authorize the application on GitHub."
case "slow_down":
return "Please wait a moment before trying again."
case "access_denied":
return "Authentication was cancelled or denied."
case "token_exchange_failed":
return "Failed to complete authentication. Please try again."
case "polling_timeout":
return "Authentication timed out. Please try again."
case "user_info_failed":
return "Failed to get your GitHub account information. Please try again."
default:
return "Authentication failed. Please try again."
}
}
var oauthErr *OAuthError
if errors.As(err, &oauthErr) {
switch oauthErr.Code {
case "access_denied":
return "Authentication was cancelled or denied."
case "invalid_request":
return "Invalid authentication request. Please try again."
case "server_error":
return "GitHub server error. Please try again later."
default:
return fmt.Sprintf("Authentication failed: %s", oauthErr.Description)
}
}
return "An unexpected error occurred. Please try again."
}