Skip to content

Conversation

@jeffnash
Copy link
Contributor

Summary

Implements the complete GitHub Copilot authentication system.

Changes

  • Device-code OAuth flow with GitHub token exchange
  • Copilot token acquisition and refresh logic
  • Account type handling (individual/business/enterprise)
  • Token persistence and storage management
  • CLI login command (cliproxy-api copilot login)
  • Register Copilot refresh handler in SDK

Stack

This is PR 2/5 in the Copilot feature stack:

  1. Infrastructure & Config (feat(copilot): add shared infrastructure and config [1/5] #380)
  2. [This PR] Auth Flow
  3. Executor & Models
  4. Gemini Reasoning
  5. Tests

Depends on: #380 - merge that first

- Add auth directory management helper (GetAuthDir)
- Add random hex string generator for request IDs
- Add helper for generating unique machine identifiers
@chatgpt-codex-connector
Copy link

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@gemini-code-assist
Copy link

Summary of Changes

Hello @jeffnash, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates the full authentication mechanism for GitHub Copilot into the system. It provides a robust way for users to securely log in, obtain, and maintain their Copilot access tokens, supporting various account types and ensuring seamless operation. This foundational work is crucial for enabling future Copilot-powered features.

Highlights

  • GitHub Copilot Authentication Flow: Implemented the complete GitHub Copilot authentication system, including the device-code OAuth flow with GitHub token exchange.
  • Token Management: Added logic for acquiring and refreshing Copilot tokens, along with persistence and storage management for these tokens.
  • Account Type Handling: Incorporated support for different Copilot account types (individual, business, enterprise) and their respective API endpoints.
  • CLI Integration: Introduced a new CLI login command (cliproxy-api copilot login) to facilitate user authentication.
  • SDK Refresh Handler: Registered a Copilot refresh handler within the SDK to automatically manage token lifecycles.
  • Configuration Updates: Updated config.example.yaml and internal/config/config.go to include Copilot-specific configuration options, such as account type, proxy settings, and agent initiator persistence.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the authentication flow for GitHub Copilot, which is a substantial and important feature. The overall implementation is solid, but I've identified several critical issues that must be addressed. These include compilation errors due to a duplicated struct definition and a syntax error, a potential server crash from an unhandled error in the request ID generation, and a security vulnerability related to insecure directory permissions for storing tokens. I've also noted some medium-severity issues, such as ignoring potential errors from json.Marshal. Please review the detailed comments for suggestions on how to resolve these issues.

Comment on lines +107 to +109
if _, err := rand.Read(b); err != nil {
panic(fmt.Sprintf("failed to generate random bytes for request ID: %v", err))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Calling panic in a server application can cause it to crash, making the service unavailable. The crypto/rand.Read function can fail if the operating system's entropy pool is exhausted. It's crucial to handle this error gracefully instead of panicking. A good approach is to fall back to a different UUID generation method.

Note: This suggestion requires importing github.com/google/uuid and github.com/sirupsen/logrus.

if _, err := rand.Read(b); err != nil {
		log.Errorf("crypto/rand.Read failed for request ID, falling back to UUID: %v", err)
		return uuid.New().String()
	}

Comment on lines +219 to +232
// CopilotKey represents the configuration for GitHub Copilot API access.
// Authentication is handled via device code OAuth flow, not API keys.
type CopilotKey struct {
// AccountType is the Copilot subscription type (individual, business, enterprise).
// Defaults to "individual" if not specified.
AccountType string `yaml:"account-type" json:"account-type"`

// ProxyURL overrides the global proxy setting for Copilot requests if provided.
ProxyURL string `yaml:"proxy-url,omitempty" json:"proxy-url,omitempty"`

// AgentInitiatorPersist, when true, forces subsequent Copilot requests sharing the
// same prompt_cache_key to send X-Initiator=agent after the first call. Default false.
AgentInitiatorPersist bool `yaml:"agent-initiator-persist" json:"agent-initiator-persist"`
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The CopilotKey struct is defined twice. This duplicate definition is a copy-paste error that will cause a compilation failure and should be removed.

Comment on lines +85 to +86
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There is a syntax error here. The closing brace } on line 85 is incorrectly placed and indented, and the one on line 86 is extraneous. This will cause a compilation failure. The braces need to be adjusted to correctly close the nested if blocks.

Suggested change
}
}
}
}

info, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
if mkErr := os.MkdirAll(dir, 0o755); mkErr != nil {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The auth directory is being created with 0o755 permissions, which is world-readable and -executable. For a directory intended to store sensitive authentication tokens, more restrictive permissions like 0700 (read/write/execute for owner only) should be used to enhance security. Another part of the code (internal/auth/copilot/storage.go) already uses 0700, so this change would also make permissions consistent across the application.

Suggested change
if mkErr := os.MkdirAll(dir, 0o755); mkErr != nil {
if mkErr := os.MkdirAll(dir, 0700); mkErr != nil {

Comment on lines +110 to +128
# GitHub Copilot account configuration
# Note: Copilot uses OAuth device code authentication, NOT API keys or tokens.
# Do NOT paste your GitHub access token or Copilot bearer token here.
# Tokens are stored only in auth-dir JSON files, never in config.yaml.
#
# To authenticate:
# - CLI: run with -copilot-login flag
# - Web: use the /copilot-auth-url management endpoint
#
# After OAuth login, tokens are managed automatically and stored in auth-dir.
# The entries below only configure account type and optional proxy settings.
#copilot-api-key:
# - account-type: "individual" # Options: individual, business, enterprise
# proxy-url: "socks5://proxy.example.com:1080" # optional: proxy for Copilot requests

# # When set to true, this flag forces subsequent requests in a session (sharing the same prompt_cache_key)
# # to send the header "X-Initiator: agent" instead of "vscode". This mirrors VS Code's behavior for
# # long-running agent interactions and helps prevent hitting standard rate limits.
# agent-initiator-persist: true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This configuration block for GitHub Copilot is a duplicate of the one above (lines 90-108). Please remove this redundant block to avoid confusion for users configuring the service.

"client_id": GitHubClientID,
"scope": GitHubAppScopes,
}
bodyBytes, _ := json.Marshal(reqBody)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error returned by json.Marshal is being ignored. While it's unlikely to fail for a map[string]string, it is a best practice to handle all errors to prevent potential panics or unexpected behavior in production.

bodyBytes, err := json.Marshal(reqBody)
	if err != nil {
		return nil, fmt.Errorf("%w: failed to marshal request body: %v", ErrDeviceCodeFailed, err)
	}

"device_code": deviceCode,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
}
bodyBytes, _ := json.Marshal(reqBody)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error from json.Marshal is ignored. It's safer to handle this error to prevent unexpected behavior, even if a failure is unlikely with the current input.

bodyBytes, err := json.Marshal(reqBody)
	if err != nil {
		return "", fmt.Errorf("failed to marshal request body: %w", err)
	}

- Add Copilot section with agent-initiator-persist flag
- Add scanner buffer size configuration
- Add account type configuration option
- Add copilot types with account type validation
- Document configuration options in example config
- Add tests for util and copilot types
- Add device-code OAuth flow with GitHub token exchange
- Implement Copilot token acquisition and refresh logic
- Add account type handling (individual/business/enterprise)
- Add token persistence and storage management
- Add CLI login command (cliproxy-api copilot login)
- Register Copilot refresh handler in SDK
- Validate account_type with warning for invalid values
@jeffnash jeffnash force-pushed the feat/copilot-auth-flow branch from 9e7a1a4 to 0b7e617 Compare November 30, 2025 21:48
@luispater luispater closed this Dec 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants