Table of Contents
Preface
Who is the book for?
Are you new to Go and taking part in the Go Challenge? Some of the challenges might require you to create a web app in Go that uses OAuth to access a resource on say GitHub or Flickr etc. Don’t know how? If yes, then this eBook is for you and will show you how it’s done.
What will you learn?
In the end, you will understand and know how to access GitHub using Go.
Using Code Examples
All of the code in this book can be used pretty much anywhere and anyhow you please.
How to Contact Me
I can be reached via e-mail at golangchallenge@gmail.com. Please contact me if you have any questions, comments, kudos or criticism on the eBook. Constructive criticism is definitely appreciated; I want this eBook to get better through your feedback.
Thanks
Thanks for downloading and checking out this eBook. As part of the lean publishing philosophy, you’ll be able to interact with me as the eBook is completed. I’ll be able to change things, reorganize parts, and generally make a better eBook. I hope you enjoy.
Reference
I have based this free eBook, targetted towards Go newbies, on Krzysztof Kowalczyk’s excellent article.
Accessing GitHub using Go
GitHub, like many other sites, uses OAuth 2.0 protocol for authentication. OAuth2 is a protocol that lets external apps request authorization to private details in a user’s GitHub account without getting their password. Applications that need to read or write private information using the API on behalf of another user should use OAuth.
GitHub API
First, read thro’ the GitHub API documentation.
3-legged authorization
On a conceptual level it works in the following way:
- Client has signed up to the GitHub server and got his client credentials (also known as consumer key and secret) ahead of time
- User wants to give the client access to his protected resources on the server
- Client retrieves the temporary credentials (also known as “request token”) from the server
- Client redirects the resource owner to the server
- Resource owner grants the client access to his protected resources on the server
- Server redirects the user back to the client
- Client uses the temporary credentials to retrieve the token credentials (also known as “access token”) from the server
- Client uses the token credentials to access the protected resources on the server
Register your app
Log into your GitHub a/c and register your app at https://github.com/settings/applications. Click on the “Register new application” button. A registered OAuth application is assigned a unique Client ID and Client Secret. The Client Secret should not be shared. While registering, you can fill out every piece of information however you like, except the Authorization callback URL. This is easily the most important piece to setting up your application. It’s the callback URL that GitHub returns the user to, after successful authentication.
I have registered my app githuboa.go at my GitHub a/c.

Register an app
Let’s build our app githuboa.go
We shall be using oauth2 a Go package that contains a client implementation for the OAuth 2.0 specification.
It will be useful if you read the documentation for oauth2 and the OAuth documentation of GitHub.
Now let’s install oauth2 and the github packages.
1 go get golang.org/x/oauth2
2 go get github.com/google/go-github/github
The flow of our app githuboa.go
- the user is on your website and clicks “Log into GitHub” link
- you redirect the user to GitHub’s authorization page. In that url you specify desired access level and a random secret
- the user authorizes your app by clicking on a link
- GitHub redirects to a callback url on your website (which you provided when registering the app with GitHub)
- in the url handler, extract “secret” and “code” arguments
- you have to check that the secret is the same as the one you sent to GitHub (security measure that prevents forgery)
- you call another GitHub url to exchange code for access token
Access token is what you use to authenticate your API calls and allows you to make requests to the API on a behalf of a user.
Code: Create an OAuth config object
1 package main
2
3 import (
4 "golang.org/x/oauth2"
5 githuboauth "golang.org/x/oauth2/github"
6 )
7
8 var (
9 // You must register the app at https://github.com/settings/applications
10 // Set callback to http://127.0.0.1:7000/githuboa_cb
11 // Set ClientId and ClientSecret to the values you got
12 // after registering your app
13 oauthConf = &oauth2.Config{
14 ClientID: "", // please enter your value
15 ClientSecret: "", // please enter your value
16 // Comma separated list of scopes
17 // select level of access you want https://developer.github.com/v3/oauth/#scopes
18 Scopes: []string{"user:email"},
19 Endpoint: githuboauth.Endpoint,
20 }
21 // An unguessable random string. It is used to protect against
22 // cross-site request forgery attacks
23 oauthStateString = "arandomstring"
24 )
25
26 func main() {
27 }
Config used above describes a typical 3-legged OAuth2 flow, with both the client application information and the server’s endpoint URLs. The Endpoint used above is defined here.
Your main html page
1 package main
2
3 import (
4 "fmt"
5 "net/http"
6
7 "golang.org/x/oauth2"
8 githuboauth "golang.org/x/oauth2/github"
9 )
10
11 const htmlIndex = `<html><body><p>Well, hello there!</p>
12 <p>We're going to now talk to the GitHub API. Ready?</p>
13 <p>Log into <a href="/login">GitHub</a></p>
14 </body></html>
15 `
16
17 var (
18 // You must register the app at https://github.com/settings/applications
19 // Set callback to http://127.0.0.1:7000/githuboa_cb
20 // Set ClientId and ClientSecret to the values you got
21 // after registering your app
22 oauthConf = &oauth2.Config{
23 ClientID: "", // please enter your value
24 ClientSecret: "", // please enter your value
25 // Comma separated list of scopes
26 // select level of access you want https://developer.github.com/v3/oauth/#scopes
27 Scopes: []string{"user:email"},
28 Endpoint: githuboauth.Endpoint,
29 }
30 // An unguessable random string. It is used to protect against
31 // cross-site request forgery attacks
32 oauthStateString = "arandomstring"
33 )
34
35 func main() {
36 http.HandleFunc("/", handleMain)
37
38 fmt.Print("Started running on http://127.0.0.1:7000\n")
39 fmt.Println(http.ListenAndServe(":7000", nil))
40 }
41
42 func handleMain(w http.ResponseWriter, r *http.Request) {
43 w.Header().Set("Content-Type", "text/html; charset=utf-8")
44 w.WriteHeader(http.StatusOK)
45 w.Write([]byte(htmlIndex))
46 }
Refer to the ResponseWriter interface where the functions Header(), WriteHeader() and Write() are mentioned. Check the details of the Set method.
Login to GitHub
Once the user clicks on the “Log into GitHub” link the handler for /login url, redirects to GitHub’s authorization page. GitHub will show the authorization page to your user. If the user authorizes your app, GitHub will re-direct to OAuth callback. Here’s how you can turn it into a token, token into http client and use that client to list GitHub information about the user.
1 package main
2
3 import (
4 "fmt"
5 "net/http"
6
7 "golang.org/x/oauth2"
8 githuboauth "golang.org/x/oauth2/github"
9 "html/template"
10 )
11
12 const htmlIndex = `<html><body><p>Well, hello there!</p>
13 <p>We're going to now talk to the GitHub API. Ready?</p>
14 <p>Log into <a href="/login">GitHub</a></p>
15 </body></html>
16 `
17
18 var userInfoTemplate = template.Must(template.New("").Parse(`
19 <html><body>
20 <p>This app is now authenticated to access your GitHub user info.</p>
21 <p>User details are:</p><p>
22 {{.}}
23 </p>
24 <p>That's it!</p>
25 </body></html>
26 `))
27
28 var (
29 // You must register the app at https://github.com/settings/applications
30 // Set callback to http://127.0.0.1:7000/githuboa_cb
31 // Set ClientId and ClientSecret to the values you got
32 // after registering your app
33 oauthConf = &oauth2.Config{
34 ClientID: "", // please enter your value
35 ClientSecret: "", // please enter your value
36 // Comma separated list of scopes
37 // select level of access you want https://developer.github.com/v3/oauth/#scopes
38 Scopes: []string{"user:email"},
39 Endpoint: githuboauth.Endpoint,
40 }
41 // An unguessable random string. It is used to protect against
42 // cross-site request forgery attacks
43 oauthStateString = "arandomstring"
44 )
45
46 func main() {
47 http.HandleFunc("/", handleMain)
48 http.HandleFunc("/login", handleGitHubLogin)
49 http.HandleFunc("/githuboa_cb", handleGitHubCallback)
50
51 fmt.Print("Started running on http://127.0.0.1:7000\n")
52 fmt.Println(http.ListenAndServe(":7000", nil))
53 }
54
55 func handleMain(w http.ResponseWriter, r *http.Request) {
56 w.Header().Set("Content-Type", "text/html; charset=utf-8")
57 w.WriteHeader(http.StatusOK)
58 w.Write([]byte(htmlIndex))
59 }
60
61 // /login
62 func handleGitHubLogin(w http.ResponseWriter, r *http.Request) {
63 url := oauthConf.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
64 http.Redirect(w, r, url, http.StatusTemporaryRedirect)
65 }
66
67 // githuboa_cb. Called by github after authorization is granted
68 func handleGitHubCallback(w http.ResponseWriter, r *http.Request) {
69 // If the user accepts your request, GitHub redirects back
70 // to your site with a temporary code in a code parameter
71 // as well as the state you provided in the previous step
72 // in a state parameter. If the states don't match, the
73 // request has been created by a third party and the process
74 // should be aborted.
75 state := r.FormValue("state")
76 if state != oauthStateString {
77 fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
78 http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
79 return
80 }
81
82 code := r.FormValue("code")
83
84 // On success, exchange this for an access token
85 token, err := oauthConf.Exchange(oauth2.NoContext, code)
86 if err != nil {
87 fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err)
88 http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
89 return
90 }
91
92 oauthClient := oauthConf.Client(oauth2.NoContext, token)
93
94 // https://godoc.org/github.com/google/go-github/github
95 client := github.NewClient(oauthClient)
96
97 user, _, err := client.Users.Get("")
98 if err != nil {
99 fmt.Printf("client.Users.Get() failed with '%s'\n", err)
100 http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
101 return
102 }
103
104 buf := []string{"GitHub login id: ", *user.Login, "| GitHub email id: ", *user.Email}
105
106 userInfoTemplate.Execute(w, buf)
107 }
Read the details of AuthCodeURL. AuthCodeURL returns a URL to OAuth 2.0 provider’s consent page that asks for permissions for the required scopes explicitly. State is a token to protect the user from CSRF attacks. You must always provide a non-zero string. Opts may include AccessTypeOnline or AccessTypeOffline, as well as ApprovalForce.
The Exchange method above converts an authorization code into a token. It is used after a resource provider redirects the user back to the Redirect URI (the URL obtained from AuthCodeURL). The HTTP client to use is derived from the context. If a client is not provided via the context, http.DefaultClient is used. The code will be in the *http.Request.FormValue("code"). Before calling Exchange, be sure to validate FormValue("state").
The Client method above returns an HTTP client using the provided token. The token will auto-refresh as necessary. The underlying HTTP transport will be obtained using the provided context. The returned client and its Transport should not be modified.
client := github.NewClient(oauthClient) constructs a new GitHub client, then uses the various services on the client to access different parts of the GitHub API.
user, _, err := client.Users.Get("") - see details here fetches a user. Passing the empty string will fetch the authenticated user.
You can get all the details about a User.
Run the above program on http://127.0.0.1:7000.
That’s it!
You can download the entire program from here.
Exercise
How about writing an app that accesses Flickr using OAuth?

Flickr OAuth