Browse Source

Finally get the session store properly working

master
Stephanie Gredell 1 year ago
parent
commit
0f3386bb2a
  1. 33
      auth/auth.go
  2. 170
      auth/sessions.go
  3. 4
      db/db.go
  4. 11
      go.mod
  5. 26
      go.sum
  6. 4
      utils/util.go

33
auth/auth.go

@ -5,7 +5,7 @@ import ( @@ -5,7 +5,7 @@ import (
"github.com/markbates/goth/gothic"
"log"
"net/http"
"sponsorahacker/config"
db "sponsorahacker/db"
)
func Login(c *gin.Context) {
@ -17,11 +17,14 @@ func Login(c *gin.Context) { @@ -17,11 +17,14 @@ func Login(c *gin.Context) {
}
func Callback(c *gin.Context) {
sessionStore, err := NewSessionManager(config.GetEnvVar("DATABASE_URL"))
dbclient, err := db.NewDbClient()
if err != nil {
panic(err)
log.Println(err)
return
}
sessionStore := NewSessionManager(dbclient)
user, err := gothic.CompleteUserAuth(c.Writer, c.Request)
if err != nil {
@ -30,10 +33,10 @@ func Callback(c *gin.Context) { @@ -30,10 +33,10 @@ func Callback(c *gin.Context) {
return
}
c.SetCookie("user_id", user.UserID, 3600, "/", "localhost", false, true)
err = sessionStore.SetSession(user.Name, c)
err = sessionStore.CreateSession(user, c)
if err != nil {
log.Println("failed to set session:", err)
log.Println("failed to create session in db:", err)
}
// For now, redirect to profile page after successful login
@ -41,14 +44,24 @@ func Callback(c *gin.Context) { @@ -41,14 +44,24 @@ func Callback(c *gin.Context) {
}
func Logout(c *gin.Context) {
sessionStore, err := NewSessionManager(config.GetEnvVar("DATABASE_URL"))
dbClient, err := db.NewDbClient()
if err != nil {
log.Fatal(err)
return
}
sessionStore := NewSessionManager(dbClient)
if err != nil {
panic(err)
log.Fatal(err)
return
}
c.SetCookie("user_id", "", -1, "/", "localhost", false, true)
err = sessionStore.DeleteSession(c)
if err != nil {
log.Println("failed to delete session:", err)
log.Fatal("failed to delete session:", err)
}
c.Redirect(http.StatusTemporaryRedirect, "/")
}

170
auth/sessions.go

@ -3,105 +3,153 @@ package auth @@ -3,105 +3,153 @@ package auth
import (
"encoding/json"
"fmt"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
"github.com/gorilla/securecookie"
"github.com/markbates/goth"
"net/url"
"sponsorahacker/config"
"sponsorahacker/db"
"strconv"
"time"
)
type SessionManager interface {
SetSession(username string, c *gin.Context) error
GetSession(c *gin.Context) (string, error)
type SessionStore interface {
CreateSession(*gin.Context) error
GetSession(string) (Session, error)
DeleteSession(string) error
}
type SessionStore struct {
SessionDB db.Database
Store *sessions.CookieStore
type SessionManager struct {
DB *db.DBClient
}
func NewSessionManager(dbUrl string) (*SessionStore, error) {
db, err := db.NewDbClient(dbUrl)
if err != nil {
return nil, err
}
// Create sessions table if not exist
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL UNIQUE,
data BLOB NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
`)
type Session struct {
sessionId string
sessionData string
createdOn string
modifiedOn string
expiresOn string
}
if err != nil {
return nil, err
}
var secureCookie *securecookie.SecureCookie
sessionSecret := config.GetEnvVar("SESSION_SECRET")
func NewSessionManager(db *db.DBClient) SessionManager {
store := sessions.NewCookieStore([]byte(sessionSecret))
return &SessionStore{db, store}, nil
return SessionManager{DB: db}
}
func (s *SessionStore) SetSession(username string, c *gin.Context) error {
session, err := s.Store.Get(c.Request, "session")
func (s *SessionManager) CreateSession(user goth.User, c *gin.Context) error {
// create a new row that will store the user data
sessionData, err := json.Marshal(user)
if err != nil {
return err
fmt.Printf("error marshalling user: %v", err)
}
auth := Session{
sessionData: string(sessionData),
createdOn: time.Now().Format("20060102150405"),
modifiedOn: time.Now().Format("20060102150405"),
expiresOn: user.ExpiresAt.Format("20060102150405"),
}
session.Values["username"] = username
if err := session.Save(c.Request, c.Writer); err != nil {
// todo: insert the query once you figure the rest out
result, err := s.DB.Exec(`
INSERT INTO sessions (sessionData, createdOn, modifiedOn, expiresOn)
VALUES (?, ?, ?, ?);`, auth.sessionData, auth.createdOn, auth.modifiedOn, auth.expiresOn)
if result == nil {
fmt.Printf("error getting result from database while creating the session: %v", err)
return err
}
return s.saveSessionToDB(session)
}
sessionId, err := result.LastInsertId()
func (s *SessionStore) GetSession(c *gin.Context) (string, error) {
session, err := s.Store.Get(c.Request, "session")
if err != nil {
return "", err
fmt.Printf("error getting session id from database while creating the session: %v", err)
}
username, ok := session.Values["username"].(string)
if !ok {
return "", fmt.Errorf("username not found in session")
hash := []byte(config.GetEnvVar("COOKIE_HASH"))
block := []byte(config.GetEnvVar("COOKIE_BLOCK"))
secureCookie = securecookie.New(hash, block)
cookieValue := map[string]string{
"sessionId": strconv.Itoa(int(sessionId)),
}
return username, nil
}
encoded, err := secureCookie.Encode("_session", cookieValue)
func (s *SessionStore) DeleteSession(c *gin.Context) error {
session, err := s.Store.Get(c.Request, "session")
if err != nil {
fmt.Printf("error encoding cookie value: %v", err)
return err
}
session.Values["username"] = make(map[interface{}]interface{})
c.SetCookie("_session", encoded, 3600, "/", "localhost", false, true)
if err := session.Save(c.Request, c.Writer); err != nil {
return err
return nil
}
func (s *SessionManager) GetSession(session sessions.Session) (Session, error) {
// query for one row
result, err := s.DB.Query(`SELECT sessionData FROM sessions WHERE sessionId=$1 LIMIT 1`, session.ID())
// if err, then return an empty struct
if err != nil {
return Session{}, err
}
_, err = s.SessionDB.Exec("DELETE FROM sessions WHERE session_id = ?", session.ID)
// else go through the results and create a Session
for result.Next() {
var s Session
// unless there is an error, of course, then return an empty struct
if err := result.StructScan(&s); err != nil {
return Session{}, err
}
return s, nil
}
return err
// if we get nothing, well, we go nothing
return Session{}, nil
}
func (s *SessionStore) saveSessionToDB(session *sessions.Session) error {
data, err := json.Marshal(session.Values)
if err != nil {
return err
func (s *SessionManager) DeleteSession(c *gin.Context) error {
if cookie, err := c.Request.Cookie("_session"); err == nil {
value := make(map[string]string)
cookieValue, _ := url.QueryUnescape(cookie.Value)
hash := []byte(config.GetEnvVar("COOKIE_HASH"))
block := []byte(config.GetEnvVar("COOKIE_BLOCK"))
secureCookie := securecookie.New(hash, block)
err = secureCookie.Decode("_session", cookieValue, &value)
if err != nil {
fmt.Printf("error decoding cookie value: %v", err)
return err
}
sessionId := value["sessionId"]
sessionIdInt, err := strconv.Atoi(sessionId)
if err != nil {
fmt.Printf("error converting sessionId to int: %v", err)
return err
}
_, err = s.DB.Exec(`DELETE FROM sessions WHERE ID = ?`, sessionIdInt)
if err != nil {
return err
}
if err != nil {
return err
}
}
_, err = s.SessionDB.Exec(`
INSERT INTO sessions (session_id, data, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(session_id) DO UPDATE SET data = ?, updated_at = CURRENT_TIMESTAMP
`, session.ID, data, data)
c.SetCookie("_session", "", -1, "/", "localhost", false, true)
return err
return nil
}

4
db/db.go

@ -5,6 +5,7 @@ import ( @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/tursodatabase/go-libsql"
"os"
)
type Database interface {
@ -17,7 +18,8 @@ type DBClient struct { @@ -17,7 +18,8 @@ type DBClient struct {
db *sqlx.DB
}
func NewDbClient(dsn string) (Database, error) {
func NewDbClient() (*DBClient, error) {
dsn := os.Getenv("DATABASE_URL")
db, err := sqlx.Open("libsql", dsn)
if err != nil {
return nil, fmt.Errorf("failed to open db: %w", err)

11
go.mod

@ -3,8 +3,9 @@ module sponsorahacker @@ -3,8 +3,9 @@ module sponsorahacker
go 1.22
require (
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0
github.com/gorilla/securecookie v1.1.2
github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1
github.com/markbates/goth v1.80.0
@ -13,8 +14,6 @@ require ( @@ -13,8 +14,6 @@ require (
)
require (
cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
@ -28,10 +27,9 @@ require ( @@ -28,10 +27,9 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/mux v1.6.2 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.1.1 // indirect
github.com/gorilla/sessions v1.2.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kr/text v0.2.0 // indirect
@ -40,7 +38,6 @@ require ( @@ -40,7 +38,6 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect

26
go.sum

@ -1,7 +1,3 @@ @@ -1,7 +1,3 @@
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
@ -20,6 +16,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c @@ -20,6 +16,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
@ -46,18 +44,16 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ @@ -46,18 +44,16 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@ -89,8 +85,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w @@ -89,8 +85,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/nicklaw5/helix v1.25.0 h1:Mrz537izZVsGdM3I46uGAAlslj61frgkhS/9xQqyT/M=
github.com/nicklaw5/helix v1.25.0/go.mod h1:yvXZFapT6afIoxnAvlWiJiUMsYnoHl7tNs+t0bloAMw=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=

4
utils/util.go

@ -3,8 +3,8 @@ package utils @@ -3,8 +3,8 @@ package utils
import "github.com/gin-gonic/gin"
func CheckIfLoggedIn(c *gin.Context) bool {
userID, err := c.Cookie("user_id")
if err != nil || userID == "" {
cookie, err := c.Cookie("_session")
if err != nil || cookie == "" {
return false
}

Loading…
Cancel
Save