auth: complete account authorization flows
(cherry picked from commit 04f4527df32ad5c35720cccc41d27fe51549612f)
This commit is contained in:
parent
af41d18478
commit
6dc42942c8
21 changed files with 1780 additions and 50 deletions
|
|
@ -5,6 +5,7 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
|
|
@ -24,35 +25,97 @@ func NewPasswordStore(db sqlcgen.DBTX) *PasswordStore {
|
|||
}
|
||||
|
||||
func (s *PasswordStore) GetByUser(ctx context.Context, userID int64) (domain.PasswordSettings, bool, error) {
|
||||
row, err := s.q.GetPasswordByUser(ctx, userID)
|
||||
if err != nil {
|
||||
row := s.db.QueryRow(ctx, `
|
||||
SELECT
|
||||
has_recovery, has_secure_values, has_password, hint,
|
||||
email_unconfirmed_pattern, login_email_pattern, secure_random,
|
||||
current_algo_salt1, current_algo_salt2, current_algo_g, current_algo_p,
|
||||
srp_id, srp_verifier, srp_b_secret, srp_b,
|
||||
recovery_email, recovery_code, recovery_code_expires_at
|
||||
FROM account_passwords
|
||||
WHERE user_id = $1`, userID)
|
||||
var settings domain.PasswordSettings
|
||||
var salt1, salt2, p []byte
|
||||
var recoveryExpires sql.NullTime
|
||||
if err := row.Scan(
|
||||
&settings.HasRecovery, &settings.HasSecureValues, &settings.HasPassword, &settings.Hint,
|
||||
&settings.EmailUnconfirmedPattern, &settings.LoginEmailPattern, &settings.SecureRandom,
|
||||
&salt1, &salt2, &settings.NewAlgo.G, &p,
|
||||
&settings.SRPID, &settings.SRPVerifier, &settings.SRPBSecret, &settings.SRPB,
|
||||
&settings.RecoveryEmail, &settings.RecoveryCode, &recoveryExpires,
|
||||
); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return domain.PasswordSettings{}, false, nil
|
||||
}
|
||||
return domain.PasswordSettings{}, false, fmt.Errorf("get account password: %w", err)
|
||||
}
|
||||
return domain.PasswordSettings{
|
||||
HasRecovery: row.HasRecovery,
|
||||
HasSecureValues: row.HasSecureValues,
|
||||
HasPassword: row.HasPassword,
|
||||
Hint: row.Hint,
|
||||
EmailUnconfirmedPattern: row.EmailUnconfirmedPattern,
|
||||
LoginEmailPattern: row.LoginEmailPattern,
|
||||
SecureRandom: append([]byte(nil), row.SecureRandom...),
|
||||
}, true, nil
|
||||
if len(salt1) > 0 || len(salt2) > 0 || len(p) > 0 || settings.NewAlgo.G != 0 {
|
||||
settings.CurrentAlgo = &domain.PasswordKDFAlgo{
|
||||
Salt1: append([]byte(nil), salt1...),
|
||||
Salt2: append([]byte(nil), salt2...),
|
||||
G: settings.NewAlgo.G,
|
||||
P: append([]byte(nil), p...),
|
||||
}
|
||||
}
|
||||
settings.NewAlgo.Salt1 = append([]byte(nil), salt1...)
|
||||
settings.NewAlgo.Salt2 = append([]byte(nil), salt2...)
|
||||
settings.NewAlgo.P = append([]byte(nil), p...)
|
||||
if recoveryExpires.Valid {
|
||||
settings.RecoveryCodeExpiresAt = recoveryExpires.Time.Unix()
|
||||
}
|
||||
settings.SecureRandom = append([]byte(nil), settings.SecureRandom...)
|
||||
settings.SRPVerifier = append([]byte(nil), settings.SRPVerifier...)
|
||||
settings.SRPBSecret = append([]byte(nil), settings.SRPBSecret...)
|
||||
settings.SRPB = append([]byte(nil), settings.SRPB...)
|
||||
return settings, true, nil
|
||||
}
|
||||
|
||||
func (s *PasswordStore) Save(ctx context.Context, userID int64, settings domain.PasswordSettings) error {
|
||||
if err := s.q.UpsertPassword(ctx, sqlcgen.UpsertPasswordParams{
|
||||
UserID: userID,
|
||||
HasRecovery: settings.HasRecovery,
|
||||
HasSecureValues: settings.HasSecureValues,
|
||||
HasPassword: settings.HasPassword,
|
||||
Hint: settings.Hint,
|
||||
EmailUnconfirmedPattern: settings.EmailUnconfirmedPattern,
|
||||
LoginEmailPattern: settings.LoginEmailPattern,
|
||||
SecureRandom: settings.SecureRandom,
|
||||
}); err != nil {
|
||||
algo := settings.NewAlgo
|
||||
if settings.CurrentAlgo != nil {
|
||||
algo = *settings.CurrentAlgo
|
||||
}
|
||||
var recoveryExpires any
|
||||
if settings.RecoveryCodeExpiresAt > 0 {
|
||||
recoveryExpires = time.Unix(settings.RecoveryCodeExpiresAt, 0)
|
||||
}
|
||||
_, err := s.db.Exec(ctx, `
|
||||
INSERT INTO account_passwords (
|
||||
user_id, has_recovery, has_secure_values, has_password, hint,
|
||||
email_unconfirmed_pattern, login_email_pattern, secure_random,
|
||||
current_algo_salt1, current_algo_salt2, current_algo_g, current_algo_p,
|
||||
srp_id, srp_verifier, srp_b_secret, srp_b,
|
||||
recovery_email, recovery_code, recovery_code_expires_at
|
||||
)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
|
||||
ON CONFLICT (user_id) DO UPDATE SET
|
||||
has_recovery = EXCLUDED.has_recovery,
|
||||
has_secure_values = EXCLUDED.has_secure_values,
|
||||
has_password = EXCLUDED.has_password,
|
||||
hint = EXCLUDED.hint,
|
||||
email_unconfirmed_pattern = EXCLUDED.email_unconfirmed_pattern,
|
||||
login_email_pattern = EXCLUDED.login_email_pattern,
|
||||
secure_random = EXCLUDED.secure_random,
|
||||
current_algo_salt1 = EXCLUDED.current_algo_salt1,
|
||||
current_algo_salt2 = EXCLUDED.current_algo_salt2,
|
||||
current_algo_g = EXCLUDED.current_algo_g,
|
||||
current_algo_p = EXCLUDED.current_algo_p,
|
||||
srp_id = EXCLUDED.srp_id,
|
||||
srp_verifier = EXCLUDED.srp_verifier,
|
||||
srp_b_secret = EXCLUDED.srp_b_secret,
|
||||
srp_b = EXCLUDED.srp_b,
|
||||
recovery_email = EXCLUDED.recovery_email,
|
||||
recovery_code = EXCLUDED.recovery_code,
|
||||
recovery_code_expires_at = EXCLUDED.recovery_code_expires_at,
|
||||
updated_at = now()`,
|
||||
userID,
|
||||
settings.HasRecovery, settings.HasSecureValues, settings.HasPassword, settings.Hint,
|
||||
settings.EmailUnconfirmedPattern, settings.LoginEmailPattern, settings.SecureRandom,
|
||||
algo.Salt1, algo.Salt2, algo.G, algo.P,
|
||||
settings.SRPID, settings.SRPVerifier, settings.SRPBSecret, settings.SRPB,
|
||||
settings.RecoveryEmail, settings.RecoveryCode, recoveryExpires,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upsert account password: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue