Github OAuth2 Callback in Go

November 22nd 2018

I've been thinking about how to handle blog subscriptions lately. I currently use Mailchimp - and it's been great. But I wanted to do something different, less expensive and more programming-centered.

I also want to be able to personalize the subscriptions based on the interests of my readers.

Since I run a programming blog, a Subscribe with Github button seemed like a fine choice. I can figure out the languages and tools my readers use and recommend articles that they might enjoy. Plus it seemed like fun!

Want to read more posts like this? Subscribe to the blog! Sign in with Github

Here's an example of how to setup a basic Github Authentication Callback server in Go!

package main

import (
    "context"
    "errors"
    "fmt"
    "github.com/google/go-github/github"
    "golang.org/x/oauth2"
    "log"
    "net/http"
)

const (
    CLIENT_ID     = "YOUR_CLIENT_ID"
    CLIENT_SECRET = "YOUR_CLIENT_SECRET"
    REDIRECT_URL  = "https://SOME_REDIRECT_URL.com"
    AUTHORIZE_URL = "https://github.com/login/oauth/authorize"
    TOKEN_URL     = "https://github.com/login/oauth/access_token"
)

func main() {
    http.HandleFunc("/auth", HandleCallback)
    log.Fatal(http.ListenAndServe(":3000", nil))
}

func HandleCallback(w http.ResponseWriter, r *http.Request) {
    oauthConfig := &oauth2.Config{
        ClientID:     CLIENT_ID,
        ClientSecret: CLIENT_SECRET,
        Endpoint: oauth2.Endpoint{
            AuthURL:  AUTHORIZE_URL,
            TokenURL: TOKEN_URL,
        },
        RedirectURL: REDIRECT_URL,
        Scopes:      getScopes(),
    }

    tkn, err := oauthConfig.Exchange(oauth2.NoContext, r.URL.Query().Get("code"))
    if err != nil {
        // Handle Error
    }

    if !tkn.Valid() {
        // Handle error
    }

    client := github.NewClient(oauthConfig.Client(oauth2.NoContext, tkn))
    user, _, err := client.Users.Get(context.Background(), "")
    if err != nil {
        // Handle Error
    }

    fmt.Printf("Name: %s\n", *user.Name)     // Name: Your Name
    http.Redirect(w, r, REDIRECT_URL, http.StatusPermanentRedirect)
}

func getScopes() []string {
    return []string{"user:email"}
}

Gotchas

It'd probably be best to, instead of using the constants CLIENT_ID and CLIENT_SECRET, use something like os.Getenv('CLIENT_SECRET') and set them as environment variables. It's always best to avoid putting your secret keys in source control so that they don't get compromised if someone gets access to your codebase.

It'd also be a good idea to create a user session and store the token in it before redirecting. That would allow you to persist the user's logged-in state.

Want to read more posts like this? Subscribe to the blog! Sign in with Github