feat(publisher): integrate GitHub Client ID into device flow authentication
This commit is contained in:
parent
5d9171bdcc
commit
4dbf80562b
2 changed files with 48 additions and 36 deletions
|
|
@ -20,8 +20,7 @@ type Authentication struct {
|
|||
// PublishRequest represents a request to publish a server to the registry
|
||||
type PublishRequest struct {
|
||||
ServerDetail `json:",inline"`
|
||||
Authentication Authentication `json:"-"` // Now provided via Authorization header
|
||||
AuthStatusToken string `json:"-"` // Used internally for device flows
|
||||
AuthStatusToken string `json:"-"` // Used internally for device flows
|
||||
}
|
||||
|
||||
// Repository represents a source code repository as defined in the spec
|
||||
|
|
@ -121,7 +120,7 @@ type Server struct {
|
|||
|
||||
// ServerDetail represents detailed server information as defined in the spec
|
||||
type ServerDetail struct {
|
||||
Server `json:",inline" bson:",inline"`
|
||||
Packages []Package `json:"packages,omitempty" bson:"packages,omitempty"`
|
||||
Remotes []Remote `json:"remotes,omitempty" bson:"remotes,omitempty"`
|
||||
Server `json:",inline" bson:",inline"`
|
||||
Packages []Package `json:"packages,omitempty" bson:"packages,omitempty"`
|
||||
Remotes []Remote `json:"remotes,omitempty" bson:"remotes,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@ const (
|
|||
// GitHub OAuth URLs
|
||||
GitHubDeviceCodeURL = "https://github.com/login/device/code"
|
||||
GitHubAccessTokenURL = "https://github.com/login/oauth/access_token"
|
||||
|
||||
// Environment variable for GitHub Client ID
|
||||
EnvGithubClientID = "MCP_REGISTRY_GITHUB_CLIENT_ID"
|
||||
)
|
||||
|
||||
// DeviceCodeResponse represents the response from GitHub's device code endpoint
|
||||
|
|
@ -40,6 +37,11 @@ type AccessTokenResponse struct {
|
|||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type ServerHealthResponse struct {
|
||||
Status string `json:"status"`
|
||||
GitHubClientId string `json:"github_client_id"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var registryURL string
|
||||
var mcpFilePath string
|
||||
|
|
@ -58,18 +60,31 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// Check for GitHub client ID in environment if we're going to need it for authentication
|
||||
if providedToken == "" && os.Getenv(EnvGithubClientID) == "" {
|
||||
fmt.Printf("Warning: Environment variable %s is not set. This is required for GitHub authentication.\n", EnvGithubClientID)
|
||||
fmt.Println("You can set it with: export " + EnvGithubClientID + "=your_github_client_id")
|
||||
fmt.Println("Or provide a token directly with the --token flag.")
|
||||
|
||||
// Only return if we'll need to do GitHub auth
|
||||
_, statErr := os.Stat(tokenFilePath)
|
||||
if forceLogin || os.IsNotExist(statErr) {
|
||||
return
|
||||
}
|
||||
// get the clientID from the server's health endpoint
|
||||
healthURL := registryURL + "/v0/health"
|
||||
resp, err := http.Get(healthURL)
|
||||
if err != nil {
|
||||
fmt.Printf("Error fetching health endpoint: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Printf("Health endpoint returned status %d: %s\n", resp.StatusCode, body)
|
||||
return
|
||||
}
|
||||
var healthResponse ServerHealthResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&healthResponse)
|
||||
if err != nil {
|
||||
fmt.Printf("Error decoding health response: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
if healthResponse.GitHubClientId == "" {
|
||||
fmt.Println("GitHub Client ID is not set in the server's health response.")
|
||||
return
|
||||
}
|
||||
|
||||
githubClientID := healthResponse.GitHubClientId
|
||||
|
||||
var token string
|
||||
|
||||
|
|
@ -80,7 +95,7 @@ func main() {
|
|||
// Check if token exists or force login is requested
|
||||
_, statErr := os.Stat(tokenFilePath)
|
||||
if forceLogin || os.IsNotExist(statErr) {
|
||||
err := performDeviceFlowLogin()
|
||||
err := performDeviceFlowLogin(githubClientID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to perform device flow login: %s\n", err.Error())
|
||||
return
|
||||
|
|
@ -113,15 +128,15 @@ func main() {
|
|||
fmt.Println("Successfully published to registry!")
|
||||
}
|
||||
|
||||
func performDeviceFlowLogin() error {
|
||||
// Check if the environment variable is set
|
||||
if os.Getenv(EnvGithubClientID) == "" {
|
||||
return fmt.Errorf("environment variable %s must be set for GitHub authentication", EnvGithubClientID)
|
||||
func performDeviceFlowLogin(githubClientID string) error {
|
||||
|
||||
if githubClientID == "" {
|
||||
return fmt.Errorf("GitHub Client ID is required for device flow login")
|
||||
}
|
||||
|
||||
// Device flow login logic using GitHub's device flow
|
||||
// First, request a device code
|
||||
deviceCode, userCode, verificationURI, err := requestDeviceCode()
|
||||
deviceCode, userCode, verificationURI, err := requestDeviceCode(githubClientID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error requesting device code: %w", err)
|
||||
}
|
||||
|
|
@ -134,7 +149,7 @@ func performDeviceFlowLogin() error {
|
|||
|
||||
// Poll for the token
|
||||
fmt.Println("Waiting for authorization...")
|
||||
token, err := pollForToken(deviceCode)
|
||||
token, err := pollForToken(deviceCode, githubClientID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error polling for token: %w", err)
|
||||
}
|
||||
|
|
@ -150,14 +165,13 @@ func performDeviceFlowLogin() error {
|
|||
}
|
||||
|
||||
// requestDeviceCode initiates the device authorization flow
|
||||
func requestDeviceCode() (string, string, string, error) {
|
||||
clientID := os.Getenv(EnvGithubClientID)
|
||||
if clientID == "" {
|
||||
return "", "", "", fmt.Errorf("environment variable %s is not set", EnvGithubClientID)
|
||||
func requestDeviceCode(githubClientID string) (string, string, string, error) {
|
||||
if githubClientID == "" {
|
||||
return "", "", "", fmt.Errorf("GitHub Client ID is required for device flow login")
|
||||
}
|
||||
|
||||
payload := map[string]string{
|
||||
"client_id": clientID,
|
||||
"client_id": githubClientID,
|
||||
"scope": "read:org read:user",
|
||||
}
|
||||
|
||||
|
|
@ -199,14 +213,13 @@ func requestDeviceCode() (string, string, string, error) {
|
|||
}
|
||||
|
||||
// pollForToken polls for access token after user completes authorization
|
||||
func pollForToken(deviceCode string) (string, error) {
|
||||
clientID := os.Getenv(EnvGithubClientID)
|
||||
if clientID == "" {
|
||||
return "", fmt.Errorf("environment variable %s is not set", EnvGithubClientID)
|
||||
func pollForToken(deviceCode, githubClientID string) (string, error) {
|
||||
if githubClientID == "" {
|
||||
return "", fmt.Errorf("GitHub Client ID is required for device flow login")
|
||||
}
|
||||
|
||||
payload := map[string]string{
|
||||
"client_id": clientID,
|
||||
"client_id": githubClientID,
|
||||
"device_code": deviceCode,
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue