api: add github oauth
Closes https://gitlab.com/commento/commento/issues/20
This commit is contained in:
parent
24d76c2fb6
commit
55f24b2de2
4
api/Gopkg.lock
generated
4
api/Gopkg.lock
generated
@ -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"
|
||||
|
@ -47,6 +47,9 @@ func configParse() error {
|
||||
|
||||
"GOOGLE_KEY": "",
|
||||
"GOOGLE_SECRET": "",
|
||||
|
||||
"GITHUB_KEY": "",
|
||||
"GITHUB_SECRET": "",
|
||||
}
|
||||
|
||||
for key, value := range defaults {
|
||||
|
@ -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
43
api/oauth_github.go
Normal 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
|
||||
}
|
115
api/oauth_github_callback.go
Normal file
115
api/oauth_github_callback.go
Normal 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>")
|
||||
}
|
25
api/oauth_github_redirect.go
Normal file
25
api/oauth_github_redirect.go
Normal 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)
|
||||
}
|
@ -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")
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user