twitter.go (6684B)
1 package login 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "github.com/SocialNetworkNews/SocialNetworkNews_API/api/util" 7 "github.com/SocialNetworkNews/SocialNetworkNews_API/db" 8 "github.com/dghubble/gologin" 9 libLogin "github.com/dghubble/gologin/oauth1" 10 oauth1Login "github.com/dghubble/gologin/oauth1" 11 "github.com/dghubble/gologin/twitter" 12 "github.com/dghubble/oauth1" 13 "github.com/dghubble/sessions" 14 "github.com/dgraph-io/badger" 15 "github.com/satori/go.uuid" 16 "log" 17 "net/http" 18 ) 19 20 var TConfig *oauth1.Config 21 22 const ( 23 sessionName = "ssn-app" 24 sessionSecret = "006b2294-201d-4283-9114-149b3347b264" 25 sessionUserKey = "twitterID" 26 ) 27 28 // sessionStore encodes and decodes session data stored in signed cookies 29 var sessionStore = sessions.NewCookieStore([]byte(sessionSecret), nil) 30 31 func getUserUUID(id string) (string, error) { 32 var uuidS string 33 dataDB, err := db.OpenDB() 34 if err != nil { 35 return "", err 36 } 37 38 DBerr := dataDB.View(func(txn *badger.Txn) error { 39 data, err := db.Get(txn, []byte(fmt.Sprintf("users|T|%s|uuid", id))) 40 uuidS = fmt.Sprintf("%s", data) 41 return err 42 }) 43 44 return uuidS, DBerr 45 } 46 47 func saveTUser(id, accessToken, accessSecret, name string, data []byte) error { 48 dataDB, err := db.OpenDB() 49 if err != nil { 50 return err 51 } 52 53 puuid, UUIDerr := uuid.NewV4() 54 if UUIDerr != nil { 55 return UUIDerr 56 } 57 newUUID := puuid.String() 58 59 return dataDB.Update(func(txn *badger.Txn) error { 60 ATerr := txn.Set([]byte(fmt.Sprintf("users|T|%s|accessToken", id)), []byte(accessToken)) 61 if ATerr != nil { 62 return ATerr 63 } 64 65 ASerr := txn.Set([]byte(fmt.Sprintf("users|T|%s|accessSecret", id)), []byte(accessSecret)) 66 if ASerr != nil { 67 return ASerr 68 } 69 70 Nerr := txn.Set([]byte(fmt.Sprintf("users|username|T|%s", newUUID)), []byte(name)) 71 if Nerr != nil { 72 return Nerr 73 } 74 75 IDerr := txn.Set([]byte(fmt.Sprintf("users|id|T|%s", newUUID)), []byte(id)) 76 if IDerr != nil { 77 return IDerr 78 } 79 80 UUIDDBerr := txn.Set([]byte(fmt.Sprintf("users|T|%s|uuid", id)), []byte(newUUID)) 81 if UUIDDBerr != nil { 82 return UUIDDBerr 83 } 84 85 return txn.Set([]byte(fmt.Sprintf("users|T|%s|data", id)), []byte(data)) 86 }) 87 } 88 89 func checkUserExists(id string) (bool, error) { 90 exists := true 91 dataDB, err := db.OpenDB() 92 if err != nil { 93 return true, err 94 } 95 96 dbErr := dataDB.View(func(txn *badger.Txn) error { 97 _, QueryErr := txn.Get([]byte(fmt.Sprintf("users|%s", id))) 98 if QueryErr != nil && QueryErr != badger.ErrKeyNotFound { 99 return QueryErr 100 } 101 if QueryErr == badger.ErrKeyNotFound { 102 exists = false 103 } 104 return nil 105 }) 106 return exists, dbErr 107 } 108 109 // issueSession issues a cookie session after successful Twitter login 110 func IssueSession() http.Handler { 111 fn := func(w http.ResponseWriter, req *http.Request) { 112 ctx := req.Context() 113 twitterUser, err := twitter.UserFromContext(ctx) 114 if err != nil { 115 http.Error(w, err.Error(), http.StatusInternalServerError) 116 return 117 } 118 if exists, err := checkUserExists(twitterUser.IDStr); !exists { 119 if err != nil { 120 http.Error(w, err.Error(), http.StatusInternalServerError) 121 return 122 } 123 124 accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx) 125 if err != nil { 126 http.Error(w, err.Error(), http.StatusInternalServerError) 127 return 128 } 129 130 b, err := json.Marshal(twitterUser) 131 if err != nil { 132 http.Error(w, err.Error(), http.StatusInternalServerError) 133 } 134 135 SErr := saveTUser(twitterUser.IDStr, accessToken, accessSecret, twitterUser.ScreenName, b) 136 if SErr != nil { 137 http.Error(w, SErr.Error(), http.StatusInternalServerError) 138 return 139 } 140 } 141 142 // 2. Implement a success handler to issue some form of session 143 session := sessionStore.New(sessionName) 144 session.Values[sessionUserKey] = twitterUser.IDStr 145 session.Save(w) 146 147 uuidS, err := getUserUUID(twitterUser.IDStr) 148 if err != nil { 149 http.Error(w, err.Error(), http.StatusInternalServerError) 150 return 151 } 152 w.Header().Set("UUID", uuidS) 153 w.Header().Set("API-VERSION", util.APIVersion) 154 domain := req.Host 155 log.Println("domain:", domain) 156 w.WriteHeader(http.StatusOK) 157 } 158 return http.HandlerFunc(fn) 159 } 160 161 // LogoutHandler destroys the session on POSTs and redirects to home. 162 func LogoutHandler(w http.ResponseWriter, req *http.Request) { 163 if req.Method == "POST" { 164 sessionStore.Destroy(w, sessionName) 165 } 166 http.Redirect(w, req, "/", http.StatusFound) 167 } 168 169 // IsAuthenticated returns true if the user has a signed session cookie. 170 func IsAuthenticated(req *http.Request) bool { 171 if _, err := sessionStore.Get(req, sessionName); err == nil { 172 return true 173 } 174 return false 175 } 176 177 // IsAuthenticatedHandleFunc returns 200 if the user has a signed session cookie. 178 func IsAuthenticatedHandleFunc(w http.ResponseWriter, req *http.Request) { 179 w.Header().Set("API-VERSION", util.APIVersion) 180 if IsAuthenticated(req) { 181 Reqsession, err := sessionStore.Get(req, sessionName) 182 if err != nil { 183 http.Error(w, err.Error(), http.StatusInternalServerError) 184 return 185 } 186 twitterUserID := Reqsession.Values[sessionUserKey] 187 switch twitterUserID.(type) { 188 case string: 189 // We know this is a string 190 uuidS, err := getUserUUID(twitterUserID.(string)) 191 if err != nil { 192 http.Error(w, err.Error(), http.StatusInternalServerError) 193 return 194 } 195 w.Header().Set("UUID", uuidS) 196 w.WriteHeader(http.StatusOK) 197 } 198 199 w.WriteHeader(http.StatusUnauthorized) 200 } else { 201 w.WriteHeader(http.StatusUnauthorized) 202 } 203 } 204 205 // LoginHandler handles Twitter login requests by obtaining a request token and 206 // redirecting to the authorization URL. 207 func LoginHandler(config *oauth1.Config, failure http.Handler) http.Handler { 208 // oauth1.LoginHandler -> oauth1.AuthRedirectHandler 209 success := AuthRedirectHandler(config, failure) 210 return oauth1Login.LoginHandler(config, success, failure) 211 } 212 213 // AuthRedirectHandler reads the request token from the ctx and redirects 214 // to the authorization URL. 215 func AuthRedirectHandler(config *oauth1.Config, failure http.Handler) http.Handler { 216 if failure == nil { 217 failure = gologin.DefaultFailureHandler 218 } 219 fn := func(w http.ResponseWriter, req *http.Request) { 220 domain := req.Host 221 log.Println("domain:", domain) 222 config.CallbackURL = "https://" + domain + "/login/twitter/callback" 223 TConfig = config 224 ctx := req.Context() 225 requestToken, _, err := libLogin.RequestTokenFromContext(ctx) 226 if err != nil { 227 ctx = gologin.WithError(ctx, err) 228 failure.ServeHTTP(w, req.WithContext(ctx)) 229 return 230 } 231 authorizationURL, err := config.AuthorizationURL(requestToken) 232 if err != nil { 233 ctx = gologin.WithError(ctx, err) 234 failure.ServeHTTP(w, req.WithContext(ctx)) 235 return 236 } 237 http.Redirect(w, req, authorizationURL.String(), http.StatusFound) 238 } 239 return http.HandlerFunc(fn) 240 }