123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- package accounts
- import (
- "notabug.org/apiote/amuse/db"
- "encoding/base64"
- "errors"
- "fmt"
- "math/rand"
- "strings"
- "golang.org/x/crypto/argon2"
- "notabug.org/apiote/gott"
- )
- func findNoUser(args ...interface{}) (interface{}, error) {
- authData := args[0].(*AuthData)
- authResult := args[1].(*AuthResult)
- user, err := db.GetUser(authData.username)
- authResult.user = user
- if _, ok := err.(db.EmptyError); ok {
- err = nil
- } else if err == nil {
- err = AuthError{
- Err: errors.New("user_exists"),
- }
- }
- return gott.Tuple(args), err
- }
- func prepareSalt(args ...interface{}) (interface{}, error) {
- argon := args[2].(*Argon)
- salt := make([]byte, 16)
- _, err := rand.Read(salt)
- argon.salt = salt
- return gott.Tuple(args), err
- }
- func hashPassword(args ...interface{}) interface{} {
- authData := args[0].(*AuthData)
- argon := args[2].(*Argon)
- password := authData.password
- hash := argon2.IDKey([]byte(password), argon.salt, 1, 64*1024, 4, 32)
- b64Salt := base64.RawStdEncoding.EncodeToString(argon.salt)
- b64Hash := base64.RawStdEncoding.EncodeToString(hash)
- format := "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s"
- fullHash := fmt.Sprintf(format, argon2.Version, 64*1024, 1, 4, b64Salt, b64Hash)
- authData.password = fullHash
- return gott.Tuple(args)
- }
- func createRecoveryCodes(args ...interface{}) interface{} {
- authData := args[0].(*AuthData)
- sfaSecret := authData.sfa
- if sfaSecret != "" {
- result := args[1].(*AuthResult)
- codes := []string{}
- for i := 0; i < 12; i++ {
- code := rand.Int63n(999999999999)
- codeStr := fmt.Sprintf("%012d", code)
- codes = append(codes, codeStr)
- }
- result.recoveryCodesRaw = strings.Join(codes, ",")
- }
- return gott.Tuple(args)
- }
- func insertUser(args ...interface{}) (interface{}, error) {
- authData := args[0].(*AuthData)
- result := args[1].(*AuthResult)
- sfaSecret := authData.sfa
- err := db.InsertUser(authData.username, authData.password, sfaSecret, result.recoveryCodesRaw)
- return gott.Tuple(args), err
- }
- func Signup(username, password, sfaSecret string) (string, error) {
- r, err := gott.
- NewResult(gott.Tuple{&AuthData{username: username, password: password,
- sfa: sfaSecret}, &AuthResult{}, &Argon{}}).
- Bind(findNoUser).
- Bind(prepareSalt).
- Map(hashPassword).
- Map(createRecoveryCodes).
- Bind(insertUser).
- Finish()
- if err != nil {
- return "", err
- }
- return r.(gott.Tuple)[1].(*AuthResult).recoveryCodesRaw, err
- }
|