1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-07-07 00:55:37 +02:00

password hashing via hasher (defaults: bcrypt)

This commit is contained in:
Evghenii Maslennikov
2022-04-18 23:42:02 +03:00
committed by Stephen Afam-Osemene
parent ccfe4d1c31
commit 42f90e2471
13 changed files with 83 additions and 8 deletions

View File

@ -65,12 +65,12 @@ func (a *Authboss) Init(modulesToLoad ...string) error {
// in sessions for a user requires special mechanisms not currently provided // in sessions for a user requires special mechanisms not currently provided
// by authboss. // by authboss.
func (a *Authboss) UpdatePassword(ctx context.Context, user AuthableUser, newPassword string) error { func (a *Authboss) UpdatePassword(ctx context.Context, user AuthableUser, newPassword string) error {
pass, err := bcrypt.GenerateFromPassword([]byte(newPassword), a.Config.Modules.BCryptCost) pass, err := a.Config.Core.Hasher.GenerateHash(newPassword)
if err != nil { if err != nil {
return err return err
} }
user.PutPassword(string(pass)) user.PutPassword(pass)
storer := a.Config.Storage.Server storer := a.Config.Storage.Server
if err := storer.Save(ctx, user); err != nil { if err := storer.Save(ctx, user); err != nil {

View File

@ -25,6 +25,7 @@ func TestAuthbossUpdatePassword(t *testing.T) {
ab := New() ab := New()
ab.Config.Storage.Server = storer ab.Config.Storage.Server = storer
ab.Config.Core.Hasher = mockHasher{}
if err := ab.UpdatePassword(context.Background(), user, "hello world"); err != nil { if err := ab.UpdatePassword(context.Background(), user, "hello world"); err != nil {
t.Error(err) t.Error(err)

View File

@ -239,6 +239,9 @@ type Config struct {
// Mailer is the mailer being used to send e-mails out via smtp // Mailer is the mailer being used to send e-mails out via smtp
Mailer Mailer Mailer Mailer
// Hasher hashes passwords into hashes
Hasher Hasher
// Logger implies just a few log levels for use, can optionally // Logger implies just a few log levels for use, can optionally
// also implement the ContextLogger to be able to upgrade to a // also implement the ContextLogger to be able to upgrade to a
// request specific logger. // request specific logger.

View File

@ -6,6 +6,7 @@ import (
"crypto/sha512" "crypto/sha512"
"encoding/base64" "encoding/base64"
"errors" "errors"
"github.com/volatiletech/authboss/v3/defaults"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -71,6 +72,7 @@ func testSetup() *testHarness {
harness.ab.Config.Core.BodyReader = harness.bodyReader harness.ab.Config.Core.BodyReader = harness.bodyReader
harness.ab.Config.Core.Logger = mocks.Logger{} harness.ab.Config.Core.Logger = mocks.Logger{}
harness.ab.Config.Core.Hasher = defaults.NewBCryptHasher(harness.ab.Modules.BCryptCost)
harness.ab.Config.Core.Mailer = harness.mailer harness.ab.Config.Core.Mailer = harness.mailer
harness.ab.Config.Core.Redirector = harness.redirector harness.ab.Config.Core.Redirector = harness.redirector
harness.ab.Config.Core.MailRenderer = harness.renderer harness.ab.Config.Core.MailRenderer = harness.renderer

View File

@ -25,5 +25,6 @@ func SetCore(config *authboss.Config, readJSON, useUsername bool) {
config.Core.Redirector = NewRedirector(config.Core.ViewRenderer, authboss.FormValueRedirect) config.Core.Redirector = NewRedirector(config.Core.ViewRenderer, authboss.FormValueRedirect)
config.Core.BodyReader = NewHTTPBodyReader(readJSON, useUsername) config.Core.BodyReader = NewHTTPBodyReader(readJSON, useUsername)
config.Core.Mailer = NewLogMailer(os.Stdout) config.Core.Mailer = NewLogMailer(os.Stdout)
config.Core.Hasher = NewBCryptHasher(config.Modules.BCryptCost)
config.Core.Logger = logger config.Core.Logger = logger
} }

20
defaults/hasher.go Normal file
View File

@ -0,0 +1,20 @@
package defaults
import "golang.org/x/crypto/bcrypt"
type BCryptHasher struct {
cost int
}
func NewBCryptHasher(cost int) *BCryptHasher {
return &BCryptHasher{cost: cost}
}
func (h *BCryptHasher) GenerateHash(raw string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(raw), h.cost)
if err != nil {
return "", err
}
return string(hash), nil
}

29
defaults/hasher_test.go Normal file
View File

@ -0,0 +1,29 @@
package defaults
import (
"golang.org/x/crypto/bcrypt"
"strings"
"testing"
)
func TestHasher(t *testing.T) {
t.Parallel()
hasher := NewBCryptHasher(bcrypt.DefaultCost)
hash, err := hasher.GenerateHash("qwerty")
if err != nil {
t.Error(err)
}
if hash == "" {
t.Error("Result Hash must be not empty")
}
if len(hash) != 60 {
t.Error("hash was invalid length", len(hash))
}
if !strings.HasPrefix(hash, "$2a$10$") {
t.Error("hash was wrong", hash)
}
}

5
hasher.go Normal file
View File

@ -0,0 +1,5 @@
package authboss
type Hasher interface {
GenerateHash(s string) (string, error)
}

View File

@ -3,6 +3,7 @@ package authboss
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"golang.org/x/crypto/bcrypt"
"net/http" "net/http"
"time" "time"
) )
@ -213,3 +214,14 @@ type mockLogger struct{}
func (m mockLogger) Info(s string) {} func (m mockLogger) Info(s string) {}
func (m mockLogger) Error(s string) {} func (m mockLogger) Error(s string) {}
type mockHasher struct{}
func (m mockHasher) GenerateHash(s string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(s), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hash), nil
}

View File

@ -16,7 +16,6 @@ import (
"time" "time"
"github.com/volatiletech/authboss/v3" "github.com/volatiletech/authboss/v3"
"golang.org/x/crypto/bcrypt"
) )
// Constants for templates etc. // Constants for templates etc.
@ -271,12 +270,12 @@ func (r *Recover) EndPost(w http.ResponseWriter, req *http.Request) error {
return nil return nil
} }
pass, err := bcrypt.GenerateFromPassword([]byte(password), r.Authboss.Config.Modules.BCryptCost) pass, err := r.Authboss.Config.Core.Hasher.GenerateHash(password)
if err != nil { if err != nil {
return err return err
} }
user.PutPassword(string(pass)) user.PutPassword(pass)
user.PutRecoverSelector("") // Don't allow another recovery user.PutRecoverSelector("") // Don't allow another recovery
user.PutRecoverVerifier("") // Don't allow another recovery user.PutRecoverVerifier("") // Don't allow another recovery
user.PutRecoverExpiry(time.Now().UTC()) // Put current time for those DBs that can't handle 0 time user.PutRecoverExpiry(time.Now().UTC()) // Put current time for those DBs that can't handle 0 time

View File

@ -5,6 +5,7 @@ import (
"crypto/sha512" "crypto/sha512"
"encoding/base64" "encoding/base64"
"errors" "errors"
"github.com/volatiletech/authboss/v3/defaults"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings" "strings"
@ -85,6 +86,7 @@ func testSetup() *testHarness {
harness.ab.Config.Core.BodyReader = harness.bodyReader harness.ab.Config.Core.BodyReader = harness.bodyReader
harness.ab.Config.Core.Logger = mocks.Logger{} harness.ab.Config.Core.Logger = mocks.Logger{}
harness.ab.Config.Core.Hasher = defaults.NewBCryptHasher(harness.ab.Config.Modules.BCryptCost)
harness.ab.Config.Core.Mailer = harness.mailer harness.ab.Config.Core.Mailer = harness.mailer
harness.ab.Config.Core.Redirector = harness.redirector harness.ab.Config.Core.Redirector = harness.redirector
harness.ab.Config.Core.MailRenderer = harness.renderer harness.ab.Config.Core.MailRenderer = harness.renderer

View File

@ -9,7 +9,6 @@ import (
"github.com/friendsofgo/errors" "github.com/friendsofgo/errors"
"github.com/volatiletech/authboss/v3" "github.com/volatiletech/authboss/v3"
"golang.org/x/crypto/bcrypt"
) )
// Pages // Pages
@ -92,13 +91,13 @@ func (r *Register) Post(w http.ResponseWriter, req *http.Request) error {
storer := authboss.EnsureCanCreate(r.Config.Storage.Server) storer := authboss.EnsureCanCreate(r.Config.Storage.Server)
user := authboss.MustBeAuthable(storer.New(req.Context())) user := authboss.MustBeAuthable(storer.New(req.Context()))
pass, err := bcrypt.GenerateFromPassword([]byte(password), r.Config.Modules.BCryptCost) pass, err := r.Authboss.Config.Core.Hasher.GenerateHash(password)
if err != nil { if err != nil {
return err return err
} }
user.PutPID(pid) user.PutPID(pid)
user.PutPassword(string(pass)) user.PutPassword(pass)
if arbUser, ok := user.(authboss.ArbitraryUser); ok && arbitrary != nil { if arbUser, ok := user.(authboss.ArbitraryUser); ok && arbitrary != nil {
arbUser.PutArbitrary(arbitrary) arbUser.PutArbitrary(arbitrary)

View File

@ -9,6 +9,7 @@ import (
"github.com/friendsofgo/errors" "github.com/friendsofgo/errors"
"github.com/volatiletech/authboss/v3" "github.com/volatiletech/authboss/v3"
"github.com/volatiletech/authboss/v3/defaults"
"github.com/volatiletech/authboss/v3/mocks" "github.com/volatiletech/authboss/v3/mocks"
) )
@ -88,6 +89,7 @@ func testSetup() *testHarness {
harness.ab.Config.Core.BodyReader = harness.bodyReader harness.ab.Config.Core.BodyReader = harness.bodyReader
harness.ab.Config.Core.Logger = mocks.Logger{} harness.ab.Config.Core.Logger = mocks.Logger{}
harness.ab.Config.Core.Hasher = defaults.NewBCryptHasher(harness.ab.Modules.BCryptCost)
harness.ab.Config.Core.Responder = harness.responder harness.ab.Config.Core.Responder = harness.responder
harness.ab.Config.Core.Redirector = harness.redirector harness.ab.Config.Core.Redirector = harness.redirector
harness.ab.Config.Storage.SessionState = harness.session harness.ab.Config.Storage.SessionState = harness.session