pub struct CopilotAuthHandler { /* private fields */ }Expand description
Handler for GitHub Copilot authentication.
Manages the complete authentication lifecycle including:
- Device code flow for initial authentication
- Token caching and validation
- Automatic token refresh
- Silent authentication attempts
§Architecture
The handler implements a hierarchical token resolution strategy:
- Cached Copilot Token: Check local cache for valid token
- Environment Variable: Check
COPILOT_API_KEY - Cached Access Token: Use cached GitHub token to fetch new Copilot token
- Interactive Flow: Prompt user via device code flow
§Thread Safety
The handler is thread-safe and can be cloned cheaply. Authentication operations are protected by a global lock to prevent concurrent flows.
§Example
use std::sync::Arc;
use reqwest_middleware::ClientWithMiddleware;
use bamboo_agent::agent::llm::providers::copilot::auth::handler::CopilotAuthHandler;
async fn example() -> anyhow::Result<()> {
let client = Arc::new(ClientWithMiddleware::new(/* ... */));
let handler = CopilotAuthHandler::new(
client,
std::path::PathBuf::from("~/.bamboo"),
false,
);
// Will use cached token or trigger device flow
let token = handler.get_token().await?;
println!("Got token: {}", token);
Ok(())
}Implementations§
Source§impl CopilotAuthHandler
impl CopilotAuthHandler
Sourcepub fn new(
client: Arc<ClientWithMiddleware>,
app_data_dir: PathBuf,
headless_auth: bool,
) -> Self
pub fn new( client: Arc<ClientWithMiddleware>, app_data_dir: PathBuf, headless_auth: bool, ) -> Self
Creates a new authentication handler.
§Arguments
client- HTTP client with middleware for retry logic and error handlingapp_data_dir- Directory for storing cached tokens (.tokenand.copilot_token.json)headless_auth- Whether to print authentication instructions to console. Set totruefor CLI applications,falsefor GUI applications.
§Example
use std::sync::Arc;
use reqwest_middleware::ClientWithMiddleware;
use bamboo_agent::agent::llm::providers::copilot::auth::handler::CopilotAuthHandler;
let client = Arc::new(ClientWithMiddleware::new(/* ... */));
let handler = CopilotAuthHandler::new(
client,
std::path::PathBuf::from("~/.bamboo"),
true, // CLI mode
);Sourcepub fn app_data_dir(&self) -> &PathBuf
pub fn app_data_dir(&self) -> &PathBuf
Returns the application data directory path.
This directory contains cached tokens:
.token: GitHub OAuth access token.copilot_token.json: Copilot API configuration
Sourcepub async fn authenticate(&self) -> Result<String>
pub async fn authenticate(&self) -> Result<String>
Performs authentication and returns an access token.
This is the primary entry point for authentication. It will attempt silent authentication first, then fall back to interactive device flow if necessary.
§Returns
A Copilot API token on success.
§Errors
Returns an error if:
- All authentication methods fail
- User denies authorization during device flow
- Device code expires before authorization
- Network errors occur
Sourcepub async fn ensure_authenticated(&self) -> Result<()>
pub async fn ensure_authenticated(&self) -> Result<()>
Ensures the handler is authenticated, without returning the token.
This is useful for pre-authenticating or verifying credentials without needing the actual token value.
§Example
// Pre-authenticate before starting the application
handler.ensure_authenticated().await?;
println!("Authentication successful!");Sourcepub async fn get_token(&self) -> Result<String>
pub async fn get_token(&self) -> Result<String>
Gets the current access token, authenticating if necessary.
Alias for authenticate.
Sourcepub async fn get_chat_token(&self) -> Result<String>
pub async fn get_chat_token(&self) -> Result<String>
Gets a chat token, using cached credentials or triggering device flow.
This method attempts silent authentication first, then falls back to interactive device code flow if necessary.
§Silent Authentication Priority
- Check cached Copilot token (
.copilot_token.json) - Check
COPILOT_API_KEYenvironment variable - Check cached GitHub access token (
.token) and exchange for new Copilot token
§Thread Safety
This method acquires a global lock to prevent concurrent authentication flows. Only one authentication attempt can be in progress at a time.
§Returns
A valid Copilot API token.
§Errors
Returns an error if all authentication methods fail.
Sourcepub async fn start_authentication(&self) -> Result<DeviceCodeResponse>
pub async fn start_authentication(&self) -> Result<DeviceCodeResponse>
Starts the authentication process by getting a device code.
This method initiates the OAuth device flow by requesting a device
code from GitHub. If headless_auth is false, it prints user-friendly
instructions to the console.
§Display Behavior
- Headless mode (
headless_auth = true): Prints full instructions with ASCII art - GUI mode (
headless_auth = false): Returns device code for custom UI
§Returns
A DeviceCodeResponse with the device code and verification information.
§Example
let device_code = handler.start_authentication().await?;
// In GUI mode, display these values to the user
println!("URL: {}", device_code.verification_uri);
println!("Code: {}", device_code.user_code);Sourcepub async fn complete_authentication(
&self,
device_code: &DeviceCodeResponse,
) -> Result<CopilotConfig>
pub async fn complete_authentication( &self, device_code: &DeviceCodeResponse, ) -> Result<CopilotConfig>
Completes authentication by polling for access token and exchanging for Copilot token.
This method completes the OAuth flow by:
- Polling GitHub for the access token (waits for user authorization)
- Exchanging the access token for a Copilot API token
- Caching both tokens to disk for future use
§Arguments
device_code- Device code response fromstart_authentication
§Returns
A CopilotConfig containing the Copilot API token and configuration.
§Side Effects
Writes the following files to app_data_dir:
.token: GitHub OAuth access token.copilot_token.json: Copilot API configuration
§Errors
Returns an error if:
- User denies authorization
- Device code expires before authorization
- Token exchange fails
- File writing fails
Sourcepub async fn try_get_chat_token_silent(&self) -> Result<Option<String>>
pub async fn try_get_chat_token_silent(&self) -> Result<Option<String>>
Attempts silent authentication without user interaction.
This method tries multiple authentication strategies in order of preference, all of which can succeed without requiring user interaction:
- Cached Copilot Token: Load from
.copilot_token.jsonif still valid - Environment Variable: Check
COPILOT_API_KEY - Cached Access Token: Use cached GitHub token to fetch new Copilot token
§Returns
Ok(Some(token))if silent authentication succeededOk(None)if silent authentication is not possible (triggers interactive flow)Err(...)if an unexpected error occurred
§Side Effects
If using a cached access token, this method will:
- Fetch a new Copilot token from GitHub
- Cache the new Copilot token to
.copilot_token.json - Remove the cached access token if it’s invalid
§Example
match handler.try_get_chat_token_silent().await? {
Some(token) => println!("Got token silently: {}", token),
None => println!("Need interactive authentication"),
}Sourcepub async fn force_refresh_chat_token(&self) -> Result<Option<String>>
pub async fn force_refresh_chat_token(&self) -> Result<Option<String>>
Force refresh a Copilot token using the cached GitHub OAuth access token.
This bypasses the .copilot_token.json cache and is useful when the cached
Copilot token is rejected early (e.g. revoked) even if it hasn’t reached
expires_at yet.
Returns:
Ok(Some(token))if the refresh succeededOk(None)if no cached access token exists
Trait Implementations§
Source§impl Clone for CopilotAuthHandler
impl Clone for CopilotAuthHandler
Source§fn clone(&self) -> CopilotAuthHandler
fn clone(&self) -> CopilotAuthHandler
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more