93 lines
3.4 KiB
Go
93 lines
3.4 KiB
Go
// Package domain holds the IAM bounded context's entities and the
|
|
// repository interfaces its application layer depends on. Nothing here
|
|
// imports database/sql, net/http or gRPC — see ARCHITECTURE.md's note
|
|
// on dependency direction ("domain depends on nothing; everything else
|
|
// depends on domain").
|
|
package domain
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// User is an enkly account — identical in shape to the monolith's
|
|
// models.User, minus the json tags (this is an internal entity now, not
|
|
// a value serialized straight to a template).
|
|
type User struct {
|
|
ID int64
|
|
Email string
|
|
PasswordHash string
|
|
EmailVerified bool
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// Session is one logged-in browser, identified at rest by the SHA-256
|
|
// hash of its cookie token (see pkg/authsupport.HashToken) — the raw
|
|
// token exists only in the cookie itself and the gRPC response that
|
|
// minted it.
|
|
type Session struct {
|
|
UserID int64
|
|
TokenHash string
|
|
ExpiresAt time.Time
|
|
}
|
|
|
|
// VerificationCode is a 6-digit email-verification code, stored only as
|
|
// its hash plus an attempt counter (see internal/db's original comment
|
|
// on why a short code needs active guessing defense that a 256-bit
|
|
// token wouldn't).
|
|
type VerificationCode struct {
|
|
UserID int64
|
|
CodeHash string
|
|
Attempts int
|
|
ExpiresAt time.Time
|
|
UsedAt *time.Time
|
|
}
|
|
|
|
// PasswordResetToken is a "forgot password" link's token, stored only
|
|
// as its hash.
|
|
type PasswordResetToken struct {
|
|
UserID int64
|
|
TokenHash string
|
|
ExpiresAt time.Time
|
|
UsedAt *time.Time
|
|
}
|
|
|
|
// UserRepository persists and retrieves User rows. Implemented by
|
|
// internal/infrastructure/postgres.UserRepo.
|
|
type UserRepository interface {
|
|
Create(ctx context.Context, email, passwordHash string) (*User, error)
|
|
GetByEmail(ctx context.Context, email string) (*User, error)
|
|
GetByID(ctx context.Context, id int64) (*User, error)
|
|
MarkEmailVerified(ctx context.Context, userID int64) error
|
|
UpdatePassword(ctx context.Context, userID int64, newPasswordHash string) error
|
|
}
|
|
|
|
// SessionRepository persists and retrieves Session rows.
|
|
type SessionRepository interface {
|
|
Create(ctx context.Context, userID int64, tokenHash string, expiresAt time.Time) error
|
|
GetUserByTokenHash(ctx context.Context, tokenHash string) (*User, error)
|
|
Delete(ctx context.Context, tokenHash string) error
|
|
DeleteAllForUser(ctx context.Context, userID int64) error
|
|
}
|
|
|
|
// VerificationRepository persists and retrieves VerificationCode rows.
|
|
type VerificationRepository interface {
|
|
Create(ctx context.Context, userID int64, codeHash string, expiresAt time.Time) error
|
|
// VerifyAndConsume replaces db.VerifyEmailCode: checks the most
|
|
// recent unused, unexpired code for userID against codeHash,
|
|
// incrementing its attempt counter on a mismatch and marking it
|
|
// used (and the user verified) on a match. Returns domain.ErrInvalid
|
|
// when no code matches.
|
|
VerifyAndConsume(ctx context.Context, userID int64, codeHash string) error
|
|
}
|
|
|
|
// PasswordResetRepository persists and retrieves PasswordResetToken rows.
|
|
type PasswordResetRepository interface {
|
|
Create(ctx context.Context, userID int64, tokenHash string, expiresAt time.Time) error
|
|
Valid(ctx context.Context, tokenHash string) (bool, error)
|
|
// ConsumeAndResetPassword replaces db.ResetPassword: validates the
|
|
// token, updates the password hash, marks the token used, and
|
|
// deletes every session for the user, all in one transaction.
|
|
ConsumeAndResetPassword(ctx context.Context, tokenHash, newPasswordHash string) (userID int64, err error)
|
|
}
|