authkit initial
This commit is contained in:
parent
5173b0a43d
commit
134393fbca
43 changed files with 5188 additions and 1 deletions
69
service_reset.go
Normal file
69
service_reset.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package authkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"git.juancwu.dev/juancwu/errx"
|
||||
)
|
||||
|
||||
// RequestPasswordReset mints a single-use password-reset token for the user
|
||||
// behind email and returns the plaintext for the caller to deliver via email.
|
||||
// Returns ErrUserNotFound when the email isn't registered (per project
|
||||
// policy of distinct errors over anti-enumeration).
|
||||
func (a *Auth) RequestPasswordReset(ctx context.Context, email string) (string, error) {
|
||||
const op = "authkit.Auth.RequestPasswordReset"
|
||||
u, err := a.deps.Users.GetUserByEmail(ctx, normalizeEmail(email))
|
||||
if err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
plaintext, hash, err := mintSecret(prefixPasswordRset, a.cfg.Random)
|
||||
if err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
now := a.now()
|
||||
t := &Token{
|
||||
Hash: hash,
|
||||
Kind: TokenPasswordReset,
|
||||
UserID: u.ID,
|
||||
CreatedAt: now,
|
||||
ExpiresAt: now.Add(a.cfg.PasswordResetTTL),
|
||||
}
|
||||
if err := a.deps.Tokens.CreateToken(ctx, t); err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// ConfirmPasswordReset consumes the reset token, sets the new password,
|
||||
// bumps the user's session_version, and revokes outstanding sessions so the
|
||||
// reset constitutes a global logout.
|
||||
func (a *Auth) ConfirmPasswordReset(ctx context.Context, plaintextToken, newPassword string) error {
|
||||
const op = "authkit.Auth.ConfirmPasswordReset"
|
||||
hash, ok := parseSecret(prefixPasswordRset, plaintextToken)
|
||||
if !ok {
|
||||
return errx.Wrap(op, ErrTokenInvalid)
|
||||
}
|
||||
now := a.now()
|
||||
t, err := a.deps.Tokens.ConsumeToken(ctx, TokenPasswordReset, hash, now)
|
||||
if err != nil {
|
||||
return errx.Wrap(op, err)
|
||||
}
|
||||
newHash, err := a.deps.Hasher.Hash(newPassword)
|
||||
if err != nil {
|
||||
return errx.Wrap(op, err)
|
||||
}
|
||||
if err := a.deps.Users.SetPassword(ctx, t.UserID, newHash); err != nil {
|
||||
if errors.Is(err, ErrUserNotFound) {
|
||||
return errx.Wrap(op, ErrUserNotFound)
|
||||
}
|
||||
return errx.Wrap(op, err)
|
||||
}
|
||||
if _, err := a.deps.Users.BumpSessionVersion(ctx, t.UserID); err != nil {
|
||||
return errx.Wrap(op, err)
|
||||
}
|
||||
if err := a.deps.Sessions.DeleteUserSessions(ctx, t.UserID); err != nil {
|
||||
return errx.Wrap(op, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue