package handlers import ( "encoding/json" "fmt" "time" "github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/session" ) func identityRetrieverFromStorage(ctx *middlewares.AutheliaCtx) (*session.Identity, error) { var requestBody resetPasswordStep1RequestBody err := json.Unmarshal(ctx.PostBody(), &requestBody) if err != nil { return nil, err } details, err := ctx.Providers.UserProvider.GetDetails(requestBody.Username) if err != nil { return nil, err } if len(details.Emails) == 0 { return nil, fmt.Errorf("user %s has no email address configured", requestBody.Username) } return &session.Identity{ Username: requestBody.Username, Email: details.Emails[0], }, nil } // ResetPasswordIdentityStart the handler for initiating the identity validation for resetting a password. // We need to ensure the attacker cannot perform user enumeration by always replying with 200 whatever what happens in backend. var ResetPasswordIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{ MailTitle: "Reset your password", MailButtonContent: "Reset", TargetEndpoint: "/reset-password/step2", ActionClaim: ActionResetPassword, IdentityRetrieverFunc: identityRetrieverFromStorage, }, middlewares.TimingAttackDelay(10, 250, 85, time.Millisecond*500)) func resetPasswordIdentityFinish(ctx *middlewares.AutheliaCtx, username string) { userSession := ctx.GetSession() // TODO(c.michaud): use JWT tokens to expire the request in only few seconds for better security. userSession.PasswordResetUsername = &username err := ctx.SaveSession(userSession) if err != nil { ctx.Logger.Errorf("Unable to clear password reset flag in session for user %s: %s", userSession.Username, err) } mode := "" if ctx.Configuration.PasswordPolicy.Standard.Enabled { mode = "standard" } else if ctx.Configuration.PasswordPolicy.Zxcvbn.Enabled { mode = "zxcvbn" } policyResponse := PassworPolicyBody{ Mode: mode, MinLength: ctx.Configuration.PasswordPolicy.Standard.MinLength, MaxLength: ctx.Configuration.PasswordPolicy.Standard.MaxLength, RequireLowercase: ctx.Configuration.PasswordPolicy.Standard.RequireLowercase, RequireUppercase: ctx.Configuration.PasswordPolicy.Standard.RequireUppercase, RequireNumber: ctx.Configuration.PasswordPolicy.Standard.RequireNumber, RequireSpecial: ctx.Configuration.PasswordPolicy.Standard.RequireSpecial, MinScore: ctx.Configuration.PasswordPolicy.Zxcvbn.MinScore, } err = ctx.SetJSONBody(policyResponse) if err != nil { ctx.Logger.Errorf("Unable to send password Policy: %s", err) } } // ResetPasswordIdentityFinish the handler for finishing the identity validation. var ResetPasswordIdentityFinish = middlewares.IdentityVerificationFinish( middlewares.IdentityVerificationFinishArgs{ActionClaim: ActionResetPassword}, resetPasswordIdentityFinish)