Arcanum.Auth.Copilot (arcanum v0.1.0)

Copy Markdown View Source

GitHub Copilot OAuth device code authentication.

Implements the OAuth 2.0 Device Authorization Grant (RFC 8628) to obtain a GitHub access token that works directly against api.githubcopilot.com.

Flow

1. `start_device_flow/0`   returns {verification_uri, user_code, device_code}
2. User visits verification_uri and enters user_code
3. `poll_for_token/1`      polls GitHub until user authorizes
4. The access_token is used directly as Bearer token against Copilot API

No secondary token exchange is needed — the GitHub OAuth token works as-is.

Summary

Functions

Returns the Copilot API base URL.

Returns the headers required for Copilot API requests.

Polls GitHub for the access token after the user has entered the device code.

Makes a single poll request to GitHub's OAuth token endpoint.

Starts the OAuth device code flow.

Types

device_flow()

@type device_flow() :: %{
  device_code: String.t(),
  user_code: String.t(),
  verification_uri: String.t(),
  interval: pos_integer()
}

Functions

base_url(domain)

@spec base_url(String.t() | nil) :: String.t()

Returns the Copilot API base URL.

copilot_headers(access_token)

@spec copilot_headers(String.t()) :: [{String.t(), String.t()}]

Returns the headers required for Copilot API requests.

poll_for_token(device_flow, enterprise_domain \\ nil)

@spec poll_for_token(device_flow(), String.t() | nil) ::
  {:ok, String.t()} | {:error, term()}

Polls GitHub for the access token after the user has entered the device code.

Blocks the calling process, polling every interval + 3 seconds (per RFC 8628). Returns {:ok, access_token} when the user authorizes, or {:error, reason} on failure.

Accepts an optional enterprise_domain for GitHub Enterprise.

poll_once(device_code, enterprise_domain \\ nil)

@spec poll_once(String.t(), String.t() | nil) ::
  {:ok, String.t()} | {:pending, atom()} | {:error, term()}

Makes a single poll request to GitHub's OAuth token endpoint.

Returns:

  • {:ok, access_token} — user authorized, token obtained
  • {:pending, :authorization_pending} — user hasn't authorized yet
  • {:pending, :slow_down} — polling too fast, caller should increase interval
  • {:error, reason} — terminal failure (expired, denied, network error)

Intended for external polling loops (e.g. Oban jobs) that manage their own scheduling instead of blocking a process.

start_device_flow(enterprise_domain \\ nil)

@spec start_device_flow(String.t() | nil) :: {:ok, device_flow()} | {:error, term()}

Starts the OAuth device code flow.

Returns a map with device_code, user_code, verification_uri, and interval. The caller should display user_code and verification_uri to the user, then call poll_for_token/1 with the returned map.

Accepts an optional domain for GitHub Enterprise (e.g. "company.ghe.com").