api: add github oauth

Closes https://gitlab.com/commento/commento/issues/20
This commit is contained in:
Adhityaa Chandrasekar 2019-01-30 21:15:16 -05:00
parent 24d76c2fb6
commit 55f24b2de2
8 changed files with 200 additions and 1 deletions

4
api/Gopkg.lock generated
View File

@ -116,10 +116,11 @@
[[projects]]
branch = "master"
digest = "1:bea0314c10bd362ab623af4880d853b5bad3b63d0ab9945c47e461b8d04203ed"
digest = "1:82e6e4dc5ab71680d89684e4649be630fdeeaf81feb8e88e4a56273a0cd4d966"
name = "golang.org/x/oauth2"
packages = [
".",
"github",
"google",
"internal",
"jws",
@ -161,6 +162,7 @@
"github.com/russross/blackfriday",
"golang.org/x/crypto/bcrypt",
"golang.org/x/oauth2",
"golang.org/x/oauth2/github",
"golang.org/x/oauth2/google",
]
solver-name = "gps-cdcl"

View File

@ -47,6 +47,9 @@ func configParse() error {
"GOOGLE_KEY": "",
"GOOGLE_SECRET": "",
"GITHUB_KEY": "",
"GITHUB_SECRET": "",
}
for key, value := range defaults {

View File

@ -11,5 +11,9 @@ func oauthConfigure() error {
return err
}
if err := githubOauthConfigure(); err != nil {
return err
}
return nil
}

43
api/oauth_github.go Normal file
View File

@ -0,0 +1,43 @@
package main
import (
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
"os"
)
var githubConfig *oauth2.Config
func githubOauthConfigure() error {
githubConfig = nil
if os.Getenv("GITHUB_KEY") == "" && os.Getenv("GITHUB_SECRET") == "" {
return nil
}
if os.Getenv("GITHUB_KEY") == "" {
logger.Errorf("COMMENTO_GITHUB_KEY not configured, but COMMENTO_GITHUB_SECRET is set")
return errorOauthMisconfigured
}
if os.Getenv("GITHUB_SECRET") == "" {
logger.Errorf("COMMENTO_GITHUB_SECRET not configured, but COMMENTO_GITHUB_KEY is set")
return errorOauthMisconfigured
}
logger.Infof("loading github OAuth config")
githubConfig = &oauth2.Config{
RedirectURL: os.Getenv("ORIGIN") + "/api/oauth/github/callback",
ClientID: os.Getenv("GITHUB_KEY"),
ClientSecret: os.Getenv("GITHUB_SECRET"),
Scopes: []string{
"read:user",
"user:email",
},
Endpoint: github.Endpoint,
}
configuredOauths = append(configuredOauths, "github")
return nil
}

View File

@ -0,0 +1,115 @@
package main
import (
"encoding/json"
"fmt"
"golang.org/x/oauth2"
"io/ioutil"
"net/http"
)
func githubGetPrimaryEmail(accessToken string) (string, error) {
resp, err := http.Get("https://api.github.com/user/emails?access_token=" + accessToken)
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", errorCannotReadResponse
}
user := []map[string]interface{}{}
if err := json.Unmarshal(contents, &user); err != nil {
logger.Errorf("error unmarshaling github user: %v", err)
return "", errorInternal
}
nonPrimaryEmail := ""
for _, email := range(user) {
nonPrimaryEmail = email["email"].(string)
if email["primary"].(bool) {
return email["email"].(string), nil
}
}
return nonPrimaryEmail, nil
}
func githubCallbackHandler(w http.ResponseWriter, r *http.Request) {
commenterToken := r.FormValue("state")
code := r.FormValue("code")
_, err := commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
token, err := githubConfig.Exchange(oauth2.NoContext, code)
if err != nil {
fmt.Fprintf(w, "Error: %s", err.Error())
return
}
email, err := githubGetPrimaryEmail(token.AccessToken)
if err != nil {
fmt.Fprintf(w, "Error: %s", err.Error())
return
}
resp, err := http.Get("https://api.github.com/user?access_token=" + token.AccessToken)
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Fprintf(w, "Error: %s", errorCannotReadResponse.Error())
return
}
user := make(map[string]interface{})
if err := json.Unmarshal(contents, &user); err != nil {
fmt.Fprintf(w, "Error: %s", errorInternal.Error())
return
}
if email == "" {
if user["email"] == nil {
fmt.Fprintf(w, "Error: no email address returned by Github")
return
}
email = user["email"].(string)
}
c, err := commenterGetByEmail("github", email)
if err != nil && err != errorNoSuchCommenter {
fmt.Fprintf(w, "Error: %s", err.Error())
return
}
var commenterHex string
// TODO: in case of returning users, update the information we have on record?
if err == errorNoSuchCommenter {
var link string
if val, ok := user["html_url"]; ok {
link = val.(string)
} else {
link = "undefined"
}
commenterHex, err = commenterNew(email, user["name"].(string), link, user["avatar_url"].(string), "github", "")
if err != nil {
fmt.Fprintf(w, "Error: %s", err.Error())
return
}
} else {
commenterHex = c.CommenterHex
}
if err := commenterSessionUpdate(commenterToken, commenterHex); err != nil {
fmt.Fprintf(w, "Error: %s", err.Error())
return
}
fmt.Fprintf(w, "<html><script>window.parent.close()</script></html>")
}

View File

@ -0,0 +1,25 @@
package main
import (
"fmt"
"net/http"
)
func githubRedirectHandler(w http.ResponseWriter, r *http.Request) {
if githubConfig == nil {
logger.Errorf("github oauth access attempt without configuration")
fmt.Fprintf(w, "error: this website has not configured github OAuth")
return
}
commenterToken := r.FormValue("commenterToken")
_, err := commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "error: %s\n", err.Error())
return
}
url := githubConfig.AuthCodeURL(commenterToken)
http.Redirect(w, r, url, http.StatusFound)
}

View File

@ -29,6 +29,9 @@ func apiRouterInit(router *mux.Router) error {
router.HandleFunc("/api/oauth/google/redirect", googleRedirectHandler).Methods("GET")
router.HandleFunc("/api/oauth/google/callback", googleCallbackHandler).Methods("GET")
router.HandleFunc("/api/oauth/github/redirect", githubRedirectHandler).Methods("GET")
router.HandleFunc("/api/oauth/github/callback", githubCallbackHandler).Methods("GET")
router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST")
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")

View File

@ -269,6 +269,8 @@
avatar = create("img");
if (resp.commenter.provider === "google") {
attrSet(avatar, "src", resp.commenter.photo + "?sz=50");
} else if (resp.commenter.provider === "github") {
attrSet(avatar, "src", resp.commenter.photo + "&s=50");
} else {
attrSet(avatar, "src", resp.commenter.photo);
}
@ -699,6 +701,8 @@
avatar = create("img");
if (commenter.provider === "google") {
attrSet(avatar, "src", commenter.photo + "?sz=50");
} else if (commenter.provider === "github") {
attrSet(avatar, "src", commenter.photo + "&s=50");
} else {
attrSet(avatar, "src", commenter.photo);
}