Custom HTTP client with golang.org/x/oauth2 and GitHub Go SDK

In this post, we are going to discuss an implementation detail about how we can use a custom HTTP client to use the Go GitHub SDK and the https://pkg.go.dev/golang.org/x/oauth2 package.

Introduction

Let’s create an example *oauth2.Config for communicating with GitHub.com oauth provider:

oauthConf = &oauth2.Config{
	ClientID:     getEnvironValue("CLIENT_ID"),
	ClientSecret: getEnvironValue("CLIENT_SECRET"),
	Scopes:       []string{"repo", "user"}, // see the project desrciption for understandng why we need full scopes here
	Endpoint:     github.Endpoint,
}

One key step as part of the OAuth authorization process is to call the Exchange(..) method to obtain an access token:

t, err  = oauthConf.Exchange(ctx, ..)

The above method call requires a HTTP client to communicate.

The golang.org/x/oauth2 library looks for a HTTP client inside the context, ctx and if it doesn’t find it usingctx.Value(HTTPClient), it creates a http.DefaultClient:

type ContextKey struct{}
var HTTPClient ContextKey

if ctx != nil {
        if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
                return hc
        }
}
return http.DefaultClient

You can find the complete code here.

Using a custom HTTP client with golang.org/x/oauth2

To use a custom HTTP client, we create a new context.Context value with oauth2.HTTPClient and then pass that as the context to the Exchange() method:

customHttpClient := &http.Client{}
ctx := context.WithValue(ctx, oauth2.HTTPClient, customHttpClient)
t, err := oauthConf.Exchange(ctx, ...)

Now, the above code finds the configured HTTP client, customHttpClient and uses that to make the HTTP requests.

Using the custom HTTP client with Go Github SDK

Now, to use the same custom HTTP client, customHttpClient with the Go GitHub SDK and using the access token obtained from the Exchange() method call above, all we need to do is continue using the same context above:

// ctx is the same as we created above with the
// value containing the custom HTTP client
ts := oauth2.StaticTokenSource(
        &oauth2.Token{AccessToken: t.Token},
)
tc := oauth2.NewClient(ctx, ts)
ghClient := github.NewClient(tc)

Then, we can use the GitHub client as usual:

u, _, err := ghClient.Users.Get(ctx, "")

Summary

Configuring a custom HTTP client for use with golang.org/x/oauth2 also automatically ensures that the same client is used for accessing the GitHub API via the GitHub Go SDK. This post illustrates how that works.

If you are wondering why you may want to use a custom HTTP client, it can be useful when writing tests where you don’t want to interact with the GitHub.com oauth2 provider or the GitHub.com API. You can learn how in my previous blog post, Writing HTTP client middleware in Go.

References