github.com/wostzone/hub/auth@v0.0.0-20220118060317-7bb375743b17/pkg/authenticate/Authenticator.go (about) 1 package authenticate 2 3 import ( 4 "fmt" 5 6 "github.com/alexedwards/argon2id" 7 "github.com/sirupsen/logrus" 8 "golang.org/x/crypto/bcrypt" 9 ) 10 11 // supported password hashes 12 const ( 13 PWHASH_ARGON2id = "argon2id" 14 PWHASH_BCRYPT = "bcrypt" // fallback in case argon2i cannot be used 15 ) 16 17 // VerifyUsernamePassword is an interface to verify username/password authentication 18 type VerifyUsernamePassword func(userID string, password string) bool 19 20 // Authenticator manages client username/password authentication for access to Things 21 type Authenticator struct { 22 unpwStore IUnpwStore 23 } 24 25 // CreatePasswordHash for the given password 26 // This creates the hash and does not update the store. See also VerifyPasswordHash 27 // The only two hashes allowed are argon2id and bcrypt, although argon2id is recommended 28 // password to hash 29 // algo is the algorithm to use, PWHASH_ARGON2id (default) or PWHASH_BCRYPT 30 // iterations for argon2id, default is 10 31 func CreatePasswordHash(password string, algo string, iterations uint) (hash string, err error) { 32 if password == "" { 33 return "", fmt.Errorf("CreatePasswordHash: Missing password") 34 } 35 if algo == "" { 36 algo = PWHASH_ARGON2id 37 } 38 if algo == PWHASH_ARGON2id { 39 if iterations <= 0 { 40 iterations = 10 41 } 42 params := argon2id.DefaultParams 43 params.Iterations = uint32(iterations) 44 hash, err = argon2id.CreateHash(password, params) 45 } else if algo == PWHASH_BCRYPT { 46 var hashBytes []byte 47 hashBytes, err = bcrypt.GenerateFromPassword([]byte(password), 0) 48 hash = string(hashBytes) 49 } else { 50 err = fmt.Errorf("CreatePasswordHash: Unsupported hashing algorithm '%s'", algo) 51 } 52 return hash, err 53 } 54 55 // SetPassword hashes the given password and stores it in the password store 56 // Returns if username or password are not provided 57 func (ah *Authenticator) SetPassword(username string, password string) error { 58 if username == "" || password == "" { 59 return fmt.Errorf("SetPassword: Missing username or password") 60 } 61 // use default hashing algo 62 hash, err := CreatePasswordHash(password, "", 0) 63 if err != nil { 64 return err 65 } 66 if ah.unpwStore != nil { 67 err = ah.unpwStore.SetPasswordHash(username, hash) 68 } 69 return err 70 } 71 72 // Start the authhandler. This opens the password store. 73 // if no password store was provided this simply returns nil 74 func (ah *Authenticator) Start() error { 75 if ah.unpwStore == nil { 76 return fmt.Errorf("Authenticator.Start: missing password store") 77 } 78 err := ah.unpwStore.Open() 79 if err != nil { 80 err2 := fmt.Errorf("Authenticator.Start Failed opening password store: %s", err) 81 logrus.Errorf("%s", err2) 82 return err2 83 } 84 logrus.Infof("Authenticator.Start Success") 85 return nil 86 } 87 88 // Stop the auth handler and close the password store. 89 func (ah *Authenticator) Stop() { 90 if ah.unpwStore != nil { 91 ah.unpwStore.Close() 92 } 93 } 94 95 // VerifyUsernamePassword verifies if the given password is valid for login 96 // Returns true if valid, false if the user is unknown or the password is invalid 97 func (ah *Authenticator) VerifyUsernamePassword(loginName string, password string) bool { 98 if ah.unpwStore == nil { 99 return false 100 } 101 102 // Todo: configure hashing method 103 algo := PWHASH_ARGON2id 104 h := ah.unpwStore.GetPasswordHash(loginName) 105 match := ah.VerifyPasswordHash(h, password, algo) 106 logrus.Infof("VerifyUsernamePassword: loginName=%s, match=%v", loginName, match) 107 return match 108 } 109 110 // VerifyPasswordHash verifies if the given hash matches the password 111 // This does not access the store 112 // hash to verify 113 // password to verify against 114 // algo is the algorithm to use, PWHASH_ARGON2id or PWHASH_BCRYPT 115 // returns true if the password matches the hash, or false on mismatch 116 func (ah *Authenticator) VerifyPasswordHash(hash string, password string, algo string) bool { 117 if algo == PWHASH_ARGON2id { 118 match, _ := argon2id.ComparePasswordAndHash(password, hash) 119 return match 120 } else if algo == PWHASH_BCRYPT { 121 err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 122 return (err == nil) 123 } 124 return false 125 } 126 127 // NewAuthenticator creates a new instance of the authentication handler to update and verify user passwords. 128 // unpwStore provides the functions to access the password store. 129 func NewAuthenticator(unpwStore IUnpwStore) *Authenticator { 130 a := Authenticator{ 131 unpwStore: unpwStore, 132 } 133 return &a 134 }