summaryrefslogtreecommitdiff
path: root/src/server/api/endpoints/i/2fa/key-done.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/api/endpoints/i/2fa/key-done.ts')
-rw-r--r--src/server/api/endpoints/i/2fa/key-done.ts150
1 files changed, 0 insertions, 150 deletions
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
- };
-});