Skip to content

Commit ec7bd6f

Browse files
fix(oauth): require deletion scope opt-in
Keep delete_repo in protected-resource discovery for step-up authorization while excluding it from the default stdio OAuth grant. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4b04480c-c2e9-483e-9b0f-34830b76a2f8
1 parent cceb5cf commit ec7bd6f

3 files changed

Lines changed: 30 additions & 11 deletions

File tree

cmd/github-mcp-server/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ var (
128128
}
129129

130130
// When no static token is provided, log in via OAuth using the given
131-
// client. The requested scopes default to the full supported set
132-
// (which filters out no tools); an explicit, narrower --oauth-scopes
133-
// both narrows the grant and hides tools needing other scopes.
131+
// client. The requested scopes default to the standard set; high-risk
132+
// scopes such as delete_repo require explicit --oauth-scopes opt-in.
133+
// The requested set also filters tools needing other scopes.
134134
if token == "" && !appAuthRequested {
135-
scopes := ghoauth.SupportedScopes
135+
scopes := ghoauth.DefaultScopes
136136
if viper.IsSet("oauth-scopes") {
137137
if err := viper.UnmarshalKey("oauth-scopes", &scopes); err != nil {
138138
return fmt.Errorf("failed to unmarshal oauth-scopes: %w", err)

pkg/http/oauth/oauth.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,26 @@ const (
1919
OAuthProtectedResourcePrefix = "/.well-known/oauth-protected-resource"
2020
)
2121

22-
// SupportedScopes lists every OAuth scope that an MCP tool may require. It is the
23-
// source of truth in two places: HTTP mode advertises it as scopes_supported in
24-
// the protected-resource metadata, and stdio OAuth login requests it by default
25-
// and then filters the exposed tools to the granted scopes. A tool whose required
26-
// scope is absent here is therefore hidden under default OAuth even though a PAT
27-
// carrying that scope would expose it, so keep this list in sync with tool scope
28-
// requirements when scopes change.
22+
// DefaultScopes are requested by stdio OAuth unless the operator explicitly
23+
// supplies --oauth-scopes. High-risk scopes such as delete_repo require opt-in.
24+
var DefaultScopes = []string{
25+
"repo",
26+
"read:org",
27+
"read:user",
28+
"user:email",
29+
"read:packages",
30+
"write:packages",
31+
"read:project",
32+
"project",
33+
"gist",
34+
"notifications",
35+
"workflow",
36+
"codespace",
37+
}
38+
39+
// SupportedScopes lists every OAuth scope that an MCP tool may require. HTTP
40+
// protected-resource metadata advertises this full set so clients can step up
41+
// authorization for tools excluded from the default grant.
2942
var SupportedScopes = []string{
3043
"repo",
3144
"delete_repo",

pkg/http/oauth/oauth_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,12 @@ func TestSupportedScopes(t *testing.T) {
586586
assert.Equal(t, expectedScopes, SupportedScopes)
587587
}
588588

589+
func TestDefaultScopesRequiresExplicitDeleteRepoOptIn(t *testing.T) {
590+
assert.Contains(t, SupportedScopes, "delete_repo")
591+
assert.NotContains(t, DefaultScopes, "delete_repo")
592+
assert.Contains(t, DefaultScopes, "repo")
593+
}
594+
589595
func TestProtectedResourceResponseFormat(t *testing.T) {
590596
t.Parallel()
591597

0 commit comments

Comments
 (0)