Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
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
}
Ejemplo n.º 3
0
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
}
Ejemplo n.º 4
0
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
}
Ejemplo n.º 5
0
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
}
Ejemplo n.º 6
0
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
}
Ejemplo n.º 7
0
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
}