1
0
mirror of https://github.com/volatiletech/authboss.git synced 2024-11-28 08:58:38 +02:00
authboss/auth/auth.go
Aaron L f70bdd5eeb Add EventAuthHijack to work around ordering issue
Lock/Confirm and possibly other authentication preemption mechanisms
hook into 'Before(EventAuth)', but the ordering of these rejection
mechanisms mixed with the 2fa acceptance response could result in a
dual response.
2018-12-16 22:50:26 -08:00

128 lines
3.4 KiB
Go

// Package auth implements password based user logins.
package auth
import (
"context"
"net/http"
"golang.org/x/crypto/bcrypt"
"github.com/volatiletech/authboss"
)
const (
// PageLogin is for identifying the login page for parsing & validation
PageLogin = "login"
)
func init() {
authboss.RegisterModule("auth", &Auth{})
}
// Auth module
type Auth struct {
*authboss.Authboss
}
// Init module
func (a *Auth) Init(ab *authboss.Authboss) (err error) {
a.Authboss = ab
if err = a.Authboss.Config.Core.ViewRenderer.Load(PageLogin); err != nil {
return err
}
a.Authboss.Config.Core.Router.Get("/login", a.Authboss.Core.ErrorHandler.Wrap(a.LoginGet))
a.Authboss.Config.Core.Router.Post("/login", a.Authboss.Core.ErrorHandler.Wrap(a.LoginPost))
return nil
}
// LoginGet simply displays the login form
func (a *Auth) LoginGet(w http.ResponseWriter, r *http.Request) error {
data := authboss.HTMLData{}
if redir := r.URL.Query().Get(authboss.FormValueRedirect); len(redir) != 0 {
data[authboss.FormValueRedirect] = redir
}
return a.Core.Responder.Respond(w, r, http.StatusOK, PageLogin, data)
}
// 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
}
// Skip validation since all the validation happens during the database lookup and
// password check.
creds := authboss.MustHaveUserValues(validatable)
pid := creds.GetPID()
pidUser, err := a.Authboss.Storage.Server.Load(r.Context(), pid)
if err == authboss.ErrUserNotFound {
logger.Infof("failed to load user requested by pid: %s", pid)
data := authboss.HTMLData{authboss.DataErr: "Invalid Credentials"}
return a.Authboss.Core.Responder.Respond(w, r, http.StatusOK, PageLogin, data)
} else if err != nil {
return err
}
authUser := authboss.MustBeAuthable(pidUser)
password := authUser.GetPassword()
r = r.WithContext(context.WithValue(r.Context(), authboss.CTXKeyUser, pidUser))
var handled bool
err = bcrypt.CompareHashAndPassword([]byte(password), []byte(creds.GetPassword()))
if err != nil {
handled, err = a.Authboss.Events.FireAfter(authboss.EventAuthFail, w, r)
if err != nil {
return err
} else if handled {
return nil
}
logger.Infof("user %s failed to log in", pid)
data := authboss.HTMLData{authboss.DataErr: "Invalid Credentials"}
return a.Authboss.Core.Responder.Respond(w, r, http.StatusOK, PageLogin, data)
}
r = r.WithContext(context.WithValue(r.Context(), authboss.CTXKeyValues, validatable))
handled, err = a.Events.FireBefore(authboss.EventAuth, w, r)
if err != nil {
return err
} else if handled {
return nil
}
handled, err = a.Events.FireBefore(authboss.EventAuthHijack, w, r)
if err != nil {
return err
} else if handled {
return nil
}
logger.Infof("user %s logged in", pid)
authboss.PutSession(w, authboss.SessionKey, pid)
authboss.DelSession(w, authboss.SessionHalfAuthKey)
handled, err = a.Authboss.Events.FireAfter(authboss.EventAuth, w, r)
if err != nil {
return err
} else if handled {
return nil
}
ro := authboss.RedirectOptions{
Code: http.StatusTemporaryRedirect,
RedirectPath: a.Authboss.Paths.AuthLoginOK,
FollowRedirParam: true,
}
return a.Authboss.Core.Redirector.Redirect(w, r, ro)
}