1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-04-13 11:50:27 +02:00
authboss/recover/complete.go

144 lines
4.3 KiB
Go

package recover
import (
"crypto/md5"
"encoding/base64"
"errors"
"fmt"
"net/http"
"time"
"golang.org/x/crypto/bcrypt"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/flashutil"
)
var errRecoveryTokenExpired = errors.New("recovery token expired")
type pageRecoverComplete struct {
Token, Password, ConfirmPassword string
ErrMap map[string][]string
FlashSuccess, FlashError string
XSRFName, XSRFToken string
}
func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
switch r.Method {
case methodGET:
_, err := verifyToken(ctx, authboss.Cfg.Storer.(authboss.RecoverStorer))
if err != nil {
if err.Error() == errRecoveryTokenExpired.Error() {
fmt.Fprintln(authboss.Cfg.LogWriter, "recover [token expired]:", err)
ctx.SessionStorer.Put(authboss.FlashErrorKey, authboss.Cfg.RecoverTokenExpiredFlash)
http.Redirect(w, r, "/recover", http.StatusFound)
return
} else {
fmt.Fprintln(authboss.Cfg.LogWriter, "recover:", err)
http.Redirect(w, r, "/", http.StatusFound)
return
}
}
token, _ := ctx.FirstFormValue("token")
page := pageRecoverComplete{
Token: token,
FlashError: flashutil.Pull(ctx.SessionStorer, authboss.FlashErrorKey),
XSRFName: authboss.Cfg.XSRFName,
XSRFToken: authboss.Cfg.XSRFMaker(w, r),
}
m.execTpl(tplRecoverComplete, w, page)
case methodPOST:
errPage := m.recoverComplete(ctx, authboss.Cfg.XSRFName, authboss.Cfg.XSRFMaker(w, r))
if errPage != nil {
m.execTpl(tplRecoverComplete, w, *errPage)
return
}
http.Redirect(w, r, authboss.Cfg.AuthLoginSuccessRoute, http.StatusFound)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
// verifyToken expects a base64.URLEncoded token.
func verifyToken(ctx *authboss.Context, storer authboss.RecoverStorer) (attrs authboss.Attributes, err error) {
token, ok := ctx.FirstFormValue("token")
if !ok {
return nil, errors.New("missing form value: token")
}
decoded, err := base64.URLEncoding.DecodeString(token)
if err != nil {
return nil, err
}
sum := md5.Sum(decoded)
userInter, err := storer.RecoverUser(base64.StdEncoding.EncodeToString(sum[:]))
if err != nil {
return nil, err
}
attrs = authboss.Unbind(userInter)
expiry, ok := attrs.DateTime(attrRecoverTokenExpiry)
if !ok || time.Now().After(expiry) {
return nil, errRecoveryTokenExpired
}
return attrs, nil
}
func (m *RecoverModule) recoverComplete(ctx *authboss.Context, xsrfName, xsrfToken string) (errPage *pageRecoverComplete) {
token, _ := ctx.FirstFormValue("token")
password, _ := ctx.FirstPostFormValue("password")
confirmPassword, _ := ctx.FirstPostFormValue("confirmPassword")
defaultErrPage := &pageRecoverComplete{
Token: token,
Password: password,
ConfirmPassword: confirmPassword,
FlashError: authboss.Cfg.RecoverFailedErrorFlash,
XSRFName: xsrfName,
XSRFToken: xsrfToken,
}
var err error
ctx.User, err = verifyToken(ctx, authboss.Cfg.Storer.(authboss.RecoverStorer))
if err != nil {
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to verify token", err)
return defaultErrPage
}
policies := authboss.FilterValidators(authboss.Cfg.Policies, "password")
if validationErrs := ctx.Validate(policies, authboss.Cfg.ConfirmFields...); len(validationErrs) > 0 {
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "validation failed", validationErrs)
return &pageRecoverComplete{
Token: token,
Password: password,
ConfirmPassword: confirmPassword,
ErrMap: validationErrs.Map(),
XSRFName: xsrfName,
XSRFToken: xsrfToken,
}
}
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), authboss.Cfg.BCryptCost)
if err != nil {
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to encrypt password", err)
return defaultErrPage
}
ctx.User[attrPassword] = string(encryptedPassword)
ctx.User[attrRecoverToken] = ""
var nullTime time.Time
ctx.User[attrRecoverTokenExpiry] = nullTime
username, _ := ctx.User.String(attrUsername)
if err := ctx.SaveUser(); err != nil {
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to save user", err)
return defaultErrPage
}
ctx.SessionStorer.Put(authboss.SessionKey, username)
return nil
}