I made my own auth (and single session auth) method that save session to redis, the method is :
- i check, is browser has cookie from my server, if is not, then create and save in browser
- check is the cookie id exist on redis if yes, next step if not redirect to login
- check what redis value by cookie id as key, the value will be username, if username exist, check what get value in redis by username, if username has cookie id value, then compare, is the cookie id same with current browser id, if it's not, redirect to login
Code
before_request :
func (hs BeforeRequest) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.RequestURI, "/login") && !strings.Contains(r.RequestURI, "/logout") {
// Check is user has `guid` cookie
Guid, err := r.Cookie("guid")
// if cookie not available, set cookie and redirect to login
if err != nil {
// Set the cookie
expiration := time.Now().Add(365 * 24 * time.Hour)
cookie := http.Cookie{Name: "guid", Value: helper.GenerateGuid(), Expires:expiration}
http.SetCookie(w, &cookie)
// Redirect to login
http.Redirect(w, r, "/login", 301)
return
} else {
// Return username that used by user (by it's Guid)
_, err := redisdb.Get(Guid.Value).Result()
if err != redis.Nil {
// Get active Guid by username, return active Guid
UsedFor, err := redisdb.Get(IsHasRedis).Result()
if err != redis.Nil && err == nil {
if UsedFor != Guid.Value {
fmt.Println("this account used in another session")
http.Redirect(w, r, "/login", 301)
return
}
} else {
// definitely not logged in
http.Redirect(w, r, "/login", 301)
return
}
} else {
// definitely not logged in
http.Redirect(w, r, "/login", 301)
return
}
}
}
// handle the request.
hs[0].ServeHTTP(w, r)
}
login :
func LoginExecute(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
err := r.ParseForm() // Must be called before writing response
if err != nil {
fmt.Println(err)
} else {
if processRequest(r) {
Username, Password := r.Form["username"], r.Form["password"]
if len(Username) > 0 && len(Password) > 0 {
if len(Username[0]) <= 20 && len(Password[0]) <= 50 {
User := structs.Users{}
database, err := helper.DataDatabase()
if err != nil {
http.Error(w, "Couldn't Connect to Database", 500)
return
}
err = database.C("users").Find(bson.M{"username": Username[0]}).One(&User)
if err == nil {
CompareError := bcrypt.CompareHashAndPassword([]byte(User.Password), []byte(Password[0]))
if CompareError == nil {
Guid, err := r.Cookie("guid")
if err == nil {
redisdb.Set(Guid.Value, Username[0], 6 * time.Hour)
redisdb.Set(Username[0], Guid.Value, 6 * time.Hour)
http.Redirect(w, r, "/", 301)
} else {
http.Redirect(w, r, "/login?err=disabled-cookie", 301)
}
} else {
http.Redirect(w, r, "/login?err=password", 301)
}
} else {
http.Redirect(w, r, "/login?err=username", 301)
}
}
}
} else {
// recaptcha failed
http.Redirect(w, r, "/login?err=username", 301)
}
}
}
the problem is, this auth method was unstable, idk why but after user succesfully login :
- access /blog redirect to login
- access /blog (with developer tool opened) working
- access /settings working
- after few minute / hours access /settings redirect to /login
- i do login, success, access /settings, redirected to /login again
yeah just unstable
note :
- i use "github.com/julienschmidt/httprouter" for routing
- "github.com/go-redis/redis" for redis