oauth: add twitter
This commit is contained in:
parent
d367ac8391
commit
c07f3e8b9f
9
api/Gopkg.lock
generated
9
api/Gopkg.lock
generated
@ -25,6 +25,14 @@
|
|||||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:d03d0fae6a7a80e89c540787a69ab6e0d3b773fdb3303c0b3d96a15490c6ef32"
|
||||||
|
name = "github.com/gomodule/oauth1"
|
||||||
|
packages = ["oauth"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "9a59ed3b0a84f454c260f2f8f82918223fc5630f"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
|
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
|
||||||
name = "github.com/gorilla/context"
|
name = "github.com/gorilla/context"
|
||||||
@ -153,6 +161,7 @@
|
|||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
input-imports = [
|
input-imports = [
|
||||||
"github.com/adtac/go-akismet/akismet",
|
"github.com/adtac/go-akismet/akismet",
|
||||||
|
"github.com/gomodule/oauth1/oauth",
|
||||||
"github.com/gorilla/handlers",
|
"github.com/gorilla/handlers",
|
||||||
"github.com/gorilla/mux",
|
"github.com/gorilla/mux",
|
||||||
"github.com/lib/pq",
|
"github.com/lib/pq",
|
||||||
|
@ -50,6 +50,9 @@ func configParse() error {
|
|||||||
|
|
||||||
"GITHUB_KEY": "",
|
"GITHUB_KEY": "",
|
||||||
"GITHUB_SECRET": "",
|
"GITHUB_SECRET": "",
|
||||||
|
|
||||||
|
"TWITTER_KEY": "",
|
||||||
|
"TWITTER_SECRET": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range defaults {
|
for key, value := range defaults {
|
||||||
|
@ -15,5 +15,9 @@ func oauthConfigure() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := twitterOauthConfigure(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
51
api/oauth_twitter.go
Normal file
51
api/oauth_twitter.go
Normal 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
|
||||||
|
}
|
92
api/oauth_twitter_callback.go
Normal file
92
api/oauth_twitter_callback.go
Normal 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>")
|
||||||
|
}
|
39
api/oauth_twitter_redirect.go
Normal file
39
api/oauth_twitter_redirect.go
Normal 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)
|
||||||
|
}
|
@ -38,6 +38,9 @@ func apiRouterInit(router *mux.Router) error {
|
|||||||
router.HandleFunc("/api/oauth/github/redirect", githubRedirectHandler).Methods("GET")
|
router.HandleFunc("/api/oauth/github/redirect", githubRedirectHandler).Methods("GET")
|
||||||
router.HandleFunc("/api/oauth/github/callback", githubCallbackHandler).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/new", commentNewHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
|
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
|
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
|
||||||
|
@ -262,6 +262,8 @@
|
|||||||
attrSet(avatar, "src", commenter.photo + "?sz=50");
|
attrSet(avatar, "src", commenter.photo + "?sz=50");
|
||||||
} else if (commenter.provider === "github") {
|
} else if (commenter.provider === "github") {
|
||||||
attrSet(avatar, "src", commenter.photo + "&s=50");
|
attrSet(avatar, "src", commenter.photo + "&s=50");
|
||||||
|
} else if (commenter.provider === "twitter") {
|
||||||
|
attrSet(avatar, "src", commenter.photo + "?size=normal");
|
||||||
} else {
|
} else {
|
||||||
attrSet(avatar, "src", commenter.photo);
|
attrSet(avatar, "src", commenter.photo);
|
||||||
}
|
}
|
||||||
@ -745,6 +747,8 @@
|
|||||||
attrSet(avatar, "src", commenter.photo + "?sz=50");
|
attrSet(avatar, "src", commenter.photo + "?sz=50");
|
||||||
} else if (commenter.provider === "github") {
|
} else if (commenter.provider === "github") {
|
||||||
attrSet(avatar, "src", commenter.photo + "&s=50");
|
attrSet(avatar, "src", commenter.photo + "&s=50");
|
||||||
|
} else if (commenter.provider === "twitter") {
|
||||||
|
attrSet(avatar, "src", commenter.photo + "?size=normal");
|
||||||
} else {
|
} else {
|
||||||
attrSet(avatar, "src", commenter.photo);
|
attrSet(avatar, "src", commenter.photo);
|
||||||
}
|
}
|
||||||
|
@ -25,5 +25,12 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.commento-twitter-button {
|
||||||
|
background: #00aced;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 13px;
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user