diff --git a/apps/backend/src/auth/entity/passkey-credential.entity.ts b/apps/backend/src/auth/entity/passkey-credential.entity.ts new file mode 100644 index 0000000..9c1719c --- /dev/null +++ b/apps/backend/src/auth/entity/passkey-credential.entity.ts @@ -0,0 +1,36 @@ +import { User } from "src/user/entities/user.entity"; +import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm"; + +@Entity() +@Index(['user']) +export class PasskeyCredential { + @PrimaryGeneratedColumn('uuid') + id: string; + + // 关联用户 + @ManyToOne(() => User, user => user.passkeys, { onDelete: 'CASCADE' }) + user: User; + + // WebAuthn 必需字段 + @Column({ length: 255 }) + name: string; // 用户自定义名称,如 "iPhone", "工作笔记本" + + @Column({ unique: true }) + credentialId: string; // Base64URL 编码的 credentialId(唯一标识) + + @Column({ type: 'text' }) + publicKey: string; // Base64URL 编码的公钥(SPKI 格式) + + @Column({ type: 'int' }) + signCount: number; // 防重放攻击,每次签名递增 + + // 是否已验证(注册时验证,登录时更新) + @Column({ default: false }) + verified: boolean; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} \ No newline at end of file diff --git a/apps/backend/src/user/entities/user.entity.ts b/apps/backend/src/user/entities/user.entity.ts index 3a9df52..9b5d206 100644 --- a/apps/backend/src/user/entities/user.entity.ts +++ b/apps/backend/src/user/entities/user.entity.ts @@ -1,3 +1,4 @@ +import { PasskeyCredential } from 'src/auth/entity/passkey-credential.entity'; import { Role } from 'src/auth/role.enum'; import { BeforeInsert, @@ -6,6 +7,7 @@ import { DeleteDateColumn, Entity, Index, + OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -94,6 +96,9 @@ export class User { @Column({ type: 'jsonb', default: [] }) roles: RoleItem[]; + + @OneToMany(() => PasskeyCredential, credential => credential.user) + passkeys?: PasskeyCredential[]; } export class UserPublicProfile {