feat: 2FAのバックアップコードの実装 (#121)
This commit is contained in:
@@ -455,6 +455,7 @@ export class UserEntityService implements OnModuleInit {
|
||||
preventAiLearning: profile!.preventAiLearning,
|
||||
isExplorable: user.isExplorable,
|
||||
isDeleted: user.isDeleted,
|
||||
twoFactorBackupCodes: profile?.twoFactorBackupSecret?.length === 20 ? 'full' : (profile?.twoFactorBackupSecret?.length ?? 0) > 0 ? 'partial' : 'none',
|
||||
hideOnlineStatus: user.hideOnlineStatus,
|
||||
hasUnreadSpecifiedNotes: this.noteUnreadsRepository.count({
|
||||
where: { userId: user.id, isSpecified: true },
|
||||
|
@@ -91,6 +91,11 @@ export class UserProfile {
|
||||
})
|
||||
public twoFactorTempSecret: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
nullable: true, array: true,
|
||||
})
|
||||
public twoFactorBackupSecret: string[] | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128, nullable: true,
|
||||
})
|
||||
|
@@ -320,6 +320,11 @@ export const packedMeDetailedOnlySchema = {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
},
|
||||
twoFactorBackupCodes: {
|
||||
type: 'string',
|
||||
enum: ['full', 'partial', 'none'],
|
||||
nullable: false, optional: false,
|
||||
},
|
||||
hideOnlineStatus: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
|
@@ -155,6 +155,13 @@ export class SigninApiService {
|
||||
});
|
||||
}
|
||||
|
||||
if (profile.twoFactorBackupSecret?.includes(token)) {
|
||||
await this.userProfilesRepository.update({ userId: profile.userId }, {
|
||||
twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token),
|
||||
});
|
||||
return this.signinService.signin(request, reply, user);
|
||||
}
|
||||
|
||||
const delta = OTPAuth.TOTP.validate({
|
||||
secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!),
|
||||
digits: 6,
|
||||
|
@@ -54,8 +54,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
throw new Error('not verified');
|
||||
}
|
||||
|
||||
const backupCodes = Array.from({ length: 20 }, () => {
|
||||
return new OTPAuth.Secret().base32;
|
||||
});
|
||||
|
||||
await this.userProfilesRepository.update(me.id, {
|
||||
twoFactorSecret: profile.twoFactorTempSecret,
|
||||
twoFactorBackupSecret: backupCodes,
|
||||
twoFactorEnabled: true,
|
||||
});
|
||||
|
||||
@@ -64,6 +69,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
detail: true,
|
||||
includeSecrets: true,
|
||||
}));
|
||||
|
||||
return {
|
||||
backupCodes: backupCodes,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
await this.userProfilesRepository.update(me.id, {
|
||||
twoFactorSecret: null,
|
||||
twoFactorBackupSecret: null,
|
||||
twoFactorEnabled: false,
|
||||
usePasswordLessLogin: false,
|
||||
});
|
||||
|
Reference in New Issue
Block a user