feat: add im auth verification flows

This commit is contained in:
richjohnrambo
2026-08-12 00:20:27 +08:00
parent 1ffcb85bee
commit d239e37f56
16 changed files with 2030 additions and 22 deletions
+96
View File
@@ -1449,6 +1449,100 @@ type pcacheMapper struct{}
var PCache PersistentCacheInterface
// VerificationPersistenceInterface defines storage for product verification challenges.
type VerificationPersistenceInterface interface {
Create(tenantID types.TenantID, vc *types.VerificationCode) error
Get(tenantID types.TenantID, challengeID string) (*types.VerificationCode, error)
GetActive(tenantID types.TenantID, scene, phoneHash string, now time.Time) (*types.VerificationCode, error)
Fail(tenantID types.TenantID, challengeID string, state types.VerificationState) error
MarkVerified(tenantID types.TenantID, challengeID, tokenHash string, verifiedAt time.Time) error
Consume(tenantID types.TenantID, challengeID, tokenHash string, consumedAt time.Time) error
}
type verificationMapper struct{}
// Verifications is a singleton for product verification challenges.
var Verifications VerificationPersistenceInterface
// UserSecurityPersistenceInterface defines user security state used to
// invalidate previously issued login tokens.
type UserSecurityPersistenceInterface interface {
GetAuthVersion(tenantID types.TenantID, uid types.Uid) (int, error)
BumpAuthVersion(tenantID types.TenantID, uid types.Uid, updatedAt time.Time) (int, error)
}
type userSecurityMapper struct{}
// Security is a singleton for user security metadata.
var Security UserSecurityPersistenceInterface
func (userSecurityMapper) GetAuthVersion(tenantID types.TenantID, uid types.Uid) (int, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return 0, err
}
return tenantAdp.TenantUserSecurityGetAuthVersion(tenantID, uid)
}
func (userSecurityMapper) BumpAuthVersion(tenantID types.TenantID, uid types.Uid, updatedAt time.Time) (int, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return 0, err
}
return tenantAdp.TenantUserSecurityBumpAuthVersion(tenantID, uid, updatedAt)
}
func (verificationMapper) Create(tenantID types.TenantID, vc *types.VerificationCode) error {
if vc == nil || vc.TenantID != tenantID {
return types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantVerificationCreate(tenantID, vc)
}
func (verificationMapper) Get(tenantID types.TenantID, challengeID string) (*types.VerificationCode, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantVerificationGet(tenantID, challengeID)
}
func (verificationMapper) GetActive(tenantID types.TenantID, scene, phoneHash string, now time.Time) (*types.VerificationCode, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantVerificationGetActive(tenantID, scene, phoneHash, now)
}
func (verificationMapper) Fail(tenantID types.TenantID, challengeID string, state types.VerificationState) error {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantVerificationFail(tenantID, challengeID, state)
}
func (verificationMapper) MarkVerified(tenantID types.TenantID, challengeID, tokenHash string, verifiedAt time.Time) error {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantVerificationMarkVerified(tenantID, challengeID, tokenHash, verifiedAt)
}
func (verificationMapper) Consume(tenantID types.TenantID, challengeID, tokenHash string, consumedAt time.Time) error {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantVerificationConsume(tenantID, challengeID, tokenHash, consumedAt)
}
// Get reads a persistent cache entry.
func (pcacheMapper) Get(tenantID types.TenantID, key string) (string, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
@@ -1499,4 +1593,6 @@ func init() {
Devices = deviceMapper{}
Files = fileMapper{}
PCache = pcacheMapper{}
Verifications = verificationMapper{}
Security = userSecurityMapper{}
}
+36
View File
@@ -0,0 +1,36 @@
package types
import "time"
// VerificationState is the lifecycle state of an SMS verification challenge.
type VerificationState int16
const (
VerificationStatePending VerificationState = iota
VerificationStateVerified
VerificationStateConsumed
VerificationStateExpired
VerificationStateLocked
)
// VerificationCode is an out-of-band verification challenge.
type VerificationCode struct {
ID int64 `db:"id" json:"-"`
TenantID TenantID `db:"tenant_id" json:"-"`
ChallengeID string `db:"challenge_id" json:"challenge_id"`
Scene string `db:"scene" json:"scene"`
PhoneHash string `db:"phone_hash" json:"-"`
PhoneMask string `db:"phone_mask" json:"phone_mask,omitempty"`
TargetUser Uid `db:"target_user" json:"-"`
CodeHash string `db:"code_hash" json:"-"`
VerificationTokenHash *string `db:"verification_token_hash" json:"-"`
State VerificationState `db:"state" json:"-"`
Attempts int `db:"attempts" json:"-"`
MaxAttempts int `db:"max_attempts" json:"-"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
ResendAfter time.Time `db:"resend_after" json:"resend_after"`
VerifiedAt *time.Time `db:"verified_at" json:"-"`
ConsumedAt *time.Time `db:"consumed_at" json:"-"`
CreatedAt time.Time `db:"created_at" json:"-"`
UpdatedAt time.Time `db:"updated_at" json:"-"`
}