package bouquins import ( "encoding/json" "fmt" "log" "math/rand" "net/http" "github.com/gorilla/sessions" "golang.org/x/oauth2" ) const ( alphanums = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" sessionName = "bouquins" sessionOAuthState = "oauthState" sessionUser = "username" ) // generates a 16 characters long random string func securedRandString() string { b := make([]byte, 16) for i := range b { b[i] = alphanums[rand.Intn(len(alphanums))] } return string(b) } // current session func (app *Bouquins) Session(req *http.Request) *sessions.Session { session, _ := app.Cookies.Get(req, sessionName) return session } // logged in username func (app *Bouquins) Username(req *http.Request) string { username := app.Session(req).Values[sessionUser] if username != nil { return username.(string) } return "" } // sets value in session func (app *Bouquins) SessionSet(name string, value string, res http.ResponseWriter, req *http.Request) { session := app.Session(req) session.Values[name] = value session.Save(req, res) } // LoginPage redirects to OAuth login page (github) func (app *Bouquins) LoginPage(res http.ResponseWriter, req *http.Request) error { // TODO choose provider state := securedRandString() app.SessionSet(sessionOAuthState, state, res, req) url := app.OAuthConf.AuthCodeURL(state) http.Redirect(res, req, url, http.StatusTemporaryRedirect) return nil } // LogoutPage logout connected user func (app *Bouquins) LogoutPage(res http.ResponseWriter, req *http.Request) error { app.SessionSet(sessionUser, "", res, req) return RedirectHome(res, req) } // CallbackPage handle OAuth 2 callback func (app *Bouquins) CallbackPage(res http.ResponseWriter, req *http.Request) error { savedState := app.Session(req).Values[sessionOAuthState] if savedState == "" { return fmt.Errorf("missing saved oauth state") } app.SessionSet(sessionOAuthState, "", res, req) state := req.FormValue("state") if state != savedState { return fmt.Errorf("invalid oauth state, expected '%s', got '%s'", "state", state) } code := req.FormValue("code") token, err := app.OAuthConf.Exchange(oauth2.NoContext, code) if err != nil { return fmt.Errorf("Code exchange failed with '%s'", err) } apiReq, err := http.NewRequest("GET", "https://api.github.com/user/emails", nil) apiReq.Header.Add("Accept", "application/vnd.github.v3+json") apiReq.Header.Add("Authorization", "token "+token.AccessToken) client := &http.Client{} response, err := client.Do(apiReq) defer response.Body.Close() if err != nil { log.Println("Auth error", err) return fmt.Errorf("Authentification error") } dec := json.NewDecoder(response.Body) var emails []GitHubEmail err = dec.Decode(&emails) if err != nil { log.Println("Error reading github API response", err) return fmt.Errorf("Error reading github API response") } fmt.Printf("Content: %s\n", emails) var userEmail string for _, email := range emails { if email.Primary && email.Verified { userEmail = email.Email } } log.Println("User email:", userEmail) // FIXME list allowed users if userEmail == "meutel+github@meutel.net" { app.SessionSet(sessionUser, "Meutel", res, req) log.Println("User logged in", userEmail) return RedirectHome(res, req) } else { return fmt.Errorf("Unknown user") } }