summaryrefslogtreecommitdiff
path: root/src/server/api/endpoints/i/2fa
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
commit0e4a111f81cceed275d9bec2695f6e401fb654d8 (patch)
tree40874799472fa07416f17b50a398ac33b7771905 /src/server/api/endpoints/i/2fa
parentupdate deps (diff)
downloadmisskey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.gz
misskey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.bz2
misskey-0e4a111f81cceed275d9bec2695f6e401fb654d8.zip
refactoring
Resolve #7779
Diffstat (limited to 'src/server/api/endpoints/i/2fa')
-rw-r--r--src/server/api/endpoints/i/2fa/done.ts41
-rw-r--r--src/server/api/endpoints/i/2fa/key-done.ts150
-rw-r--r--src/server/api/endpoints/i/2fa/password-less.ts21
-rw-r--r--src/server/api/endpoints/i/2fa/register-key.ts59
-rw-r--r--src/server/api/endpoints/i/2fa/register.ts54
-rw-r--r--src/server/api/endpoints/i/2fa/remove-key.ts45
-rw-r--r--src/server/api/endpoints/i/2fa/unregister.ts32
7 files changed, 0 insertions, 402 deletions
diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts
deleted file mode 100644
index 2bd2128cce..0000000000
--- a/src/server/api/endpoints/i/2fa/done.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import $ from 'cafy';
-import * as speakeasy from 'speakeasy';
-import define from '../../../define';
-import { UserProfiles } from '@/models/index';
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- token: {
- validator: $.str
- }
- }
-};
-
-export default define(meta, async (ps, user) => {
- const token = ps.token.replace(/\s/g, '');
-
- const profile = await UserProfiles.findOneOrFail(user.id);
-
- if (profile.twoFactorTempSecret == null) {
- throw new Error('二段階認証の設定が開始されていません');
- }
-
- const verified = (speakeasy as any).totp.verify({
- secret: profile.twoFactorTempSecret,
- encoding: 'base32',
- token: token
- });
-
- if (!verified) {
- throw new Error('not verified');
- }
-
- await UserProfiles.update(user.id, {
- twoFactorSecret: profile.twoFactorTempSecret,
- twoFactorEnabled: true
- });
-});
diff --git a/src/server/api/endpoints/i/2fa/key-done.ts b/src/server/api/endpoints/i/2fa/key-done.ts
deleted file mode 100644
index b4d3af235a..0000000000
--- a/src/server/api/endpoints/i/2fa/key-done.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-import $ from 'cafy';
-import * as bcrypt from 'bcryptjs';
-import { promisify } from 'util';
-import * as cbor from 'cbor';
-import define from '../../../define';
-import {
- UserProfiles,
- UserSecurityKeys,
- AttestationChallenges,
- Users
-} from '@/models/index';
-import config from '@/config/index';
-import { procedures, hash } from '../../../2fa';
-import { publishMainStream } from '@/services/stream';
-
-const cborDecodeFirst = promisify(cbor.decodeFirst) as any;
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- clientDataJSON: {
- validator: $.str
- },
- attestationObject: {
- validator: $.str
- },
- password: {
- validator: $.str
- },
- challengeId: {
- validator: $.str
- },
- name: {
- validator: $.str
- }
- }
-};
-
-const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8'));
-
-export default define(meta, async (ps, user) => {
- const profile = await UserProfiles.findOneOrFail(user.id);
-
- // Compare password
- const same = await bcrypt.compare(ps.password, profile.password!);
-
- if (!same) {
- throw new Error('incorrect password');
- }
-
- if (!profile.twoFactorEnabled) {
- throw new Error('2fa not enabled');
- }
-
- const clientData = JSON.parse(ps.clientDataJSON);
-
- if (clientData.type != 'webauthn.create') {
- throw new Error('not a creation attestation');
- }
- if (clientData.origin != config.scheme + '://' + config.host) {
- throw new Error('origin mismatch');
- }
-
- const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8'));
-
- const attestation = await cborDecodeFirst(ps.attestationObject);
-
- const rpIdHash = attestation.authData.slice(0, 32);
- if (!rpIdHashReal.equals(rpIdHash)) {
- throw new Error('rpIdHash mismatch');
- }
-
- const flags = attestation.authData[32];
-
- // tslint:disable-next-line:no-bitwise
- if (!(flags & 1)) {
- throw new Error('user not present');
- }
-
- const authData = Buffer.from(attestation.authData);
- const credentialIdLength = authData.readUInt16BE(53);
- const credentialId = authData.slice(55, 55 + credentialIdLength);
- const publicKeyData = authData.slice(55 + credentialIdLength);
- const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
- if (publicKey.get(3) != -7) {
- throw new Error('alg mismatch');
- }
-
- if (!(procedures as any)[attestation.fmt]) {
- throw new Error('unsupported fmt');
- }
-
- const verificationData = (procedures as any)[attestation.fmt].verify({
- attStmt: attestation.attStmt,
- authenticatorData: authData,
- clientDataHash: clientDataJSONHash,
- credentialId,
- publicKey,
- rpIdHash
- });
- if (!verificationData.valid) throw new Error('signature invalid');
-
- const attestationChallenge = await AttestationChallenges.findOne({
- userId: user.id,
- id: ps.challengeId,
- registrationChallenge: true,
- challenge: hash(clientData.challenge).toString('hex')
- });
-
- if (!attestationChallenge) {
- throw new Error('non-existent challenge');
- }
-
- await AttestationChallenges.delete({
- userId: user.id,
- id: ps.challengeId
- });
-
- // Expired challenge (> 5min old)
- if (
- new Date().getTime() - attestationChallenge.createdAt.getTime() >=
- 5 * 60 * 1000
- ) {
- throw new Error('expired challenge');
- }
-
- const credentialIdString = credentialId.toString('hex');
-
- await UserSecurityKeys.save({
- userId: user.id,
- id: credentialIdString,
- lastUsed: new Date(),
- name: ps.name,
- publicKey: verificationData.publicKey.toString('hex')
- });
-
- // Publish meUpdated event
- publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, {
- detail: true,
- includeSecrets: true
- }));
-
- return {
- id: credentialIdString,
- name: ps.name
- };
-});
diff --git a/src/server/api/endpoints/i/2fa/password-less.ts b/src/server/api/endpoints/i/2fa/password-less.ts
deleted file mode 100644
index 064828b638..0000000000
--- a/src/server/api/endpoints/i/2fa/password-less.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import $ from 'cafy';
-import define from '../../../define';
-import { UserProfiles } from '@/models/index';
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- value: {
- validator: $.boolean
- }
- }
-};
-
-export default define(meta, async (ps, user) => {
- await UserProfiles.update(user.id, {
- usePasswordLessLogin: ps.value
- });
-});
diff --git a/src/server/api/endpoints/i/2fa/register-key.ts b/src/server/api/endpoints/i/2fa/register-key.ts
deleted file mode 100644
index 1b385a10ee..0000000000
--- a/src/server/api/endpoints/i/2fa/register-key.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import $ from 'cafy';
-import * as bcrypt from 'bcryptjs';
-import define from '../../../define';
-import { UserProfiles, AttestationChallenges } from '@/models/index';
-import { promisify } from 'util';
-import * as crypto from 'crypto';
-import { genId } from '@/misc/gen-id';
-import { hash } from '../../../2fa';
-
-const randomBytes = promisify(crypto.randomBytes);
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- password: {
- validator: $.str
- }
- }
-};
-
-export default define(meta, async (ps, user) => {
- const profile = await UserProfiles.findOneOrFail(user.id);
-
- // Compare password
- const same = await bcrypt.compare(ps.password, profile.password!);
-
- if (!same) {
- throw new Error('incorrect password');
- }
-
- if (!profile.twoFactorEnabled) {
- throw new Error('2fa not enabled');
- }
-
- // 32 byte challenge
- const entropy = await randomBytes(32);
- const challenge = entropy.toString('base64')
- .replace(/=/g, '')
- .replace(/\+/g, '-')
- .replace(/\//g, '_');
-
- const challengeId = genId();
-
- await AttestationChallenges.save({
- userId: user.id,
- id: challengeId,
- challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'),
- createdAt: new Date(),
- registrationChallenge: true
- });
-
- return {
- challengeId,
- challenge
- };
-});
diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts
deleted file mode 100644
index b03b98188a..0000000000
--- a/src/server/api/endpoints/i/2fa/register.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import $ from 'cafy';
-import * as bcrypt from 'bcryptjs';
-import * as speakeasy from 'speakeasy';
-import * as QRCode from 'qrcode';
-import config from '@/config/index';
-import define from '../../../define';
-import { UserProfiles } from '@/models/index';
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- password: {
- validator: $.str
- }
- }
-};
-
-export default define(meta, async (ps, user) => {
- const profile = await UserProfiles.findOneOrFail(user.id);
-
- // Compare password
- const same = await bcrypt.compare(ps.password, profile.password!);
-
- if (!same) {
- throw new Error('incorrect password');
- }
-
- // Generate user's secret key
- const secret = speakeasy.generateSecret({
- length: 32
- });
-
- await UserProfiles.update(user.id, {
- twoFactorTempSecret: secret.base32
- });
-
- // Get the data URL of the authenticator URL
- const dataUrl = await QRCode.toDataURL(speakeasy.otpauthURL({
- secret: secret.base32,
- encoding: 'base32',
- label: user.username,
- issuer: config.host
- }));
-
- return {
- qr: dataUrl,
- secret: secret.base32,
- label: user.username,
- issuer: config.host
- };
-});
diff --git a/src/server/api/endpoints/i/2fa/remove-key.ts b/src/server/api/endpoints/i/2fa/remove-key.ts
deleted file mode 100644
index dea56301ab..0000000000
--- a/src/server/api/endpoints/i/2fa/remove-key.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import $ from 'cafy';
-import * as bcrypt from 'bcryptjs';
-import define from '../../../define';
-import { UserProfiles, UserSecurityKeys, Users } from '@/models/index';
-import { publishMainStream } from '@/services/stream';
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- password: {
- validator: $.str
- },
- credentialId: {
- validator: $.str
- },
- }
-};
-
-export default define(meta, async (ps, user) => {
- const profile = await UserProfiles.findOneOrFail(user.id);
-
- // Compare password
- const same = await bcrypt.compare(ps.password, profile.password!);
-
- if (!same) {
- throw new Error('incorrect password');
- }
-
- // Make sure we only delete the user's own creds
- await UserSecurityKeys.delete({
- userId: user.id,
- id: ps.credentialId
- });
-
- // Publish meUpdated event
- publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, {
- detail: true,
- includeSecrets: true
- }));
-
- return {};
-});
diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts
deleted file mode 100644
index af53033daa..0000000000
--- a/src/server/api/endpoints/i/2fa/unregister.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import $ from 'cafy';
-import * as bcrypt from 'bcryptjs';
-import define from '../../../define';
-import { UserProfiles } from '@/models/index';
-
-export const meta = {
- requireCredential: true as const,
-
- secure: true,
-
- params: {
- password: {
- validator: $.str
- }
- }
-};
-
-export default define(meta, async (ps, user) => {
- const profile = await UserProfiles.findOneOrFail(user.id);
-
- // Compare password
- const same = await bcrypt.compare(ps.password, profile.password!);
-
- if (!same) {
- throw new Error('incorrect password');
- }
-
- await UserProfiles.update(user.id, {
- twoFactorSecret: null,
- twoFactorEnabled: false
- });
-});