How to save a session in golang - session

I'm trying to save a logged user ID in my golang backend with gorilla sessions and securecookie.
Here is my package session :
package session
import (
"fmt"
"net/http"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
)
var store = sessions.NewCookieStore(securecookie.GenerateRandomKey(32))
//GetSessionLoggedID returns loggedID
func GetSessionLoggedID(r *http.Request) int {
storeAuth, _ := store.Get(r, "authentication")
if auth, ok := storeAuth.Values["loggedID"].(bool); ok && auth {
return storeAuth.Values["loggedID"].(int)
}
fmt.Println("none found")
return 0
}
//SetSessionLoggedID sets cookie session user ID
func SetSessionLoggedID(w http.ResponseWriter, r *http.Request, id int) {
storeAuth, err := store.Get(r, "authentication")
if err != nil {
fmt.Println(err.Error())
}
storeAuth.Options = &sessions.Options{HttpOnly: true, Secure: true, MaxAge: 2628000, Path: "/"}
storeAuth.Values["loggedID"] = id
storeAuth.Save(r, w)
}
I have another package that gets to verify email / password of a user that logs in.
Here is the function :
func (handler *UserHandler) checkPassword(w http.ResponseWriter, r *http.Request) {
var body struct {
Email string
Password string
}
err := json.NewDecoder(r.Body).Decode(&body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
loggedID, err := handler.UserUsecase.PasswordMatch(body.Email, body.Password)
if err != nil || loggedID == 0 {
http.Error(w, "Could not authenticate user", http.StatusUnauthorized)
return
}
session.SetSessionLoggedID(w, r, loggedID)
json.NewEncoder(w).Encode(struct {
ID int `json:"id"`
}{loggedID})
}
The ID returned is the proper one. But the session is not saving as I would have liked.
If I add a session.GetSessionLoggedID(r) at the end of checkpassword function, I get "none found".
What am I missing ?

// watch this line
if auth, ok := storeAuth.Values["loggedID"].(bool); ok && auth {
storeAuth.Values["loggedID"] is not bool, so ok is false, then you get "none found"
Change to
if auth, ok := storeAuth.Values["loggedID"]; ok{
return auth.(int)
}
fmt.Println("none found")

Related

How to clear the session and only visit an about page after login?

This code has two parts. One is to set and clear the session and the second part is login and logout.
What it does?
In the second part, If an email and password are found in the database and the match is true then it set the session and move to the about() function which has an about file. If the logout is called then it clears the session and redirects to the home page.
What it should do?
The problem is that even if I am logged out and the session is cleared, I can still visit an about page. I don't want to be allowed to visit an about page if I am not logged in.
First part
var cookieHandler = securecookie.New(
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32),
)
func setSession(email, password string, res http.ResponseWriter) {
value := map[string]string{ "email": email, "password": password}
encoded, err := cookieHandler.Encode("session", value)
if err == nil {
cookie := &http.Cookie{ Name: "session", Value: encoded, Path: "/"}
http.SetCookie(res, cookie)
}
}
func clearSession(res http.ResponseWriter) {
cookie := &http.Cookie{ Name: "session", Value: "", Path: "/", MaxAge: -1}
http.SetCookie(res, cookie)
}
Second part
func about(res http.ResponseWriter, req *http.Request) {
if err := tmpl.ExecuteTemplate(res, "about.html", nil); err != nil {
log.Fatal("template didn't execute", nil)
}
}
func loginAuth(res http.ResponseWriter, req *http.Request) {
email := req.FormValue("email")
password := req.FormValue("password")
match := database.Findaccount(email, password)
if match == true {
setSession(email, password, res)
about(res, req)
fmt.Println("You're logged in")
} else {
tmpl.ExecuteTemplate(res, "login.html", nil)
fmt.Println("Enter the correct email or password")
}
}
func logout(res http.ResponseWriter, req *http.Request) {
clearSession(res)
http.Redirect(res, req, "/", 302)
}
Few things you don't want to do, in general:
Don't use cookie encoder directly. Use a cookie session store.
Don't call an handler within an handler, prefer a redirect. This should prevent writing twice the headers/body on the response.
Don't pass the user/password in the cookie, even encoded, in 2021 we may even prevent sending that through the form at all (you might consider sending only a hash and re hash the hash on the server side to figure out if things are good to go).
Few things you always want to do:
Write tests.
Make use of middlewares.
Always provide small reproducible examples.
That being said, I have written a lookalike code with some stubs (mostly for db), I removed template support (i was not in the mood to write HTML) and more importantly I wrote tests !!
To the question How to clear the session :
Delete the values from the store, write the store
To the question and only visit an about page after login?:
Wrap that handler with a middleware that verifies login data attached to the user cookie store
-- main.go --
package main
import (
"crypto/sha256"
"encoding/gob"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
)
// Note: Don't store your key in your source code. Pass it via an
// environmental variable, or flag (or both), and don't accidentally commit it
// alongside your code. Ensure your key is sufficiently random - i.e. use Go's
// crypto/rand or securecookie.GenerateRandomKey(32) and persist the result.
var store = sessions.NewCookieStore(
securecookie.GenerateRandomKey(32),
)
//emulate db package
func dbLookupUser(user, pwd string) bool {
return user == "user" && pwd == "pwd"
}
func dbLookupHash(h string) bool {
return h == hash("user", "pwd")
}
func hash(s ...interface{}) string {
hr := sha256.New()
fmt.Fprint(hr, s...)
return fmt.Sprintf("%x", hr.Sum(nil))
}
// hashKey is a typed key for the session map store to prevent unintented overwrites.
type hashKey string
func init() {
gob.Register(hashKey(""))
}
func loginAuth(res http.ResponseWriter, req *http.Request) {
email := req.FormValue("email")
password := req.FormValue("password")
match := dbLookupUser(email, password)
if match == true {
session, _ := store.Get(req, "session-name")
session.Values["hash"] = hash(email, password)
// Save it before we write to the response/return from the handler.
err := session.Save(req, res)
if err == nil {
// about(res, req) // don't!
// the about handler might want to setup its own http response headers
// That would conflict with what we did here.
// prefer a redirect
http.Redirect(res, req, "/about", http.StatusFound)
return
}
} else {
fmt.Fprintf(res, "try again") // use a templatee instead!
// tmpl.ExecuteTemplate(res, "login.html", nil)
}
}
func logout(res http.ResponseWriter, req *http.Request) {
session, _ := store.Get(req, "session-name")
delete(session.Values, hashKey("hash"))
_ = session.Save(req, res)
http.Redirect(res, req, "/", 302)
}
func about(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "welcome to about page")
}
func requireLogin(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-name")
var h string
if x, ok := session.Values[hashKey("hash")]; ok {
h = x.(string)
}
var match bool
if h != "" {
match = dbLookupHash(h)
}
if !match {
// Write an error and stop the handler chain
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next(w, r)
}
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", loginAuth)
r.HandleFunc("/logout", logout)
r.HandleFunc("/about", requireLogin(about))
log.Fatal(http.ListenAndServe("localhost:8080", r))
}
-- main_test.go --
package main
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func TestLogin(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
form := url.Values{}
form.Set("email", "user")
form.Set("password", "pwd")
req.Form = form
w := httptest.NewRecorder()
loginAuth(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusFound; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
func TestLoginFailure(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
form := url.Values{}
form.Set("email", "!user")
form.Set("password", "!pwd")
req.Form = form
w := httptest.NewRecorder()
loginAuth(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusOK; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
func TestAboutNotLogged(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
w := httptest.NewRecorder()
requireLogin(about)(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusForbidden; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
func TestAboutLogged(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
w := httptest.NewRecorder()
session, _ := store.Get(req, "session-name")
session.Values[hashKey("hash")] = hash("user", "pwd")
err := session.Save(req, w)
if err != nil {
t.Fatal(err)
}
hdr := w.Header()
req.Header.Add("Cookie", hdr["Set-Cookie"][0])
w = httptest.NewRecorder()
requireLogin(about)(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusOK; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}

How to validate API key in go-gin framework?

So I currently have a function that will take in a string APIKey to check it against my MongoDB collection. If nothing is found (not authenticated), it returns false - if a user is found, it returns true. My problem, however, is I'm unsure how to integrate this with a Gin POST route. Here is my code:
import (
"context"
"fmt"
"log"
"os"
"github.com/gin-gonic/gin"
_ "github.com/joho/godotenv/autoload"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Name string
APIKey string
}
func validateAPIKey(users *mongo.Collection, APIKey string) bool {
var user User
filter := bson.D{primitive.E{Key: "APIKey", Value: APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
return false
}
fmt.Printf("Found: %s\n", user.Name)
return true
}
func handleUpload(c *gin.Context) {
}
func main() {
r := gin.Default()
api := r.Group("/api")
v1 := api.Group("/v1")
v1.POST("/upload", handleUpload)
mongoURI := os.Getenv("MONGO_URI")
mongoOptions := options.Client().ApplyURI(mongoURI)
client, err := mongo.Connect(context.TODO(), mongoOptions)
if err != nil {
log.Fatal(err, "Unable to access MongoDB server, exiting...")
}
defer client.Disconnect(context.TODO())
// users := client.Database("sharex_api").Collection("authorized_users") // commented out when testing to ignore unused warnings
r.Run(":8085")
}
The validateAPIKey function works exactly as intended if tested alone, I am just unsure how I would run this function for a specific endpoint (in this case, /api/v1/upload) and pass in the users collection.
After a bit of searching, I found a resolution. I changed my validateAPIKey function to return git.HandlerFunc. Here's the code:
func validateAPIKey(users *mongo.Collection) gin.HandlerFunc {
return func(c *gin.Context) {
var user authorizedUser
APIKey := c.Request.Header.Get("X-API-Key")
filter := bson.D{primitive.E{Key: "APIKey", Value: APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
c.JSON(http.StatusUnauthorized, gin.H{"status": 401, "message": "Authentication failed"})
return
}
return
}
}
For the route, I have the following:
v1.POST("/upload", validateAPIKey(users), handleUpload)

Unable to access login page in Golang

I am new to Golang. I have made a demo app for practice in which i have login register and homepage. When i go to login page it redirects to home page . I am not understanding what is happening.
This is my go code
package main
import (
"database/sql"
"fmt"
"net/http"
_ "github.com/go-sql-driver/mysql"
"golang.org/x/crypto/bcrypt"
)
var db *sql.DB
var err error
func signupPage(res http.ResponseWriter, req *http.Request) {
fmt.Println("entered Signup")
if req.Method != "POST" {
http.ServeFile(res, req, "template/signup.html")
return
}
email := req.FormValue("email")
password := req.FormValue("password")
var user string
err := db.QueryRow("SELECT email FROM users WHERE email=?", email).Scan(&user)
switch {
case err == sql.ErrNoRows:
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
http.Error(res, "Server error, unable to create your account.", 500)
return
}
_, err = db.Exec("INSERT INTO users(email, password) VALUES(?, ?)", email, hashedPassword)
if err != nil {
http.Error(res, "Server error, unable to create your account.", 500)
}
res.Write([]byte("User Registered Successfully"))
return
case err != nil:
http.Error(res, "Server error, unable to create your account.", 500)
return
default:
http.Redirect(res, req, "/", 301)
}
}
func loginPage(res http.ResponseWriter, req *http.Request) {
fmt.Println("Entered login")
if req.Method != "POST" {
http.ServeFile(res, req, "template/login.html")
return
}
email := req.FormValue("email")
password := req.FormValue("password")
var dbemail string
var dbpassword string
err := db.QueryRow("SELECT email, password FORM users WHERE email=?", email).Scan(&dbemail, &dbpassword)
if err != nil {
http.Redirect(res, req, "/login", 301)
return
}
err = bcrypt.CompareHashAndPassword([]byte(dbpassword), []byte(password))
if err != nil {
http.Redirect(res, req, "/login", 301)
return
}
res.Write([]byte("Hello" + dbemail))
}
func homePage(res http.ResponseWriter, req *http.Request) {
http.ServeFile(res, req, "template/landing.html")
}
func main() {
db, err = sql.Open("mysql", "root:password#/golang_demo")
if err != nil {
panic(err.Error())
} else {
fmt.Println("Database connected successfully")
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err.Error())
}
http.HandleFunc("/login", loginPage)
http.HandleFunc("/", homePage)
http.HandleFunc("/signup", signupPage)
http.ListenAndServe(":9090", nil)
}
When i go to signup page it goes successfully. But when i go to login page it is redirecting me to home page. Please help!
This is just an educated guess, since from the code I am seeing it doesn't look like that should happen. Since you are using 301 redirects (Moved Permanently), I would guess that at some point in writing your function and testing it, you did a redirect to the home page. Since it is "moved permanently", the browser now doesn't even ask the server if it should redirect when it sees that URL, it just does it.
If you're in chrome, open the dev tools, and disabling the cache should resolve it. Or even better, try it in a different browser that you haven't used on this site yet, and see if it works there.

Using Colly framework I can't login to the Evernote account

I am using colly framework for scraping the website. Am trying to login the Evernote account for scraping some things. But I can't go through it. I used "username" and "password" titles for giving the credentials. Is this the right way ?.
Thank you in advance.
package main
import (
"log"
"github.com/gocolly/colly"
)
func main() {
// create a new collector
c := colly.NewCollector()
// authenticate
err := c.Post("https://www.evernote.com/Login.action",
map[string]string{"username":
"XXXXXX#XXX.com", "password": "*********"})
if err != nil {
log.Fatal("Error : ",err)
}
// attach callbacks after login
c.OnResponse(func(r *colly.Response) {
log.Println("response received", r.StatusCode)
})
// start scraping
c.Visit("https://www.evernote.com/")
}
You should try to mimic the browser behavior, take a look at this implementation, I've added comments on each step:
package evernote
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"regexp"
"strings"
)
const (
evernoteLoginURL = "https://www.evernote.com/Login.action"
)
var (
evernoteJSParamsExpr = regexp.MustCompile(`document.getElementById\("(.*)"\).value = "(.*)"`)
evernoteRedirectExpr = regexp.MustCompile(`Redirecting to <a href="(.*)">`)
errNoMatches = errors.New("No matches")
errRedirectURL = errors.New("Redirect URL not found")
)
// EvernoteClient wraps all methods required to interact with the website.
type EvernoteClient struct {
Username string
Password string
httpClient *http.Client
// These parameters persist during the login process:
hpts string
hptsh string
}
// NewEvernoteClient initializes a new Evernote client.
func NewEvernoteClient(username, password string) *EvernoteClient {
// Allocate a new cookie jar to mimic the browser behavior:
cookieJar, _ := cookiejar.New(nil)
// Fill up basic data:
c := &EvernoteClient{
Username: username,
Password: password,
}
// When initializing the http.Client, copy default values from http.DefaultClient
// Pass a pointer to the cookie jar that was created earlier:
c.httpClient = &http.Client{
Transport: http.DefaultTransport,
CheckRedirect: http.DefaultClient.CheckRedirect,
Jar: cookieJar,
Timeout: http.DefaultClient.Timeout,
}
return c
}
func (e *EvernoteClient) extractJSParams(body []byte) (err error) {
matches := evernoteJSParamsExpr.FindAllSubmatch(body, -1)
if len(matches) == 0 {
return errNoMatches
}
for _, submatches := range matches {
if len(submatches) < 3 {
err = errNoMatches
break
}
key := submatches[1]
val := submatches[2]
if bytes.Compare(key, hptsKey) == 0 {
e.hpts = string(val)
}
if bytes.Compare(key, hptshKey) == 0 {
e.hptsh = string(val)
}
}
return nil
}
// Login handles the login action.
func (e *EvernoteClient) Login() error {
// First step: fetch the login page as a browser visitor would do:
res, err := e.httpClient.Get(evernoteLoginURL)
if err != nil {
return err
}
if res.Body == nil {
return errors.New("No response body")
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
err = e.extractJSParams(body)
if err != nil {
return err
}
// Second step: we have extracted the "hpts" and "hptsh" parameters
// We send a request using only the username and setting "evaluateUsername":
values := &url.Values{}
values.Set("username", e.Username)
values.Set("evaluateUsername", "")
values.Set("analyticsLoginOrigin", "login_action")
values.Set("clipperFlow", "false")
values.Set("showSwitchService", "true")
values.Set("hpts", e.hpts)
values.Set("hptsh", e.hptsh)
rawValues := values.Encode()
req, err := http.NewRequest(http.MethodPost, evernoteLoginURL, bytes.NewBufferString(rawValues))
if err != nil {
return err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
req.Header.Set("x-requested-with", "XMLHttpRequest")
req.Header.Set("referer", evernoteLoginURL)
res, err = e.httpClient.Do(req)
if err != nil {
return err
}
body, err = ioutil.ReadAll(res.Body)
if err != nil {
return err
}
bodyStr := string(body)
if !strings.Contains(bodyStr, `"usePasswordAuth":true`) {
return errors.New("Password auth not enabled")
}
// Third step: do the final request, append password to form data:
values.Del("evaluateUsername")
values.Set("password", e.Password)
values.Set("login", "Sign in")
rawValues = values.Encode()
req, err = http.NewRequest(http.MethodPost, evernoteLoginURL, bytes.NewBufferString(rawValues))
if err != nil {
return err
}
req.Header.Set("Accept", "text/html")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
req.Header.Set("x-requested-with", "XMLHttpRequest")
req.Header.Set("referer", evernoteLoginURL)
res, err = e.httpClient.Do(req)
if err != nil {
return err
}
// Check the body in order to find the redirect URL:
body, err = ioutil.ReadAll(res.Body)
if err != nil {
return err
}
bodyStr = string(body)
matches := evernoteRedirectExpr.FindAllStringSubmatch(bodyStr, -1)
if len(matches) == 0 {
return errRedirectURL
}
m := matches[0]
if len(m) < 2 {
return errRedirectURL
}
redirectURL := m[1]
fmt.Println("Login is ok, redirect URL:", redirectURL)
return nil
}
After you successfully get the redirect URL, you should be able to send authenticated requests as long as you keep using the HTTP client that was used for the login process, the cookie jar plays a very important role here.
To call this code use:
func main() {
evernoteClient := NewEvernoteClient("user#company", "password")
err := evernoteClient.Login()
if err != nil {
panic(err)
}
}

How to pass multiple values in cookie with map

Got stuck while passing multiple values in cookie. I'm not able to find a better way for managing session and cookies. Trying to use github.com/gorilla/securecookie this package.
loginMain.go
package main
import (
"database/sql"
"log"
"net/http"
"shambhavi/packages/loginPkg"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
var router = mux.NewRouter()
var db *sql.DB
func connectDb() *sql.DB {
db, dberr := sql.Open("mysql", "root:root#tcp(127.0.0.1:8889)/shambhavi_db")
if dberr != nil {
log.Println(dberr)
}
return db
}
func login(w http.ResponseWriter, r *http.Request) {
var db *sql.DB = connectDb()
loginPkg.LoginOperation(w, r, db)
}
func main() {
http.HandleFunc("/demo", login)
http.Handle("/", router)
err := http.ListenAndServe(port, nil) // setting listening port
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
LoginPkg.go
package loginPkg
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"shambhavi/packages/sessionPkg"
_ "github.com/go-sql-driver/mysql"
)
var retMap = make(map[string]string)
func ErrorHandler(err error) {
if err != nil {
panic(err)
}
}
func LoginOperation(w http.ResponseWriter, r *http.Request, db *sql.DB) {
fmt.Println("In LoginOperation ")
r.ParseForm()
if len(r.Form["username"][0]) == 0 && len(r.Form["password"][0]) == 0 {
fmt.Fprintf(w, "Something is blank !!!")
} else {
var lvl string
var uFullName string
err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)
er := db.QueryRow("SELECT emp_name FROM emp_detail WHERE emp_uname = ?", r.FormValue("username")).Scan(&uFullName)
ErrorHandler(er)
retMap["msg"] = "Login successfully"
retMap["err"] = "Not Login"
retMap["lvl"] = lvl
retMap["fullName"] = uFullName
b, _ := json.Marshal(retMap)
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, "%s", b)
} else {
if lvl == "1" || lvl == "2" || lvl == "3" {
sessionPkg.SetSession(w, r, r.FormValue("username"), retMap) // Passing map to the fun, retMap
fmt.Fprintf(w, "%s", b)
usrnm := sessionPkg.GetUserName(r)
fmt.Println("From session variable", usrnm)
} else {
fmt.Println("Chukala ....")
fmt.Fprintf(w, "%s", b)
}
}
}
defer db.Close()
}
The problem lies in following file....
sessionHandler.go
package sessionPkg
import (
"fmt"
"net/http"
"time"
"github.com/gorilla/securecookie"
)
var cookieHandler = securecookie.New(
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32))
func SetSession(w http.ResponseWriter, r *http.Request, username string, retMap map[string]string) {
sessionData := map[string]string{
"userName": username,
"lvl": retMap["lvl"],
"fullName": retMap["fullName"],
}
expiration := time.Now().Add(365 * 24 * time.Hour)
//if encoded, err := cookieHandler.Encode("session", sessionData); err == nil {
cookie := http.Cookie{
Name: "session",
Value: sessionData["userName"], //Here i want map or something else that can accept multiple values
Expires: expiration,
//MaxAge: 3600,
}
http.SetCookie(w, &cookie)
//}
}
func GetUserName(request *http.Request) (userName string) {
//fmt.Println(request.Cookie("session"))
cookieValue := make(map[string]string)
if cookie, err := request.Cookie("session"); err == nil {
fmt.Println("cookieValue = ", cookie.Value)
//if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
//fmt.Println(cookie)
cookieValue["userName"] = cookie.Value
//fmt.Println(cookieValue["userName"])
//}
/*else {
fmt.Println("Error ", err)
}*/
}
return cookieValue["userName"]
}
/*func GetFullName(request *http.Request) (fullName string) {
fmt.Println("In GetFullName")
cookieValue := make(map[string]string)
if cookie2, err := request.Cookie("session"); err == nil {
fmt.Println("cookieValue = ", cookie2.Value)
//if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
fmt.Println(cookie2)
cookieValue["fullName"] = cookie2.Value
fmt.Println(cookieValue["fullName"])
//}
}
return cookieValue["fullName"]
}*/
func ClearSession(response http.ResponseWriter) {
cookie := &http.Cookie{
Name: "session",
Value: "",
Path: "/",
MaxAge: -1,
}
http.SetCookie(response, cookie)
}
Problem indicated in code by comment. I want to use session like in PHP. Suggest the better way to secure the cookie and maintain the session. Give some explanation too.
Edited: Explain cookieHandler.Encode() and cookieHandler.Decode(). It is not decoding the data which is passed to it.
var retMap = make(map[string]string) is a global map that you both read and write to, which is unsafe: when you have more than one user concurrently you will be overwriting the contents of this map.
You aren't using the securecookie package to encode your cookie values at all - in fact it's not clear where you're using it at all.
cookie := http.Cookie{
Name: "session",
// This should be encoded.
Value: sessionData["userName"], //Here i want map or something else that can accept multiple values
Expires: expiration,
//MaxAge: 3600,
}
Too many packages: you have a loginpackage, a sessionpackage and a package main. Packages should fulfil a 'theme' - an auth package might make more sense, or even just a single package main until you get more familiar with Go.
You're not hashing your passwords - storing the plain text password in the database and looking it up with the value of r.FormValue("password") is extremely insecure. Read this to see how to hash passwords safely in Go: Golang/App Engine - securely hashing a user's password
You should be using the gorilla/sessions package rather than the lower-level securecookie package.
Modifying the gorilla/sessions example in the documentation:
package main
import (
"net/http"
"github.com/gorilla/sessions"
)
// Use the CookieStore
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func LoginOperation(w http.ResponseWriter, r *http.Request) {
// Your existing DB code - shorter to make the example here clearer
err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)
// Don't use a global map - create a new one
userDetails := make(map[string]string)
userDetails["msg"] = "Login successfully"
userDetails["err"] = "Not Login"
userDetails["lvl"] = lvl
userDetails["fullName"] = uFullName
// Get a session (existing/new)
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Set some session values.
session.Values["userDetails"] = userDetails
// Save it before we write to the response/return from the handler.
session.Save(r, w)
}
Later, if you want to retrieve the details:
func RequireAuth(w http.ResponseWriter, r *http.Request) {
// Get a session (existing/new)
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Type assert our userDetails map out of the session's map[string]interface{}
userDetails, ok := session.Values["userDetails"].(map[string]string)
if !ok {
// User does not have an existing session - treat them as not logged in and/or re-direct them to your login page.
http.Error(w, http.StatusCode(401), 401)
return
}
// Check the user details - e.g. if userDetails["lvl"] == "ACTIVE" { ... }
// TODO
}

Resources