-
Notifications
You must be signed in to change notification settings - Fork 577
Fix GitHub Copilot authentication flow #294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Implement a simplified 4-step authentication flow: 1. Check if Copilot is enabled in config 2. Check for token in config folder 3. If no token, trigger login flow 4. With token ready, open OpenCode normally Key improvements: - Cleaner code structure with helper functions - Better error handling for auth scenarios - Support for both hosts.json and apps.json formats - Proper API version headers for GitHub Copilot - Clear documentation of client ID usage
- Remove redundant checks for Copilot provider in Validate() - Improve comments for better clarity - Maintain the same functionality with cleaner code
- Simplify logging in GitHub Copilot authentication - Remove redundant logging messages - Remove unused getMapKeys helper function - Keep only essential user feedback during authentication
- Implement device flow authentication for GitHub Copilot - Add token saving to standard ~/.config/github-copilot/hosts.json - Fix token validation and error handling - Continue polling when receiving empty responses - Set environment variables for immediate use of token - Clean and simplify code implementation
- Remove redundant token saving functions - Combine hosts.json handling in a single function - Simplify environment variable checks - Remove excessive debug logging - Streamline error handling and code paths
internal/llm/provider/copilot.go
Outdated
} | ||
|
||
// Set environment variables for immediate use | ||
os.Setenv("GITHUB_TOKEN", token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid conflicts with other tools like the GitHub CLI (gh) that uses GITHUB_TOKEN
, I think we should use only GITHUB_COPILOT_TOKEN for all Copilot-related requests using opencode and leave GITHUB_TOKEN untouched.
internal/config/config.go
Outdated
logging.Debug("Attempting to load GitHub token for Copilot") | ||
|
||
// First check environment variables | ||
for _, envName := range []string{"GITHUB_TOKEN", "GITHUB_COPILOT_TOKEN", "GH_COPILOT_TOKEN"} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, I was just about to create a PR for this myself. We should move away from GITHUB_TOKEN
for the reason mentioned here. All the other tools like Gemini and ChatGPT use their own environment variables, so it makes sense to give Copilot its own too. Using GITHUB_TOKEN
can be misleading as copilot request don't accept them.
For me this method should be something like this:
func LoadGitHubToken() (string, error) {
// 1. Environment variable
var token string
if token = os.Getenv("GITHUB_COPILOT_TOKEN"); token != "" {
logging.Debug("Loaded GitHub Copilot API key from a system environment variable.")
return token, nil
}
// 2. API key from options
if token = cfg.Providers[models.ProviderCopilot].APIKey; token != "" {
logging.Debug("Loaded GitHub Copilot API key from the '.opencode.json' configuration file.")
return token, nil
}
// Get config directory
var configDir string
if xdgConfig := os.Getenv("XDG_CONFIG_HOME"); xdgConfig != "" {
configDir = xdgConfig
} else if runtime.GOOS == "windows" {
if localAppData := os.Getenv("LOCALAPPDATA"); localAppData != "" {
configDir = localAppData
} else {
configDir = filepath.Join(os.Getenv("HOME"), "AppData", "Local")
}
} else {
configDir = filepath.Join(os.Getenv("HOME"), ".config")
}
// Try both hosts.json and apps.json files
filePaths := []string{
filepath.Join(configDir, "github-copilot", "hosts.json"),
filepath.Join(configDir, "github-copilot", "apps.json"),
}
for _, filePath := range filePaths {
data, err := os.ReadFile(filePath)
if err != nil {
continue
}
var config map[string]map[string]interface{}
if err := json.Unmarshal(data, &config); err != nil {
continue
}
for key, value := range config {
if strings.Contains(key, "github.com") {
if oauthToken, ok := value["oauth_token"].(string); ok {
logging.Debug("Loaded GitHub Copilot token from the standard user configuration file.")
return oauthToken, nil
}
}
}
}
return "", fmt.Errorf("GitHub token not found in standard locations")
}
I tested your PR but ran into this error: ![]() This happens because my GITHUB_TOKEN is being rejected by Copilot. This creates a frustrating workflow for anyone who relies on that token for other tools. We shouldn't have to unset an important variable just to open opencode. So Like I said before we need to move away from If we do this we can fix #294 Thank you so much for the effort =) |
…PILOT_TOKEN Address the issue where GITHUB_TOKEN conflicts with GitHub CLI tools by: - Prioritizing GITHUB_COPILOT_TOKEN environment variable over GITHUB_TOKEN - Updating token loading logic to avoid CLI token conflicts - Modifying error messages to reference the correct token variable - Updating documentation to reflect the new token preference This resolves 401 Unauthorized errors when users have standard GitHub tokens without Copilot scope. 🤖 Generated with opencode Co-Authored-By: opencode <noreply@opencode.ai>
Hey @mendesbarreto pushed a new commit with your feedback. Is this what you had in mind? Thank you for your comments! |
It's working =) fixes #270 |
What's the next step to get it merged? |
I have been following the tale of Copilot since its conception, and the last one who approved something similar was @kujtimiihoxha. 😊 |
@bitomule I think the why is here: |
Summary
This PR fixes the GitHub Copilot authentication flow in OpenCode, which currently has issues with token retrieval and exchange.
Problem Description
Currently, the GitHub Copilot integration has several issues:
gh auth token
) which don't work with Copilot API because they lack the required Copilot scopeSolution
This PR implements a complete authentication flow inspired by the one used in the Neovim Copilot plugin:
Documentation
Added comprehensive documentation to the README explaining:
This PR makes Copilot integration work seamlessly with OpenCode, especially important for users wanting to access Claude models through Copilot.