using Dpz.Core.Entity.Base;

namespace Dpz.Core.Public.Entity;

/// <summary>
/// 用户扩展认证信息,独立于 User 集合保存,避免污染用户主文档
/// </summary>
public class UserExtendedInfo : IBaseEntity
{
    /// <summary>
    /// 扩展信息主键,使用账号保持与 User.Id 一致
    /// </summary>
    public required string Id { get; set; }

    /// <summary>
    /// 用户邮箱
    /// </summary>
    public string? Email { get; set; }

    /// <summary>
    /// 邮箱是否已验证
    /// </summary>
    public bool EmailVerified { get; set; } = false;

    /// <summary>
    /// WebAuthn 用户句柄,使用 Base64Url 编码保存
    /// </summary>
    public string? WebAuthnUserHandle { get; set; }

    /// <summary>
    /// WebAuthn / Passkey 凭证列表
    /// </summary>
    public List<WebAuthnCredential> WebAuthnCredentials { get; set; } = [];

    /// <summary>
    /// 创建时间
    /// </summary>
    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    public DateTime CreateTime { get; set; } = DateTime.Now;

    /// <summary>
    /// 最后更新时间
    /// </summary>
    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    public DateTime LastUpdateTime { get; set; } = DateTime.Now;
}

public class WebAuthnCredential
{
    /// <summary>
    /// 浏览器返回的凭证 ID,使用 Base64Url 编码保存
    /// </summary>
    public required string CredentialId { get; set; }

    /// <summary>
    /// 公钥,使用 Base64Url 编码保存
    /// </summary>
    public required string PublicKey { get; set; }

    /// <summary>
    /// 绑定该凭证时使用的用户句柄,Base64Url 编码
    /// </summary>
    public string? UserHandle { get; set; }

    /// <summary>
    /// 签名计数,用于重放与克隆设备检测
    /// </summary>
    public uint SignatureCounter { get; set; }

    /// <summary>
    /// 用户自定义设备名称
    /// </summary>
    public string? DisplayName { get; set; }

    /// <summary>
    /// 设备类型,例如 platform / cross-platform
    /// </summary>
    public string? AuthenticatorAttachment { get; set; }

    /// <summary>
    /// 凭证创建时间
    /// </summary>
    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    public DateTime CreateTime { get; set; } = DateTime.Now;

    /// <summary>
    /// 最近使用时间
    /// </summary>
    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    public DateTime? LastUsedTime { get; set; }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码说明

这是一个用于存储用户扩展认证信息的实体类定义,主要用于支持 WebAuthn/Passkey 无密码登录功能。代码使用 C# 编写,基于 MongoDB 存储(从 BsonDateTimeOptions 特性可以看出)。

主要类结构

1. UserExtendedInfo 类

用户扩展信息的主实体类,与用户主文档分离存储以避免污染核心用户数据。

核心字段:

  • Id:主键,与 User.Id 保持一致,建立关联关系
  • Email / EmailVerified:邮箱及验证状态
  • WebAuthnUserHandle:WebAuthn 协议要求的用户唯一标识(Base64Url 编码)
  • WebAuthnCredentials:该用户关联的所有 WebAuthn 凭证列表
  • CreateTime / LastUpdateTime:审计时间字段

设计亮点:

  • 实现了 IBaseEntity 接口,符合项目的实体基类规范
  • 使用 required 关键字(C# 11+)强制 Id 必须初始化
  • 时间字段使用 DateTimeKind.Local 确保本地时区一致性

2. WebAuthnCredential 类

存储单个 WebAuthn/Passkey 凭证的详细信息。

核心字段:

  • CredentialId:凭证唯一标识(浏览器生成)
  • PublicKey:用于验证签名的公钥
  • UserHandle:绑定时的用户句柄(可能与主记录不同)
  • SignatureCounter:防止重放攻击的计数器
  • DisplayName:用户友好的设备名称(如"我的 iPhone")
  • AuthenticatorAttachment:设备类型标识(平台内置 vs 外部设备)
  • LastUsedTime:最后使用时间,用于凭证管理和安全审计

使用场景

这个设计典型应用于现代无密码认证系统:

// 用户可以绑定多个 Passkey 设备
var userInfo = new UserExtendedInfo 
{
    Id = "user123",
    Email = "user@example.com",
    WebAuthnUserHandle = "YWJjZGVm...", // Base64Url 编码
    WebAuthnCredentials = 
    [
        new() { 
            CredentialId = "credential1",
            PublicKey = "public_key_data",
            DisplayName = "iPhone 13",
            AuthenticatorAttachment = "platform"
        }
    ]
};

安全考量

  1. 分离存储:敏感认证信息与用户基础信息分离
  2. 签名计数器:SignatureCounter 用于检测凭证克隆
  3. 审计追踪:记录创建和使用时间
  4. 多设备支持:支持用户绑定多个认证设备

这是一个设计良好的现代认证系统数据模型。

评论加载中...