Skip to content

Commit 42f90e2

Browse files
Evghenii Maslennikovstephenafamo
Evghenii Maslennikov
authored andcommitted
password hashing via hasher (defaults: bcrypt)
1 parent ccfe4d1 commit 42f90e2

13 files changed

+83
-8
lines changed

authboss.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ func (a *Authboss) Init(modulesToLoad ...string) error {
6565
// in sessions for a user requires special mechanisms not currently provided
6666
// by authboss.
6767
func (a *Authboss) UpdatePassword(ctx context.Context, user AuthableUser, newPassword string) error {
68-
pass, err := bcrypt.GenerateFromPassword([]byte(newPassword), a.Config.Modules.BCryptCost)
68+
pass, err := a.Config.Core.Hasher.GenerateHash(newPassword)
6969
if err != nil {
7070
return err
7171
}
7272

73-
user.PutPassword(string(pass))
73+
user.PutPassword(pass)
7474

7575
storer := a.Config.Storage.Server
7676
if err := storer.Save(ctx, user); err != nil {

authboss_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func TestAuthbossUpdatePassword(t *testing.T) {
2525

2626
ab := New()
2727
ab.Config.Storage.Server = storer
28+
ab.Config.Core.Hasher = mockHasher{}
2829

2930
if err := ab.UpdatePassword(context.Background(), user, "hello world"); err != nil {
3031
t.Error(err)

config.go

+3
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ type Config struct {
239239
// Mailer is the mailer being used to send e-mails out via smtp
240240
Mailer Mailer
241241

242+
// Hasher hashes passwords into hashes
243+
Hasher Hasher
244+
242245
// Logger implies just a few log levels for use, can optionally
243246
// also implement the ContextLogger to be able to upgrade to a
244247
// request specific logger.

confirm/confirm_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"crypto/sha512"
77
"encoding/base64"
88
"errors"
9+
"github.com/volatiletech/authboss/v3/defaults"
910
"net/http"
1011
"net/http/httptest"
1112
"testing"
@@ -71,6 +72,7 @@ func testSetup() *testHarness {
7172

7273
harness.ab.Config.Core.BodyReader = harness.bodyReader
7374
harness.ab.Config.Core.Logger = mocks.Logger{}
75+
harness.ab.Config.Core.Hasher = defaults.NewBCryptHasher(harness.ab.Modules.BCryptCost)
7476
harness.ab.Config.Core.Mailer = harness.mailer
7577
harness.ab.Config.Core.Redirector = harness.redirector
7678
harness.ab.Config.Core.MailRenderer = harness.renderer

defaults/defaults.go

+1
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ func SetCore(config *authboss.Config, readJSON, useUsername bool) {
2525
config.Core.Redirector = NewRedirector(config.Core.ViewRenderer, authboss.FormValueRedirect)
2626
config.Core.BodyReader = NewHTTPBodyReader(readJSON, useUsername)
2727
config.Core.Mailer = NewLogMailer(os.Stdout)
28+
config.Core.Hasher = NewBCryptHasher(config.Modules.BCryptCost)
2829
config.Core.Logger = logger
2930
}

defaults/hasher.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package defaults
2+
3+
import "golang.org/x/crypto/bcrypt"
4+
5+
type BCryptHasher struct {
6+
cost int
7+
}
8+
9+
func NewBCryptHasher(cost int) *BCryptHasher {
10+
return &BCryptHasher{cost: cost}
11+
}
12+
13+
func (h *BCryptHasher) GenerateHash(raw string) (string, error) {
14+
hash, err := bcrypt.GenerateFromPassword([]byte(raw), h.cost)
15+
if err != nil {
16+
return "", err
17+
}
18+
19+
return string(hash), nil
20+
}

defaults/hasher_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package defaults
2+
3+
import (
4+
"golang.org/x/crypto/bcrypt"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestHasher(t *testing.T) {
10+
t.Parallel()
11+
12+
hasher := NewBCryptHasher(bcrypt.DefaultCost)
13+
14+
hash, err := hasher.GenerateHash("qwerty")
15+
if err != nil {
16+
t.Error(err)
17+
}
18+
19+
if hash == "" {
20+
t.Error("Result Hash must be not empty")
21+
22+
}
23+
if len(hash) != 60 {
24+
t.Error("hash was invalid length", len(hash))
25+
}
26+
if !strings.HasPrefix(hash, "$2a$10$") {
27+
t.Error("hash was wrong", hash)
28+
}
29+
}

hasher.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package authboss
2+
3+
type Hasher interface {
4+
GenerateHash(s string) (string, error)
5+
}

mocks_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package authboss
33
import (
44
"context"
55
"encoding/json"
6+
"golang.org/x/crypto/bcrypt"
67
"net/http"
78
"time"
89
)
@@ -213,3 +214,14 @@ type mockLogger struct{}
213214

214215
func (m mockLogger) Info(s string) {}
215216
func (m mockLogger) Error(s string) {}
217+
218+
type mockHasher struct{}
219+
220+
func (m mockHasher) GenerateHash(s string) (string, error) {
221+
hash, err := bcrypt.GenerateFromPassword([]byte(s), bcrypt.DefaultCost)
222+
if err != nil {
223+
return "", err
224+
}
225+
226+
return string(hash), nil
227+
}

recover/recover.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"time"
1717

1818
"github.com/volatiletech/authboss/v3"
19-
"golang.org/x/crypto/bcrypt"
2019
)
2120

2221
// Constants for templates etc.
@@ -271,12 +270,12 @@ func (r *Recover) EndPost(w http.ResponseWriter, req *http.Request) error {
271270
return nil
272271
}
273272

274-
pass, err := bcrypt.GenerateFromPassword([]byte(password), r.Authboss.Config.Modules.BCryptCost)
273+
pass, err := r.Authboss.Config.Core.Hasher.GenerateHash(password)
275274
if err != nil {
276275
return err
277276
}
278277

279-
user.PutPassword(string(pass))
278+
user.PutPassword(pass)
280279
user.PutRecoverSelector("") // Don't allow another recovery
281280
user.PutRecoverVerifier("") // Don't allow another recovery
282281
user.PutRecoverExpiry(time.Now().UTC()) // Put current time for those DBs that can't handle 0 time

recover/recover_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/sha512"
66
"encoding/base64"
77
"errors"
8+
"github.com/volatiletech/authboss/v3/defaults"
89
"net/http"
910
"net/http/httptest"
1011
"strings"
@@ -85,6 +86,7 @@ func testSetup() *testHarness {
8586

8687
harness.ab.Config.Core.BodyReader = harness.bodyReader
8788
harness.ab.Config.Core.Logger = mocks.Logger{}
89+
harness.ab.Config.Core.Hasher = defaults.NewBCryptHasher(harness.ab.Config.Modules.BCryptCost)
8890
harness.ab.Config.Core.Mailer = harness.mailer
8991
harness.ab.Config.Core.Redirector = harness.redirector
9092
harness.ab.Config.Core.MailRenderer = harness.renderer

register/register.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/friendsofgo/errors"
1010

1111
"github.com/volatiletech/authboss/v3"
12-
"golang.org/x/crypto/bcrypt"
1312
)
1413

1514
// Pages
@@ -92,13 +91,13 @@ func (r *Register) Post(w http.ResponseWriter, req *http.Request) error {
9291
storer := authboss.EnsureCanCreate(r.Config.Storage.Server)
9392
user := authboss.MustBeAuthable(storer.New(req.Context()))
9493

95-
pass, err := bcrypt.GenerateFromPassword([]byte(password), r.Config.Modules.BCryptCost)
94+
pass, err := r.Authboss.Config.Core.Hasher.GenerateHash(password)
9695
if err != nil {
9796
return err
9897
}
9998

10099
user.PutPID(pid)
101-
user.PutPassword(string(pass))
100+
user.PutPassword(pass)
102101

103102
if arbUser, ok := user.(authboss.ArbitraryUser); ok && arbitrary != nil {
104103
arbUser.PutArbitrary(arbitrary)

register/register_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/friendsofgo/errors"
1111
"github.com/volatiletech/authboss/v3"
12+
"github.com/volatiletech/authboss/v3/defaults"
1213
"github.com/volatiletech/authboss/v3/mocks"
1314
)
1415

@@ -88,6 +89,7 @@ func testSetup() *testHarness {
8889

8990
harness.ab.Config.Core.BodyReader = harness.bodyReader
9091
harness.ab.Config.Core.Logger = mocks.Logger{}
92+
harness.ab.Config.Core.Hasher = defaults.NewBCryptHasher(harness.ab.Modules.BCryptCost)
9193
harness.ab.Config.Core.Responder = harness.responder
9294
harness.ab.Config.Core.Redirector = harness.redirector
9395
harness.ab.Config.Storage.SessionState = harness.session

0 commit comments

Comments
 (0)