diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-09-08 14:05:03 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-08 14:05:03 +0900 |
| commit | ff9a65e8faa46a101d3ed3dc8915dd1f269ef556 (patch) | |
| tree | a6b1ae734e61da58b4205cd08a505ce392b317a9 /packages/backend/src/server/api/SigninApiService.ts | |
| parent | Update CHANGELOG.md (diff) | |
| download | sharkey-ff9a65e8faa46a101d3ed3dc8915dd1f269ef556.tar.gz sharkey-ff9a65e8faa46a101d3ed3dc8915dd1f269ef556.tar.bz2 sharkey-ff9a65e8faa46a101d3ed3dc8915dd1f269ef556.zip | |
feat: passkey support (#11804)
https://github.com/MisskeyIO/misskey/pull/149
Diffstat (limited to 'packages/backend/src/server/api/SigninApiService.ts')
| -rw-r--r-- | packages/backend/src/server/api/SigninApiService.ts | 115 |
1 files changed, 15 insertions, 100 deletions
diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 58a5cca4fc..ac8371d8d0 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -3,22 +3,26 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { randomBytes } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import * as OTPAuth from 'otpauth'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js'; +import type { + SigninsRepository, + UserProfilesRepository, + UsersRepository, +} from '@/models/index.js'; import type { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { MiLocalUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; -import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; import { bindThis } from '@/decorators.js'; +import { WebAuthnService } from '@/core/WebAuthnService.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; -import type { FastifyRequest, FastifyReply } from 'fastify'; +import type { AuthenticationResponseJSON } from '@simplewebauthn/typescript-types'; +import type { FastifyReply, FastifyRequest } from 'fastify'; @Injectable() export class SigninApiService { @@ -29,22 +33,16 @@ export class SigninApiService { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.userSecurityKeysRepository) - private userSecurityKeysRepository: UserSecurityKeysRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, - @Inject(DI.attestationChallengesRepository) - private attestationChallengesRepository: AttestationChallengesRepository, - @Inject(DI.signinsRepository) private signinsRepository: SigninsRepository, private idService: IdService, private rateLimiterService: RateLimiterService, private signinService: SigninService, - private twoFactorAuthenticationService: TwoFactorAuthenticationService, + private webAuthnService: WebAuthnService, ) { } @@ -55,11 +53,7 @@ export class SigninApiService { username: string; password: string; token?: string; - signature?: string; - authenticatorData?: string; - clientDataJSON?: string; - credentialId?: string; - challengeId?: string; + credential?: AuthenticationResponseJSON; }; }>, reply: FastifyReply, @@ -181,64 +175,16 @@ export class SigninApiService { } else { return this.signinService.signin(request, reply, user); } - } else if (body.credentialId && body.clientDataJSON && body.authenticatorData && body.signature) { + } else if (body.credential) { if (!same && !profile.usePasswordLessLogin) { return await fail(403, { id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', }); } - const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex'); - const clientData = JSON.parse(clientDataJSON.toString('utf-8')); - const challenge = await this.attestationChallengesRepository.findOneBy({ - userId: user.id, - id: body.challengeId, - registrationChallenge: false, - challenge: this.twoFactorAuthenticationService.hash(clientData.challenge).toString('hex'), - }); - - if (!challenge) { - return await fail(403, { - id: '2715a88a-2125-4013-932f-aa6fe72792da', - }); - } - - await this.attestationChallengesRepository.delete({ - userId: user.id, - id: body.challengeId, - }); - - if (new Date().getTime() - challenge.createdAt.getTime() >= 5 * 60 * 1000) { - return await fail(403, { - id: '2715a88a-2125-4013-932f-aa6fe72792da', - }); - } - - const securityKey = await this.userSecurityKeysRepository.findOneBy({ - id: Buffer.from( - body.credentialId - .replace(/-/g, '+') - .replace(/_/g, '/'), - 'base64', - ).toString('hex'), - }); - - if (!securityKey) { - return await fail(403, { - id: '66269679-aeaf-4474-862b-eb761197e046', - }); - } - - const isValid = this.twoFactorAuthenticationService.verifySignin({ - publicKey: Buffer.from(securityKey.publicKey, 'hex'), - authenticatorData: Buffer.from(body.authenticatorData, 'hex'), - clientDataJSON, - clientData, - signature: Buffer.from(body.signature, 'hex'), - challenge: challenge.challenge, - }); + const authorized = await this.webAuthnService.verifyAuthentication(user.id, body.credential); - if (isValid) { + if (authorized) { return this.signinService.signin(request, reply, user); } else { return await fail(403, { @@ -252,42 +198,11 @@ export class SigninApiService { }); } - const keys = await this.userSecurityKeysRepository.findBy({ - userId: user.id, - }); - - if (keys.length === 0) { - return await fail(403, { - id: 'f27fd449-9af4-4841-9249-1f989b9fa4a4', - }); - } - - // 32 byte challenge - const challenge = randomBytes(32).toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); - - const challengeId = this.idService.genId(); - - await this.attestationChallengesRepository.insert({ - userId: user.id, - id: challengeId, - challenge: this.twoFactorAuthenticationService.hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: false, - }); + const authRequest = await this.webAuthnService.initiateAuthentication(user.id); reply.code(200); - return { - challenge, - challengeId, - securityKeys: keys.map(key => ({ - id: key.id, - })), - }; + return authRequest; } // never get here } } - |