func (rec *Recover) startHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error { switch r.Method { case methodGET: data := authboss.NewHTMLData( "primaryID", rec.PrimaryID, "primaryIDValue", "", "confirmPrimaryIDValue", "", ) return rec.templates.Render(ctx, w, r, tplRecover, data) case methodPOST: primaryID := r.FormValue(rec.PrimaryID) confirmPrimaryID := r.FormValue(fmt.Sprintf("confirm_%s", rec.PrimaryID)) errData := authboss.NewHTMLData( "primaryID", rec.PrimaryID, "primaryIDValue", primaryID, "confirmPrimaryIDValue", confirmPrimaryID, ) policies := authboss.FilterValidators(rec.Policies, rec.PrimaryID) if validationErrs := authboss.Validate(r, policies, rec.PrimaryID, authboss.ConfirmPrefix+rec.PrimaryID).Map(); len(validationErrs) > 0 { errData.MergeKV("errs", validationErrs) return rec.templates.Render(ctx, w, r, tplRecover, errData) } // redirect to login when user not found to prevent username sniffing if err := ctx.LoadUser(primaryID); err == authboss.ErrUserNotFound { return authboss.ErrAndRedirect{err, rec.RecoverOKPath, recoverInitiateSuccessFlash, ""} } else if err != nil { return err } email, err := ctx.User.StringErr(authboss.StoreEmail) if err != nil { return err } encodedToken, encodedChecksum, err := newToken() if err != nil { return err } ctx.User[StoreRecoverToken] = encodedChecksum ctx.User[StoreRecoverTokenExpiry] = time.Now().Add(rec.RecoverTokenDuration) if err := ctx.SaveUser(); err != nil { return err } goRecoverEmail(rec, email, encodedToken) ctx.SessionStorer.Put(authboss.FlashSuccessKey, recoverInitiateSuccessFlash) response.Redirect(ctx, w, r, rec.RecoverOKPath, "", "", true) default: w.WriteHeader(http.StatusMethodNotAllowed) } return nil }
func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error { switch r.Method { case methodGET: ctx.SessionStorer.Del(authboss.SessionKey) ctx.CookieStorer.Del(authboss.CookieRemember) ctx.SessionStorer.Del(authboss.SessionLastAction) response.Redirect(ctx, w, r, a.AuthLogoutOKPath, "You have logged out", "", true) default: w.WriteHeader(http.StatusMethodNotAllowed) } return nil }
func (c *Confirm) confirmHandler(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error { token := r.FormValue(FormValueConfirm) if len(token) == 0 { return authboss.ClientDataErr{Name: FormValueConfirm} } toHash, err := base64.URLEncoding.DecodeString(token) if err != nil { return authboss.ErrAndRedirect{ Location: "/", Err: fmt.Errorf("confirm: token failed to decode %q => %v\n", token, err), } } sum := md5.Sum(toHash) dbTok := base64.StdEncoding.EncodeToString(sum[:]) user, err := ctx.Storer.(ConfirmStorer).ConfirmUser(dbTok) if err == authboss.ErrUserNotFound { return authboss.ErrAndRedirect{Location: "/", Err: errors.New("confirm: token not found")} } else if err != nil { return err } ctx.User = authboss.Unbind(user) ctx.User[StoreConfirmToken] = "" ctx.User[StoreConfirmed] = true key, err := ctx.User.StringErr(c.PrimaryID) if err != nil { return err } if err := ctx.SaveUser(); err != nil { return err } ctx.SessionStorer.Put(authboss.SessionKey, key) response.Redirect(ctx, w, r, c.RegisterOKPath, "You have successfully confirmed your account.", "", true) return nil }
func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error { switch r.Method { case methodGET: data := authboss.NewHTMLData( "showRemember", a.IsLoaded("remember"), "showRecover", a.IsLoaded("recover"), "showRegister", a.IsLoaded("register"), "primaryID", a.PrimaryID, "primaryIDValue", "", ) return a.templates.Render(ctx, w, r, tplLogin, data) case methodPOST: key, _ := ctx.FirstPostFormValue(a.PrimaryID) password, _ := ctx.FirstPostFormValue("password") errData := authboss.NewHTMLData( "error", fmt.Sprintf("invalid %s and/or password", a.PrimaryID), "primaryID", a.PrimaryID, "primaryIDValue", key, "showRemember", a.IsLoaded("remember"), "showRecover", a.IsLoaded("recover"), "showRegister", a.IsLoaded("register"), ) if valid, err := validateCredentials(ctx, key, password); err != nil { errData["error"] = "Internal server error" fmt.Fprintf(a.LogWriter, "auth: validate credentials failed: %v\n", err) return a.templates.Render(ctx, w, r, tplLogin, errData) } else if !valid { if err := a.Callbacks.FireAfter(authboss.EventAuthFail, ctx); err != nil { fmt.Fprintf(a.LogWriter, "EventAuthFail callback error'd out: %v\n", err) } return a.templates.Render(ctx, w, r, tplLogin, errData) } interrupted, err := a.Callbacks.FireBefore(authboss.EventAuth, ctx) if err != nil { return err } else if interrupted != authboss.InterruptNone { var reason string switch interrupted { case authboss.InterruptAccountLocked: reason = "Your account has been locked." case authboss.InterruptAccountNotConfirmed: reason = "Your account has not been confirmed." } response.Redirect(ctx, w, r, a.AuthLoginFailPath, "", reason, false) return nil } ctx.SessionStorer.Put(authboss.SessionKey, key) ctx.SessionStorer.Del(authboss.SessionHalfAuthKey) if err := a.Callbacks.FireAfter(authboss.EventAuth, ctx); err != nil { return err } response.Redirect(ctx, w, r, a.AuthLoginOKPath, "", "", true) default: w.WriteHeader(http.StatusMethodNotAllowed) } return nil }
func (reg *Register) registerPostHandler(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error { key := r.FormValue(reg.PrimaryID) password := r.FormValue(authboss.StorePassword) validationErrs := authboss.Validate(r, reg.Policies, reg.ConfirmFields...) if user, err := ctx.Storer.Get(key); err != nil && err != authboss.ErrUserNotFound { return err } else if user != nil { validationErrs = append(validationErrs, authboss.FieldError{reg.PrimaryID, errors.New("Already in use")}) } if len(validationErrs) != 0 { data := authboss.HTMLData{ "primaryID": reg.PrimaryID, "primaryIDValue": key, "errs": validationErrs.Map(), } for _, f := range reg.PreserveFields { data[f] = r.FormValue(f) } return reg.templates.Render(ctx, w, r, tplRegister, data) } attr, err := authboss.AttributesFromRequest(r) // Attributes from overriden forms if err != nil { return err } pass, err := bcrypt.GenerateFromPassword([]byte(password), reg.BCryptCost) if err != nil { return err } attr[reg.PrimaryID] = key attr[authboss.StorePassword] = string(pass) ctx.User = attr if err := reg.Storer.(RegisterStorer).Create(key, attr); err == authboss.ErrUserFound { data := authboss.HTMLData{ "primaryID": reg.PrimaryID, "primaryIDValue": key, "errs": map[string][]string{reg.PrimaryID: []string{"Already in use"}}, } for _, f := range reg.PreserveFields { data[f] = r.FormValue(f) } return reg.templates.Render(ctx, w, r, tplRegister, data) } else if err != nil { return err } if err := reg.Callbacks.FireAfter(authboss.EventRegister, ctx); err != nil { return err } if reg.IsLoaded("confirm") { response.Redirect(ctx, w, r, reg.RegisterOKPath, "Account successfully created, please verify your e-mail address.", "", true) return nil } ctx.SessionStorer.Put(authboss.SessionKey, key) response.Redirect(ctx, w, r, reg.RegisterOKPath, "Account successfully created, you are now logged in.", "", true) return nil }
func (o *OAuth2) oauthCallback(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error { provider := strings.ToLower(filepath.Base(r.URL.Path)) sessState, err := ctx.SessionStorer.GetErr(authboss.SessionOAuth2State) ctx.SessionStorer.Del(authboss.SessionOAuth2State) if err != nil { return err } sessValues, ok := ctx.SessionStorer.Get(authboss.SessionOAuth2Params) // Don't delete this value from session immediately, callbacks use this too var values map[string]string if ok { if err := json.Unmarshal([]byte(sessValues), &values); err != nil { return err } } hasErr := r.FormValue("error") if len(hasErr) > 0 { if err := o.Callbacks.FireAfter(authboss.EventOAuthFail, ctx); err != nil { return err } return authboss.ErrAndRedirect{ Err: errors.New(r.FormValue("error_reason")), Location: o.AuthLoginFailPath, FlashError: fmt.Sprintf("%s login cancelled or failed.", strings.Title(provider)), } } cfg, ok := o.OAuth2Providers[provider] if !ok { return fmt.Errorf("OAuth2 provider %q not found", provider) } // Ensure request is genuine state := r.FormValue(authboss.FormValueOAuth2State) splState := strings.Split(state, ";") if len(splState) == 0 || splState[0] != sessState { return errOAuthStateValidation } // Get the code code := r.FormValue("code") token, err := exchanger(cfg.OAuth2Config, o.Config.ContextProvider(r), code) if err != nil { return fmt.Errorf("Could not validate oauth2 code: %v", err) } user, err := cfg.Callback(o.Config.ContextProvider(r), *cfg.OAuth2Config, token) if err != nil { return err } // OAuth2UID is required. uid, err := user.StringErr(authboss.StoreOAuth2UID) if err != nil { return err } user[authboss.StoreOAuth2UID] = uid user[authboss.StoreOAuth2Provider] = provider user[authboss.StoreOAuth2Expiry] = token.Expiry user[authboss.StoreOAuth2Token] = token.AccessToken if len(token.RefreshToken) != 0 { user[authboss.StoreOAuth2Refresh] = token.RefreshToken } if err = ctx.OAuth2Storer.PutOAuth(uid, provider, user); err != nil { return err } // Fully log user in ctx.SessionStorer.Put(authboss.SessionKey, fmt.Sprintf("%s;%s", uid, provider)) ctx.SessionStorer.Del(authboss.SessionHalfAuthKey) if err = o.Callbacks.FireAfter(authboss.EventOAuth, ctx); err != nil { return nil } ctx.SessionStorer.Del(authboss.SessionOAuth2Params) redirect := o.AuthLoginOKPath query := make(url.Values) for k, v := range values { switch k { case authboss.CookieRemember: case authboss.FormValueRedirect: redirect = v default: query.Set(k, v) } } if len(query) > 0 { redirect = fmt.Sprintf("%s?%s", redirect, query.Encode()) } sf := fmt.Sprintf("Logged in successfully with %s.", strings.Title(provider)) response.Redirect(ctx, w, r, redirect, sf, "", false) return nil }
func (r *Recover) completeHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, req *http.Request) (err error) { switch req.Method { case methodGET: _, err = verifyToken(ctx, req) if err == errRecoveryTokenExpired { return authboss.ErrAndRedirect{err, "/recover", "", recoverTokenExpiredFlash} } else if err != nil { return authboss.ErrAndRedirect{err, "/", "", ""} } token := req.FormValue(formValueToken) data := authboss.NewHTMLData(formValueToken, token) return r.templates.Render(ctx, w, req, tplRecoverComplete, data) case methodPOST: token := req.FormValue(formValueToken) if len(token) == 0 { return authboss.ClientDataErr{formValueToken} } password := req.FormValue(authboss.StorePassword) //confirmPassword, _ := ctx.FirstPostFormValue("confirmPassword") policies := authboss.FilterValidators(r.Policies, authboss.StorePassword) if validationErrs := authboss.Validate(req, policies, authboss.StorePassword, authboss.ConfirmPrefix+authboss.StorePassword).Map(); len(validationErrs) > 0 { data := authboss.NewHTMLData( formValueToken, token, "errs", validationErrs, ) return r.templates.Render(ctx, w, req, tplRecoverComplete, data) } if ctx.User, err = verifyToken(ctx, req); err != nil { return err } encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), r.BCryptCost) if err != nil { return err } ctx.User[authboss.StorePassword] = string(encryptedPassword) ctx.User[StoreRecoverToken] = "" var nullTime time.Time ctx.User[StoreRecoverTokenExpiry] = nullTime primaryID, err := ctx.User.StringErr(r.PrimaryID) if err != nil { return err } if err := ctx.SaveUser(); err != nil { return err } if err := r.Callbacks.FireAfter(authboss.EventPasswordReset, ctx); err != nil { return err } ctx.SessionStorer.Put(authboss.SessionKey, primaryID) response.Redirect(ctx, w, req, r.AuthLoginOKPath, "", "", true) default: w.WriteHeader(http.StatusMethodNotAllowed) } return nil }