chore: 2FA

This commit is contained in:
xiaojunnuo
2025-04-17 01:15:55 +08:00
parent 412e8a32dd
commit d5d54d4d3b
12 changed files with 211 additions and 79 deletions
@@ -14,7 +14,6 @@ export class UserTwoFactorSetting extends BaseSettings {
authenticator: TwoFactorAuthenticator = {
enabled:false,
verified:false,
type: "totp"
};
}
@@ -1,7 +1,6 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { UserSettingsService } from "./user-settings-service.js";
import { UserTwoFactorSetting } from "./models.js";
import { utils } from "@certd/basic";
import { UserService } from "../../sys/authority/service/user-service.js";
/**
@@ -17,17 +16,19 @@ export class TwoFactorService {
async getAuthenticatorQrCode(userId: any) {
const setting = await this.userSettingsService.getSetting<UserTwoFactorSetting>(userId, UserTwoFactorSetting);
const setting = await this.getSetting(userId)
const authenticator = setting.authenticator;
if (!authenticator.secret) {
authenticator.secret = utils.id.simpleNanoId(16);
const authenticatorSetting = setting.authenticator;
if (!authenticatorSetting.secret) {
const { authenticator } = await import("otplib");
authenticatorSetting.secret = authenticator.generateSecret()
await this.userSettingsService.saveSetting(userId, setting);
}
const user = await this.userService.info(userId);
const username = user.username;
const secret = authenticator.secret;
const secret = authenticatorSetting.secret;
const qrcodeContent = `otpauth://totp/Certd:${username}?secret=${secret}&issuer=Certd`;
//生成qrcode base64
@@ -39,13 +40,13 @@ export class TwoFactorService {
async saveAuthenticator(req: { userId: any; verifyCode: any }) {
const userId = req.userId;
const { authenticator } = await import("otplib");
const tfSetting = await this.userSettingsService.getSetting<UserTwoFactorSetting>(userId, UserTwoFactorSetting);
const setting = await this.getSetting(userId)
const setting = tfSetting.authenticator;
if (!setting.secret) {
const authenticatorSetting = setting.authenticator;
if (!authenticatorSetting.secret) {
throw new Error("secret is required");
}
const secret = setting.secret;
const secret = authenticatorSetting.secret;
const token = req.verifyCode;
const isValid = authenticator.verify({ token, secret });
@@ -54,9 +55,38 @@ export class TwoFactorService {
}
//校验成功,保存开启状态
setting.enabled = true;
setting.verified = true;
authenticatorSetting.enabled = true;
authenticatorSetting.verified = true;
await this.userSettingsService.saveSetting(userId, setting);
}
async offAuthenticator(userId:number) {
if (!userId) {
throw new Error("userId is required");
}
const setting = await this.getSetting(userId)
setting.authenticator.enabled = false;
setting.authenticator.verified = false;
setting.authenticator.secret = '';
await this.userSettingsService.saveSetting(userId, setting);
}
async getSetting(userId:number) {
return await this.userSettingsService.getSetting<UserTwoFactorSetting>(userId, UserTwoFactorSetting);
}
async verifyAuthenticatorCode(userId: any, verifyCode: string) {
const { authenticator } = await import("otplib");
const setting = await this.getSetting(userId)
if (!setting.authenticator.enabled) {
throw new Error("authenticator 未开启");
}
if (!authenticator.verify({ token: verifyCode, secret: setting.authenticator.secret })) {
throw new Error("验证码错误");
}
return true;
}
}
@@ -95,6 +95,9 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
const type: any = bean.constructor;
const key = type.__key__;
if(!key){
throw new Error(`${type.name} must have __key__`);
}
const entity = await this.getByKey(key,userId);
const newEntity = new UserSettingsEntity();
if (entity) {
@@ -104,8 +107,8 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
newEntity.title = type.__title__;
newEntity.userId = userId;
}
entity.setting = JSON.stringify(bean);
await this.repository.save(entity);
newEntity.setting = JSON.stringify(bean);
await this.repository.save(newEntity);
}
}