├── .gitignore
├── config.json.example
├── static
└── css
│ └── style.css
├── README.md
├── templates
├── layout.html
└── home.html
├── main.go
└── handlers.go
/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 | tmp/
--------------------------------------------------------------------------------
/config.json.example:
--------------------------------------------------------------------------------
1 | {
2 | "clientID": "asdf9823u9f8u23",
3 | "clientSecret": "asdkjfaskdjflaksj",
4 | "serverSecret": "asdifj9182u98u98efua"
5 | }
--------------------------------------------------------------------------------
/static/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Lato', sans-serif;
3 | }
4 |
5 | .current-user .avatar-thumbnail {
6 | width: 100px;
7 | height: 100px;
8 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Golang GitHub OAuth example
2 |
3 | Example code for GitHub OAuth applications.
4 |
5 | ## Instructions
6 | - `config.json.example` -> `config.json`
7 | - ensure your callback URL is sent to `/auth-callback`
--------------------------------------------------------------------------------
/templates/layout.html:
--------------------------------------------------------------------------------
1 | {{ define "base" }}
2 |
3 |
4 |
5 |
6 | {{template "title" . }}
7 |
8 |
9 |
10 | golang-github-oauth-example
11 |
12 |
13 | {{ template "content" . }}
14 |
15 |
16 |
17 | {{ end }}
--------------------------------------------------------------------------------
/templates/home.html:
--------------------------------------------------------------------------------
1 | {{ define "title"}}Example{{ end }}
2 | {{ define "content" }}
3 |
4 | - Start
5 | {{ if .github_user }}
6 | - Logout
7 | {{ end }}
8 |
9 |
10 | {{ if .github_user }}
11 |
Logged in as {{ .github_user.Name }}
12 |

13 |
Data
14 |
15 | {{ range $key, $value := .github_user_map }}
16 | - {{ $key }}: {{ $value }}
17 | {{ end }}
18 |
19 |
20 | {{ else }}
21 |
Not logged in as anyone
22 | {{ end }}
23 |
24 |
25 |
26 | {{ end }}
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "html/template"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | "github.com/gorilla/mux"
12 | "github.com/gorilla/sessions"
13 | "golang.org/x/oauth2"
14 | )
15 |
16 | const (
17 | defaultLayout = "templates/layout.html"
18 | templateDir = "templates/"
19 |
20 | defaultConfigFile = "config.json"
21 |
22 | githubAuthorizeUrl = "https://github.com/login/oauth/authorize"
23 | githubTokenUrl = "https://github.com/login/oauth/access_token"
24 | redirectUrl = ""
25 | )
26 |
27 | type Config struct {
28 | ClientSecret string `json:"clientSecret"`
29 | ClientID string `json:"clientID"`
30 |
31 | ServerSecret string `json:"serverSecret"`
32 | }
33 |
34 | var (
35 | cfg *Config
36 | oauthCfg *oauth2.Config
37 | store *sessions.CookieStore
38 |
39 | // scopes
40 | scopes = []string{"repo"}
41 |
42 | tmpls = map[string]*template.Template{}
43 | )
44 |
45 | func loadConfig(file string) (*Config, error) {
46 | var config Config
47 |
48 | b, err := ioutil.ReadFile(file)
49 | if err != nil {
50 | return nil, err
51 | }
52 |
53 | if err = json.Unmarshal(b, &config); err != nil {
54 | return nil, err
55 | }
56 |
57 | return &config, nil
58 | }
59 |
60 | func main() {
61 | tmpls["home.html"] = template.Must(template.ParseFiles(templateDir+"home.html", defaultLayout))
62 |
63 | var err error
64 | cfg, err = loadConfig(defaultConfigFile)
65 | if err != nil {
66 | log.Fatal(err)
67 | }
68 |
69 | store = sessions.NewCookieStore([]byte(cfg.ServerSecret))
70 |
71 | oauthCfg = &oauth2.Config{
72 | ClientID: cfg.ClientID,
73 | ClientSecret: cfg.ClientSecret,
74 | Endpoint: oauth2.Endpoint{
75 | AuthURL: githubAuthorizeUrl,
76 | TokenURL: githubTokenUrl,
77 | },
78 | RedirectURL: redirectUrl,
79 | Scopes: scopes,
80 | }
81 |
82 | r := mux.NewRouter()
83 | r.HandleFunc("/", HomeHandler)
84 | r.HandleFunc("/start", StartHandler)
85 | r.HandleFunc("/auth-callback", AuthCallbackHandler)
86 | r.HandleFunc("/destroy-session", SessionDestroyHandler)
87 |
88 | http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
89 | http.Handle("/", r)
90 |
91 | listenAddr := ":8080"
92 |
93 | envPort := os.Getenv("PORT")
94 | if len(envPort) > 0 {
95 | listenAddr = ":" + envPort
96 | }
97 |
98 | log.Printf("attempting listen on %s", listenAddr)
99 | log.Fatalln(http.ListenAndServe(listenAddr, nil))
100 | }
101 |
--------------------------------------------------------------------------------
/handlers.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "encoding/base64"
7 | "encoding/gob"
8 | "fmt"
9 | "net/http"
10 |
11 | "github.com/google/go-github/github"
12 | "github.com/mitchellh/mapstructure"
13 | "golang.org/x/oauth2"
14 | )
15 |
16 | const sessionStoreKey = "sess"
17 |
18 | func init() {
19 | gob.Register(&oauth2.Token{})
20 | }
21 |
22 | func HomeHandler(w http.ResponseWriter, r *http.Request) {
23 | session, err := store.Get(r, sessionStoreKey)
24 | if err != nil {
25 | fmt.Fprintln(w, err)
26 | return
27 | }
28 |
29 | renderData := map[string]interface{}{}
30 | if accessToken, ok := session.Values["githubAccessToken"].(*oauth2.Token); ok {
31 | client := github.NewClient(oauthCfg.Client(oauth2.NoContext, accessToken))
32 |
33 | user, _, err := client.Users.Get(context.Background(), "")
34 | if err != nil {
35 | fmt.Fprintln(w, err)
36 | return
37 | }
38 |
39 | renderData["github_user"] = user
40 |
41 | var userMap map[string]interface{}
42 | mapstructure.Decode(user, &userMap)
43 | renderData["github_user_map"] = userMap
44 | }
45 |
46 | tmpls["home.html"].ExecuteTemplate(w, "base", renderData)
47 | }
48 |
49 | func StartHandler(w http.ResponseWriter, r *http.Request) {
50 | b := make([]byte, 16)
51 | rand.Read(b)
52 |
53 | state := base64.URLEncoding.EncodeToString(b)
54 |
55 | session, _ := store.Get(r, sessionStoreKey)
56 | session.Values["state"] = state
57 | session.Save(r, w)
58 |
59 | url := oauthCfg.AuthCodeURL(state)
60 | http.Redirect(w, r, url, 302)
61 | }
62 |
63 | func AuthCallbackHandler(w http.ResponseWriter, r *http.Request) {
64 | session, err := store.Get(r, sessionStoreKey)
65 | if err != nil {
66 | fmt.Fprintln(w, "aborted")
67 | return
68 | }
69 |
70 | if r.URL.Query().Get("state") != session.Values["state"] {
71 | fmt.Fprintln(w, "no state match; possible csrf OR cookies not enabled")
72 | return
73 | }
74 |
75 | token, err := oauthCfg.Exchange(oauth2.NoContext, r.URL.Query().Get("code"))
76 | if err != nil {
77 | fmt.Fprintln(w, "there was an issue getting your token")
78 | return
79 | }
80 |
81 | if !token.Valid() {
82 | fmt.Fprintln(w, "retreived invalid token")
83 | return
84 | }
85 |
86 | client := github.NewClient(oauthCfg.Client(oauth2.NoContext, token))
87 |
88 | user, _, err := client.Users.Get(context.Background(), "")
89 | if err != nil {
90 | fmt.Println(w, "error getting name")
91 | return
92 | }
93 |
94 | session.Values["githubUserName"] = user.Name
95 | session.Values["githubAccessToken"] = token
96 | session.Save(r, w)
97 |
98 | http.Redirect(w, r, "/", 302)
99 | }
100 |
101 | // http://www.gorillatoolkit.org/pkg/sessions#CookieStore.MaxAge
102 | func SessionDestroyHandler(w http.ResponseWriter, r *http.Request) {
103 | session, err := store.Get(r, sessionStoreKey)
104 | if err != nil {
105 | fmt.Fprintln(w, "aborted")
106 | return
107 | }
108 |
109 | session.Options.MaxAge = -1
110 |
111 | session.Save(r, w)
112 | http.Redirect(w, r, "/", 302)
113 |
114 | }
115 |
116 | // func sessionGithubUser(r *http.Request) error {
117 | // session, err := store.Get(r, "sess")
118 | // if err != nil {
119 | // return err
120 | // }
121 |
122 | // accessToken, ok := session.Values["githubAccessToken"].(*oauth2.Token); ok {
123 | // client := github.NewClient(oauthCfg.Client(oauth2.NoContext, accessToken))
124 |
125 | // }
126 |
127 | // return nil
128 | // }
129 |
--------------------------------------------------------------------------------