oauth: add twitter

This commit is contained in:
Adhityaa Chandrasekar 2019-02-22 21:23:24 -05:00
parent d367ac8391
commit c07f3e8b9f
9 changed files with 212 additions and 0 deletions

9
api/Gopkg.lock generated
View File

@ -25,6 +25,14 @@
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:d03d0fae6a7a80e89c540787a69ab6e0d3b773fdb3303c0b3d96a15490c6ef32"
name = "github.com/gomodule/oauth1"
packages = ["oauth"]
pruneopts = "UT"
revision = "9a59ed3b0a84f454c260f2f8f82918223fc5630f"
[[projects]]
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
name = "github.com/gorilla/context"
@ -153,6 +161,7 @@
analyzer-version = 1
input-imports = [
"github.com/adtac/go-akismet/akismet",
"github.com/gomodule/oauth1/oauth",
"github.com/gorilla/handlers",
"github.com/gorilla/mux",
"github.com/lib/pq",

View File

@ -50,6 +50,9 @@ func configParse() error {
"GITHUB_KEY": "",
"GITHUB_SECRET": "",
"TWITTER_KEY": "",
"TWITTER_SECRET": "",
}
for key, value := range defaults {

View File

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

51
api/oauth_twitter.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"github.com/gomodule/oauth1/oauth"
"os"
"sync"
)
type twitterOauthState struct {
CommenterToken string
Cred *oauth.Credentials
}
var twitterClient *oauth.Client
var twitterCredMapLock sync.RWMutex
var twitterCredMap map[string]twitterOauthState
func twitterOauthConfigure() error {
twitterClient = nil
if os.Getenv("TWITTER_KEY") == "" && os.Getenv("TWITTER_SECRET") == "" {
return nil
}
if os.Getenv("TWITTER_KEY") == "" {
logger.Errorf("COMMENTO_TWITTER_KEY not configured, but COMMENTO_TWITTER_SECRET is set")
return errorOauthMisconfigured
}
if os.Getenv("TWITTER_SECRET") == "" {
logger.Errorf("COMMENTO_TWITTER_SECRET not configured, but COMMENTO_TWITTER_KEY is set")
return errorOauthMisconfigured
}
logger.Infof("loading twitter OAuth config")
twitterClient = &oauth.Client{
TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token",
ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authenticate",
TokenRequestURI: "https://api.twitter.com/oauth/access_token",
Credentials: oauth.Credentials{
Token: os.Getenv("TWITTER_KEY"),
Secret: os.Getenv("TWITTER_SECRET"),
},
}
configuredOauths = append(configuredOauths, "twitter")
twitterCredMap = make(map[string]twitterOauthState, 1e3)
return nil
}

View File

@ -0,0 +1,92 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func twitterCallbackHandler(w http.ResponseWriter, r *http.Request) {
token := r.FormValue("oauth_token")
verifier := r.FormValue("oauth_verifier")
twitterCredMapLock.RLock()
s, ok := twitterCredMap[token]
twitterCredMapLock.RUnlock()
commenterToken := s.CommenterToken
if !ok {
fmt.Fprintf(w, "no such token/verifier combination found")
return
}
_, err := commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
x, _, err := twitterClient.RequestToken(nil, s.Cred, verifier)
if err != nil {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
twitterCredMapLock.Lock()
delete(twitterCredMap, token)
twitterCredMapLock.Unlock()
resp, err := twitterClient.Get(nil, x, "https://api.twitter.com/1.1/account/verify_credentials.json", url.Values{"include_email": {"true"}})
if err != nil {
fmt.Fprintf(w, "Error getting email: %s\n", err.Error())
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
msg, _ := ioutil.ReadAll(resp.Body)
fmt.Fprintf(w, "Error: status %d: %s\n", resp.StatusCode, msg)
return
}
var res map[string]interface{}
if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
name := res["name"].(string)
handle := res["screen_name"].(string)
link := "https://twitter.com/" + handle
photo := "https://twitter.com/" + handle + "/profile_image"
email := res["email"].(string)
c, err := commenterGetByEmail("twitter", 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 {
commenterHex, err = commenterNew(email, name, link, photo, "twitter", "")
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,39 @@
package main
import (
"fmt"
"net/http"
"os"
)
func twitterRedirectHandler(w http.ResponseWriter, r *http.Request) {
if twitterClient == nil {
logger.Errorf("twitter oauth access attempt without configuration")
fmt.Fprintf(w, "error: this website has not configured twitter OAuth")
return
}
commenterToken := r.FormValue("commenterToken")
_, err := commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "error: %s\n", err.Error())
return
}
cred, err := twitterClient.RequestTemporaryCredentials(nil, os.Getenv("ORIGIN")+"/api/oauth/twitter/callback", nil)
if err != nil {
logger.Errorf("cannot get temporary twitter credentials: %v", err)
fmt.Fprintf(w, "error: %v", errorInternal.Error())
return
}
twitterCredMapLock.Lock()
twitterCredMap[cred.Token] = twitterOauthState{
CommenterToken: commenterToken,
Cred: cred,
}
twitterCredMapLock.Unlock()
http.Redirect(w, r, twitterClient.AuthorizationURL(cred, nil), http.StatusFound)
}

View File

@ -38,6 +38,9 @@ func apiRouterInit(router *mux.Router) error {
router.HandleFunc("/api/oauth/github/redirect", githubRedirectHandler).Methods("GET")
router.HandleFunc("/api/oauth/github/callback", githubCallbackHandler).Methods("GET")
router.HandleFunc("/api/oauth/twitter/redirect", twitterRedirectHandler).Methods("GET")
router.HandleFunc("/api/oauth/twitter/callback", twitterCallbackHandler).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

@ -262,6 +262,8 @@
attrSet(avatar, "src", commenter.photo + "?sz=50");
} else if (commenter.provider === "github") {
attrSet(avatar, "src", commenter.photo + "&s=50");
} else if (commenter.provider === "twitter") {
attrSet(avatar, "src", commenter.photo + "?size=normal");
} else {
attrSet(avatar, "src", commenter.photo);
}
@ -745,6 +747,8 @@
attrSet(avatar, "src", commenter.photo + "?sz=50");
} else if (commenter.provider === "github") {
attrSet(avatar, "src", commenter.photo + "&s=50");
} else if (commenter.provider === "twitter") {
attrSet(avatar, "src", commenter.photo + "?size=normal");
} else {
attrSet(avatar, "src", commenter.photo);
}

View File

@ -25,5 +25,12 @@
font-size: 13px;
width: 70px;
}
.commento-twitter-button {
background: #00aced;
text-transform: uppercase;
font-size: 13px;
width: 70px;
}
}
}