Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement shallow remember me #212

Closed
frederikhors opened this issue Jan 5, 2019 · 8 comments
Closed

Implement shallow remember me #212

frederikhors opened this issue Jan 5, 2019 · 8 comments

Comments

@frederikhors
Copy link
Contributor

frederikhors commented Jan 5, 2019

Issue opened for the creation of a wiki page that summarizes the doubts and problems for newbies (#210).

It would be amazing to use "Remember me" feature not with DB token queries, just using the cookie MaxAge > 0 if checkbox checked (like Rails devise gem does).

Is there a way to do:

if remember_me checkobx checked then cookie MaxAge = 30 days
else MaxAge = 0

Maybe I'm wrong about this because maybe I did not understand how authboss remember_me module works. In that case I will create a wiki page to explain this to newbies like me.

@frederikhors
Copy link
Contributor Author

How to enter in the "login" phase of authboss?

Is there any hook? For example: onLogin(func)?

@aarondl
Copy link
Member

aarondl commented Jan 6, 2019

I'm confused about both parts of your post.

Can you further explain how devise does this? One of the reasons to use database based things however is so that you can delete them server-side and disable the clients ability to activate remember me.

There is no login phase (it's stateless unless you're using 2fa). You simply GET /mountpoint/login, then POST credentials to /mountpoint/login

@frederikhors
Copy link
Contributor Author

My bad, sorry.

What I mean is "login function".

Authboss "remember me" feature is something different from what I imagine.

  • How it woks "authboss remember me" now: if enabled it sets a cookie with token and saves token in DB. Every new visit the user is half-authed and so on...

  • What I imagine is: (without remember me module), the user is logging in and check the checkbox "Remember me". In the login function (which I'm searching in the code, I think in /auth/auth.go, the LoginPost one, but I'm not sure still) I can verify the checkbox value and if true I can edit on the fly the cstore.Options.MaxAge value in something >0.

What do you think about this, @aarondl?

Can we call it "shallow remember me"?

@frederikhors frederikhors changed the title Remember me not with DB token queries, just the cookie MaxAge > 0 if checkbox checked Implements shallow remember me Jan 6, 2019
@frederikhors
Copy link
Contributor Author

Something like:

/auth/auth.go:

// LoginPost attempts to validate the credentials passed in
// to log in a user.
func (a *Auth) LoginPost(w http.ResponseWriter, r *http.Request) error {
	logger := a.RequestLogger(r)

	validatable, err := a.Authboss.Core.BodyReader.Read(PageLogin, r)
	if err != nil {
		return err
	}

	if r.Form.Get("remember_me") == "on" {
		fmt.Println("Yes, shallow remember me please!")
		a.Config.Storage.SessionState.Store.Options.MaxAge = 30 * 60 *60 *24 // this obviously doesn't work as it is now
	}
...

@frederikhors frederikhors changed the title Implements shallow remember me Implement shallow remember me Jan 6, 2019
@frederikhors
Copy link
Contributor Author

This works, but:

if r.Form.Get("remember_me") == "on" {
	fmt.Println("Yes, shallow remember me please!")

	oldCookie, _ := r.Cookie("MyCookieName")

	newCookie := oldCookie
	newCookie.MaxAge = 30 * 60 * 60 * 24
	// newCookie.Domain = "" // path is every time "auth", why?
	// newCookie.Path = "" // also with this path is every time "auth", why?

	http.SetCookie(w, newCookie)
}

but there is a problem: path is every time /auth, why?

@frederikhors
Copy link
Contributor Author

I fixed it using newCookie.Path = "/" but now I noticed cookie is rewritten (because it change the session I think and my cookie MaxAge is missing. LoginPost is not the correct place.

I cannot use this code in my chi router because authboss login routes are under r.Mount(AUTH_URL, http.StripPrefix(AUTH_URL, ab.Config.Core.Router)) and I cannot read login page form from there.

@frederikhors
Copy link
Contributor Author

I can use this, but:

r.Group(func(r chi.Router) {
	r.Use(authboss.ModuleListMiddleware(ab))
	r.Use(checkShallowRememberMe)
	r.Mount(AUTH_URL, http.StripPrefix(AUTH_URL, ab.Config.Core.Router))
})

func checkShallowRememberMe(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		err := r.ParseForm()
		utility.CheckErr(err)

		ctx := r.Context()
		if r.Form.Get("remember_me") == "on" {
			oldSessionState := ctx.Value(authboss.CTXKeySessionState).(*abclientstate.SessionState)
			oldSessionState.Options.MaxAge = 30*60*60*24*1000 // this doesn't work because Options is not in SessionState
			ctx = context.WithValue(ctx, authboss.CTXKeySessionState, oldSessionState)
		}

		next.ServeHTTP(w, r.WithContext(ctx))
	}
	return http.HandlerFunc(fn)
}

I still don't know how to find Options.MaxAge... work in progress! :)

@aarondl
Copy link
Member

aarondl commented Jan 6, 2019

In order to control max age - you have to implement an authboss.ClientStateReadWriter. Because Authboss doesn't understand anything except simple key-value storage when it comes to sessions. It doesn't understand max-age or secure or httponly flags or any of that. That's all abstracted behind the ClientStateReadWriter.

Also - there is a checkbox in Authboss for remember me as well. The only difference is it uses the database and tokens.

Having MaxAge = 0 is also an oddity because it means a user (or an attacker) can indefinitely stay logged in. With sites that have aggressive expire settings to log users out - a MaxAge = 0 cookie circumvents that and so we won't do it that way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants