Golang web session. User authorization. How to implement? Gorilla/sessions or alternatives.

5,899 views
Skip to first unread message

dmp...@gmail.com

unread,
Jul 1, 2014, 3:11:02 AM7/1/14
to golan...@googlegroups.com
Hello everyone.

I stuck with a problem connected to implementation of "web session" in my project. I try to create user authorization.

The idea is simple.
I have a list of users. When user sends to me authorization data (login & password) I check if user with this data exists. If it does, I need to set user's id to the session (that will mean user currently is authorized).

So I need some mechanism like $_SESSION in PHP.

I already use gorilla/mux in my project for mapping the HTTP requests on handlers. So I decided to take a look to another gorilla's package: sessions. I tried to implement few simple examples from their website and got a problem.

As I understand, gorilla/sessions package provides two types of stores: CookieStore and FilesystemStore.

The first one example:

var store = sessions.NewCookieStore([]byte("randomstringfortestingpurpose"))

var Reg = RegController {
   
    Index: func(w http.ResponseWriter, r *http.Request) {

    session, error := store.Get(r, "somename")
    fmt.Println(session.Values[42])
    if error != nil {
        panic(error)
    }
    session.Values[42] = 43
    session.Save(r, w)
       
        data := map[string]interface{}{}
       
        tmpl, _ := template.ParseFiles(
            "template/main.tpl",
            "template/menu.tpl",
            "template/footer.tpl",
            "template/content/registration.tpl")
           
        tmpl.Execute(w, data)
    }
}


P.S. Index - is a field of RegController structure. To group handlers into logical groups I am using structures.

So after running this example I get an error:

2014/07/01 06:56:28 http: panic serving 10.240.40.71:45871: illegal base64 data at input byte 123

Googling didn't brought me to any solution of the error. And I can't understand the reason of this error. My code is very similar to code example provided on the gorilla's website.

Okay. Then I decided to take a look at FilesystemStore.
Changing this:
var store = sessions.NewCookieStore([]byte("randomstringfortestingpurpose"))
to this:
var store = sessions.NewFilesystemStore("")

Then I get another error: 2014/07/01 07:05:23 http: panic serving 10.240.40.71:52726: securecookie: no codecs provided

Maybe I missed something? There are no provided examples of usage of FilesystemStore.

Can anyone give me some tips:
1. Is gorilla/sessions package - something that I need for my purposes. What tools do you use to implement user authorization in your projects? Maybe there are a better choice?
2. I would like to understand what I was doing wrong in both cases of using  gorilla/sessions. What is the reason of these errors and how is it possible to solve them?
3. Maybe there are some other examples of gorilla/sessions which I missed? Because googling didn't give me any result.

Michael Banzon

unread,
Jul 1, 2014, 3:24:05 AM7/1/14
to dmp...@gmail.com, golang-nuts
Here is an example: http://shadynasty.biz/blog/2012/09/05/auth-and-sessions/

Please see the comments, specifically about security etc. (note that this is not Go-specific issues).


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Michael Banzon
http://michaelbanzon.com/

Matt Silverlock

unread,
Jul 1, 2014, 4:03:49 AM7/1/14
to golan...@googlegroups.com, dmp...@gmail.com
Can you post a functioning (even if it panics) example—something I can copy+paste into a file locally and run? 

From a quick skim though:
  • You're naming your error value "error" (the interface) and not "err" (the variable name by convention) - fix that
  • Your call to NewFilesystemStore does not specify a path or an authentication key (hence the codec issue): see the docs for how to use it - http://www.gorillatoolkit.org/pkg/sessions#NewFilesystemStore
Your flow should be something along the lines of:
  1. Call r.ParseForm to parse the POST form values
  2. Retrieve r.PostFormValue("username") and r.PostFormValue("password") as necessary
  3. Fetch the user from the DB via their username (preferably bouncing any "too long" usernames before they hit the DB)
  4. Compare the submitted-via-POST password with the hashed (preferably bcrypt or scrypt) from the DB
  5. Bounce them back to the login form with an error if they don't match
  6. If they do match, store the userID in a secure (authenticated AND encrypted) session cookie that has Secure: true and HttpOnly: true and is transmitted over HTTPS (always)...
  7. Save the session and then re-direct them to authenticated pages, using some middleware to check the userID in the session for validity (i.e. it's an authorised user).
Here's an excerpt from my app that should help explain this better:


var ErrCredentialsIncorrect = errors.New("Username and/or password incorrect.")

// AuthenticateUser authenticates a user against the database.
// It populates the session with a user ID to allow middleware to check future requests
// against the database.
func AuthenticateUser(c web.C, w http.ResponseWriter, r *http.Request) (int, error) {
session, err := store.Get(r, "somename")
if err != nil {
return 500, err
}
 
err = r.ParseForm()
if err != nil {
return 500, err
}
 
// Ensure email field is not obnoxiously long.
email := r.PostFormValue("email")
if utf8.RuneCountInString(email) > 255 {
return 400, err
}
 
user := models.User{Email: email}
password := r.PostFormValue("password")
 
exists, err := user.Get()
if err != nil {
return 500, err
}
 
// Re-direct back to the login page if the user does not exist
if !exists {
// Save error in session flash
session.AddFlash(ErrCredentialsIncorrect, "_errors")
err := session.Save(r, w)
if err != nil {
return 500, err
}
 
http.Redirect(w, r, loginURL, 302)
return 302, err
}
 
// Leverage the bcrypt package's secure comparison.
err = bcrypt.CompareHashAndPassword(user.PasswordHash, []byte(password))
if err != nil {
// Save error in session flash
session.AddFlash(ErrCredentialsIncorrect, "_errors")
err := session.Save(r, w)
if err != nil {
return 500, err
}
 
http.Redirect(w, r, loginURL, 302)
return 302, err
}
 
session.Values["userID"] = user.Id
err = session.Save(r, w)
if err != nil {
return 500, err
}
 
// Re-direct to the dashboard
http.Redirect(w, r, dashboardURL, 302)
return 302, nil
}

---- and below, just for the purposes of this mailing list thread:

var store *sessions.Store
 
func setup() {
 
var err error
// Note that both our authentication and encryption keys, respectively, are 32 bytes - as per
// http://www.gorillatoolkit.org/pkg/sessions#NewCookieStore - we need a 32 byte enc. key for AES-256 encrypted cookies
store, err = sessions.NewCookieStore([]byte("nRrHLlHcHH0u7fUz25Hje9m7uJ5SnJzP"), []byte("CAp1KsJncuMzARpetkqSFLqsBi5ag2bE")
if err != nil {
log.Fatal(err)
}
store.Options = &sessions.Options{
Path: "/",
MaxAge: 3600 * 4,
Secure: true,
HttpOnly: true,
}
}


If that doesn't help, let me know what parts don't make sense.

Daniel Theophanes

unread,
Jul 1, 2014, 10:00:56 AM7/1/14
to golan...@googlegroups.com, dmp...@gmail.com
While there are many ways to get a session, I prefer to give the client a random value and keep everything on the server.
If I need to attach data to the session, I attach it to the session token on the server. Here is one example:

It has a disk (boltdb) and memory implementation. You'd need to modify it to allow attaching arbitrary data, though that wouldn't be hard.

The parent directory has the code that calls it if you need context.

-Daniel

dmp...@gmail.com

unread,
Jul 1, 2014, 2:03:15 PM7/1/14
to golan...@googlegroups.com, dmp...@gmail.com
Thank you for your answers and examples.
I read all of them and still couldn't understand where the error is hiding.

And then finally I understood that my code is correct and there are no mistakes.

The problem was connected to environment where I tried to run my go application.
I am using cloud IDE for developing and trying to launch my app there. I think there are some restrictions which have a negative influence on my code.

When I came at home, I downloaded my project and successfully ran it on both Windows and Linux.
Reply all
Reply to author
Forward
0 new messages