github.com/volatiletech/authboss@v2.4.1+incompatible/auth/auth.go (about) 1 // Package auth implements password based user logins. 2 package auth 3 4 import ( 5 "context" 6 "net/http" 7 8 "golang.org/x/crypto/bcrypt" 9 10 "github.com/volatiletech/authboss" 11 ) 12 13 const ( 14 // PageLogin is for identifying the login page for parsing & validation 15 PageLogin = "login" 16 ) 17 18 func init() { 19 authboss.RegisterModule("auth", &Auth{}) 20 } 21 22 // Auth module 23 type Auth struct { 24 *authboss.Authboss 25 } 26 27 // Init module 28 func (a *Auth) Init(ab *authboss.Authboss) (err error) { 29 a.Authboss = ab 30 31 if err = a.Authboss.Config.Core.ViewRenderer.Load(PageLogin); err != nil { 32 return err 33 } 34 35 a.Authboss.Config.Core.Router.Get("/login", a.Authboss.Core.ErrorHandler.Wrap(a.LoginGet)) 36 a.Authboss.Config.Core.Router.Post("/login", a.Authboss.Core.ErrorHandler.Wrap(a.LoginPost)) 37 38 return nil 39 } 40 41 // LoginGet simply displays the login form 42 func (a *Auth) LoginGet(w http.ResponseWriter, r *http.Request) error { 43 data := authboss.HTMLData{} 44 if redir := r.URL.Query().Get(authboss.FormValueRedirect); len(redir) != 0 { 45 data[authboss.FormValueRedirect] = redir 46 } 47 return a.Core.Responder.Respond(w, r, http.StatusOK, PageLogin, data) 48 } 49 50 // LoginPost attempts to validate the credentials passed in 51 // to log in a user. 52 func (a *Auth) LoginPost(w http.ResponseWriter, r *http.Request) error { 53 logger := a.RequestLogger(r) 54 55 validatable, err := a.Authboss.Core.BodyReader.Read(PageLogin, r) 56 if err != nil { 57 return err 58 } 59 60 // Skip validation since all the validation happens during the database lookup and 61 // password check. 62 creds := authboss.MustHaveUserValues(validatable) 63 64 pid := creds.GetPID() 65 pidUser, err := a.Authboss.Storage.Server.Load(r.Context(), pid) 66 if err == authboss.ErrUserNotFound { 67 logger.Infof("failed to load user requested by pid: %s", pid) 68 data := authboss.HTMLData{authboss.DataErr: "Invalid Credentials"} 69 return a.Authboss.Core.Responder.Respond(w, r, http.StatusOK, PageLogin, data) 70 } else if err != nil { 71 return err 72 } 73 74 authUser := authboss.MustBeAuthable(pidUser) 75 password := authUser.GetPassword() 76 77 r = r.WithContext(context.WithValue(r.Context(), authboss.CTXKeyUser, pidUser)) 78 79 var handled bool 80 err = bcrypt.CompareHashAndPassword([]byte(password), []byte(creds.GetPassword())) 81 if err != nil { 82 handled, err = a.Authboss.Events.FireAfter(authboss.EventAuthFail, w, r) 83 if err != nil { 84 return err 85 } else if handled { 86 return nil 87 } 88 89 logger.Infof("user %s failed to log in", pid) 90 data := authboss.HTMLData{authboss.DataErr: "Invalid Credentials"} 91 return a.Authboss.Core.Responder.Respond(w, r, http.StatusOK, PageLogin, data) 92 } 93 94 r = r.WithContext(context.WithValue(r.Context(), authboss.CTXKeyValues, validatable)) 95 96 handled, err = a.Events.FireBefore(authboss.EventAuth, w, r) 97 if err != nil { 98 return err 99 } else if handled { 100 return nil 101 } 102 103 handled, err = a.Events.FireBefore(authboss.EventAuthHijack, w, r) 104 if err != nil { 105 return err 106 } else if handled { 107 return nil 108 } 109 110 logger.Infof("user %s logged in", pid) 111 authboss.PutSession(w, authboss.SessionKey, pid) 112 authboss.DelSession(w, authboss.SessionHalfAuthKey) 113 114 handled, err = a.Authboss.Events.FireAfter(authboss.EventAuth, w, r) 115 if err != nil { 116 return err 117 } else if handled { 118 return nil 119 } 120 121 ro := authboss.RedirectOptions{ 122 Code: http.StatusTemporaryRedirect, 123 RedirectPath: a.Authboss.Paths.AuthLoginOK, 124 FollowRedirParam: true, 125 } 126 return a.Authboss.Core.Redirector.Redirect(w, r, ro) 127 }