diff --git a/api/client.go b/api/client.go index 2fc72aaa6..e1fef7219 100644 --- a/api/client.go +++ b/api/client.go @@ -7,6 +7,7 @@ import ( "io" "io/ioutil" "net/http" + "os" "regexp" "strings" @@ -63,6 +64,46 @@ func ReplaceTripper(tr http.RoundTripper) ClientOption { } } +var issuedScopesWarning bool + +// CheckScopes checks whether an OAuth scope is present in a response +func CheckScopes(wantedScope string) ClientOption { + return func(tr http.RoundTripper) http.RoundTripper { + return &funcTripper{roundTrip: func(req *http.Request) (*http.Response, error) { + res, err := tr.RoundTrip(req) + if err != nil || issuedScopesWarning { + return res, err + } + + isApp := res.Header.Get("X-Oauth-Client-Id") != "" + hasScopes := strings.Split(res.Header.Get("X-Oauth-Scopes"), ",") + + hasWanted := false + for _, s := range hasScopes { + if wantedScope == strings.TrimSpace(s) { + hasWanted = true + break + } + } + + if !hasWanted { + fmt.Fprintln(os.Stderr, "Warning: gh now requires the `read:org` OAuth scope.") + // TODO: offer to take the person through the authentication flow again? + // TODO: retry the original request if it was a read? + if isApp { + fmt.Fprintln(os.Stderr, "To re-authenticate, please `rm ~/.config/gh/config.yml` and try again.") + } else { + // the person has pasted a Personal Access Token + fmt.Fprintln(os.Stderr, "Re-generate your token in `rm ~/.config/gh/config.yml` and try again.") + } + issuedScopesWarning = true + } + + return res, nil + }} + } +} + type funcTripper struct { roundTrip func(*http.Request) (*http.Response, error) } diff --git a/command/root.go b/command/root.go index fa65d7e05..6eafd01d4 100644 --- a/command/root.go +++ b/command/root.go @@ -131,6 +131,7 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { opts = append(opts, apiVerboseLog()) } opts = append(opts, + api.CheckScopes("read:org"), api.AddHeader("Authorization", fmt.Sprintf("token %s", token)), api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)), // antiope-preview: Checks