socialnetworknews-api

git clone git://archive.git.mtrnord.blog/SocialNetworkNews/socialnetworknews-api.git
Log | Files | Refs | LICENSE

commit 2efc3dfdfa73e28016b0bc0e50a5a1d8721a4ee9
parent 8278029fb1687f525a373aa3bd0a479d5368eb68
Author: MTRNord <mtrnord1@gmail.com>
Date:   Sat, 31 Mar 2018 16:15:30 +0200

Add JWT Token as response

Diffstat:
Mapi/login/twitter.go | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Ddb/passwords/passwords.go | 46----------------------------------------------
2 files changed, 115 insertions(+), 47 deletions(-)

diff --git a/api/login/twitter.go b/api/login/twitter.go @@ -1,12 +1,18 @@ package login import ( + "encoding/json" + "fmt" + "github.com/SocialNetworkNews/SocialNetworkNews_API/db" "github.com/dghubble/gologin" libLogin "github.com/dghubble/gologin/oauth1" oauth1Login "github.com/dghubble/gologin/oauth1" "github.com/dghubble/gologin/twitter" "github.com/dghubble/oauth1" "github.com/dghubble/sessions" + "github.com/dgraph-io/badger" + "github.com/dgrijalva/jwt-go" + "github.com/satori/go.uuid" "log" "net/http" ) @@ -15,13 +21,81 @@ var TConfig *oauth1.Config const ( sessionName = "ssn-app" - sessionSecret = "example cookie signing secret" + sessionSecret = "006b2294-201d-4283-9114-149b3347b264" sessionUserKey = "twitterID" ) // sessionStore encodes and decodes session data stored in signed cookies var sessionStore = sessions.NewCookieStore([]byte(sessionSecret), nil) +func getUserSecret(id string) (string, error) { + var secret string + dataDB, err := db.OpenDB() + if err != nil { + return "", err + } + + DBerr := dataDB.View(func(txn *badger.Txn) error { + data, err := db.Get(txn, []byte(fmt.Sprintf("users|%s|data", id))) + secret = fmt.Sprintf("%s", data) + return err + }) + + return secret, DBerr +} + +func saveUser(id, accessToken, accessSecret string, data []byte) error { + dataDB, err := db.OpenDB() + if err != nil { + return err + } + + return dataDB.Update(func(txn *badger.Txn) error { + ATerr := txn.Set([]byte(fmt.Sprintf("users|%s|accessToken", id)), []byte(accessToken)) + if ATerr != nil { + return ATerr + } + + ASerr := txn.Set([]byte(fmt.Sprintf("users|%s|accessSecret", id)), []byte(accessSecret)) + if ASerr != nil { + return ASerr + } + + puuid, UUIDerr := uuid.NewV4() + if UUIDerr != nil { + return UUIDerr + } + newUUID := puuid.String() + + USerr := txn.Set([]byte(fmt.Sprintf("users|%s|userSecret", id)), []byte(newUUID)) + if USerr != nil { + return USerr + } + + return txn.Set([]byte(fmt.Sprintf("users|%s|data", id)), []byte(data)) + }) +} + +func checkUserExists(id string) (bool, error) { + exists := true + dataDB, err := db.OpenDB() + if err != nil { + return true, err + } + + dbErr := dataDB.View(func(txn *badger.Txn) error { + _, QueryErr := txn.Get([]byte(fmt.Sprintf("users|%s", id))) + if QueryErr != nil && QueryErr != badger.ErrKeyNotFound { + return QueryErr + } + if QueryErr == badger.ErrKeyNotFound { + exists = false + } + return nil + }) + return exists, dbErr +} + // issueSession issues a cookie session after successful Twitter login func IssueSession() http.Handler { fn := func(w http.ResponseWriter, req *http.Request) { @@ -31,10 +105,50 @@ func IssueSession() http.Handler { http.Error(w, err.Error(), http.StatusInternalServerError) return } + if exists, err := checkUserExists(twitterUser.IDStr); !exists { + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + b, err := json.Marshal(twitterUser) + if err != nil { + fmt.Println("error:", err) + } + + SErr := saveUser(twitterUser.IDStr, accessToken, accessSecret, b) + if SErr != nil { + http.Error(w, SErr.Error(), http.StatusInternalServerError) + return + } + } + + // Create a new token object, specifying signing method and the claims + // you would like it to contain. + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "loggedIn": "true", + }) + + secret, err := getUserSecret(twitterUser.IDStr) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Sign and get the complete encoded token as a string using the secret + tokenString, err := token.SignedString(secret) + // 2. Implement a success handler to issue some form of session session := sessionStore.New(sessionName) session.Values[sessionUserKey] = twitterUser.ID session.Save(w) + w.Header().Set("Authorization", "Bearer "+tokenString) domain := req.Header.Get("Host") http.Redirect(w, req, domain, http.StatusFound) } diff --git a/db/passwords/passwords.go b/db/passwords/passwords.go @@ -1,46 +0,0 @@ -package passwords - -import ( - "fmt" - "github.com/SocialNetworkNews/SocialNetworkNews_API/db" - "github.com/dgraph-io/badger" - "golang.org/x/crypto/bcrypt" -) - -func SavePassword(password, username string) error { - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return err - } - - userDB, err := db.OpenDB() - if err != nil { - return err - } - - return userDB.Update(func(txn *badger.Txn) error { - return txn.Set([]byte(fmt.Sprintf("users|%s|password", username)), hash) - }) -} - -func CheckPassword(username, password string) (bool, error) { - userDB, err := db.OpenDB() - if err != nil { - return false, err - } - var hash []byte - - DBErr := userDB.View(func(txn *badger.Txn) error { - var err error - hash, err = db.Get(txn, []byte(fmt.Sprintf("users|%s|password", username))) - return err - }) - if DBErr != nil { - return false, DBErr - } - - if err := bcrypt.CompareHashAndPassword(hash, []byte(password)); err != nil { - return false, err - } - return true, nil -}