make Mailer an interface

This commit is contained in:
dhax 2025-03-05 15:42:02 +01:00
parent dd2412463b
commit 08c09ffee7
10 changed files with 234 additions and 227 deletions

View file

@ -33,21 +33,16 @@ type AuthStorer interface {
PurgeExpiredToken() error
}
// Mailer defines methods to send account emails.
type Mailer interface {
LoginToken(name, email string, c email.ContentLoginToken) error
}
// Resource implements passwordless account authentication against a database.
type Resource struct {
LoginAuth *LoginTokenAuth
TokenAuth *jwt.TokenAuth
Store AuthStorer
Mailer Mailer
Mailer email.Mailer
}
// NewResource returns a configured authentication resource.
func NewResource(authStore AuthStorer, mailer Mailer) (*Resource, error) {
func NewResource(authStore AuthStorer, mailer email.Mailer) (*Resource, error) {
loginAuth, err := NewLoginTokenAuth()
if err != nil {
return nil, err
@ -126,14 +121,17 @@ func (rs *Resource) login(w http.ResponseWriter, r *http.Request) {
tokenURL, _ := url.JoinPath(rs.LoginAuth.loginURL, lt.Token)
go func() {
content := email.ContentLoginToken{
content := ContentLoginToken{
Email: acc.Email,
Name: acc.Name,
URL: tokenURL,
Token: lt.Token,
Expiry: lt.Expiry,
}
if err := rs.Mailer.LoginToken(acc.Name, acc.Email, content); err != nil {
msg := LoginTokenEmail(acc.Name, acc.Email, content)
if err := rs.Mailer.Send(msg); err != nil {
log(r).WithField("module", "email").Error(err)
}
}()

View file

@ -24,7 +24,7 @@ import (
var (
auth *Resource
authStore MockAuthStore
mailer email.MockMailer
mailer *email.MockMailer
ts *httptest.Server
)
@ -35,7 +35,9 @@ func TestMain(m *testing.M) {
viper.SetDefault("log_level", "error")
var err error
auth, err = NewResource(&authStore, &mailer)
mailer = email.NewMockMailer()
auth, err = NewResource(&authStore, mailer)
if err != nil {
fmt.Println(err)
os.Exit(1)
@ -72,10 +74,6 @@ func TestAuthResource_login(t *testing.T) {
return &a, err
}
mailer.LoginTokenFn = func(n, e string, c email.ContentLoginToken) error {
return nil
}
tests := []struct {
name string
email string
@ -105,11 +103,11 @@ func TestAuthResource_login(t *testing.T) {
if tc.err == ErrInvalidLogin && authStore.GetAccountByEmailInvoked {
t.Error("GetByLoginToken invoked for invalid email")
}
if tc.err == nil && !mailer.LoginTokenInvoked {
t.Error("emailService.LoginToken not invoked")
if tc.err == nil && !mailer.SendInvoked {
t.Error("emailService.Send not invoked")
}
authStore.GetAccountByEmailInvoked = false
mailer.LoginTokenInvoked = false
mailer.SendInvoked = false
})
}
}

28
auth/pwdless/emails.go Normal file
View file

@ -0,0 +1,28 @@
package pwdless
import (
"os"
"time"
"github.com/dhax/go-base/email"
)
// ContentLoginToken defines content for login token email template.
type ContentLoginToken struct {
Email string
Name string
URL string
Token string
Expiry time.Time
}
// LoginTokenEmail creates and sends a login token email with provided template content.
func LoginTokenEmail(name, address string, content ContentLoginToken) email.Message {
return email.Message{
From: email.NewEmail(os.Getenv("EMAIL_FROM_NAME"), os.Getenv("EMAIL_FROM_ADDRESS")),
To: email.NewEmail(name, address),
Subject: "Login Token",
Template: "loginToken",
Content: content,
}
}

View file

@ -9,9 +9,7 @@ import (
"github.com/spf13/viper"
)
var (
errTokenNotFound = errors.New("login token not found")
)
var errTokenNotFound = errors.New("login token not found")
// LoginToken is an in-memory saved token referencing an account ID and an expiry date.
type LoginToken struct {