Merge remote-tracking branch 'msky/signin-captcha' into enh-tweak-signin-dialog
This commit is contained in:
		
						commit
						d5a3fb1916
					
				|  | @ -9,6 +9,7 @@ import * as OTPAuth from 'otpauth'; | |||
| import { IsNull } from 'typeorm'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import type { | ||||
| 	MiMeta, | ||||
| 	SigninsRepository, | ||||
| 	UserProfilesRepository, | ||||
| 	UsersRepository, | ||||
|  | @ -20,6 +21,8 @@ import { IdService } from '@/core/IdService.js'; | |||
| import { bindThis } from '@/decorators.js'; | ||||
| import { WebAuthnService } from '@/core/WebAuthnService.js'; | ||||
| import { UserAuthService } from '@/core/UserAuthService.js'; | ||||
| import { CaptchaService } from '@/core/CaptchaService.js'; | ||||
| import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; | ||||
| import { RateLimiterService } from './RateLimiterService.js'; | ||||
| import { SigninService } from './SigninService.js'; | ||||
| import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; | ||||
|  | @ -31,6 +34,9 @@ export class SigninApiService { | |||
| 		@Inject(DI.config) | ||||
| 		private config: Config, | ||||
| 
 | ||||
| 		@Inject(DI.meta) | ||||
| 		private meta: MiMeta, | ||||
| 
 | ||||
| 		@Inject(DI.usersRepository) | ||||
| 		private usersRepository: UsersRepository, | ||||
| 
 | ||||
|  | @ -45,6 +51,7 @@ export class SigninApiService { | |||
| 		private signinService: SigninService, | ||||
| 		private userAuthService: UserAuthService, | ||||
| 		private webAuthnService: WebAuthnService, | ||||
| 		private captchaService: CaptchaService, | ||||
| 	) { | ||||
| 	} | ||||
| 
 | ||||
|  | @ -56,6 +63,10 @@ export class SigninApiService { | |||
| 				password: string; | ||||
| 				token?: string; | ||||
| 				credential?: AuthenticationResponseJSON; | ||||
| 				'hcaptcha-response'?: string; | ||||
| 				'g-recaptcha-response'?: string; | ||||
| 				'turnstile-response'?: string; | ||||
| 				'm-captcha-response'?: string; | ||||
| 			}; | ||||
| 		}>, | ||||
| 		reply: FastifyReply, | ||||
|  | @ -139,6 +150,32 @@ export class SigninApiService { | |||
| 		}; | ||||
| 
 | ||||
| 		if (!profile.twoFactorEnabled) { | ||||
| 			if (process.env.NODE_ENV !== 'test') { | ||||
| 				if (this.meta.enableHcaptcha && this.meta.hcaptchaSecretKey) { | ||||
| 					await this.captchaService.verifyHcaptcha(this.meta.hcaptchaSecretKey, body['hcaptcha-response']).catch(err => { | ||||
| 						throw new FastifyReplyError(400, err); | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if (this.meta.enableMcaptcha && this.meta.mcaptchaSecretKey && this.meta.mcaptchaSitekey && this.meta.mcaptchaInstanceUrl) { | ||||
| 					await this.captchaService.verifyMcaptcha(this.meta.mcaptchaSecretKey, this.meta.mcaptchaSitekey, this.meta.mcaptchaInstanceUrl, body['m-captcha-response']).catch(err => { | ||||
| 						throw new FastifyReplyError(400, err); | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if (this.meta.enableRecaptcha && this.meta.recaptchaSecretKey) { | ||||
| 					await this.captchaService.verifyRecaptcha(this.meta.recaptchaSecretKey, body['g-recaptcha-response']).catch(err => { | ||||
| 						throw new FastifyReplyError(400, err); | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if (this.meta.enableTurnstile && this.meta.turnstileSecretKey) { | ||||
| 					await this.captchaService.verifyTurnstile(this.meta.turnstileSecretKey, body['turnstile-response']).catch(err => { | ||||
| 						throw new FastifyReplyError(400, err); | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (same) { | ||||
| 				return this.signinService.signin(request, reply, user); | ||||
| 			} else { | ||||
|  |  | |||
|  | @ -32,6 +32,10 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 				<template #prefix><i class="ti ti-lock"></i></template> | ||||
| 				<template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template> | ||||
| 			</MkInput> | ||||
| 			<MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> | ||||
| 			<MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/> | ||||
| 			<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> | ||||
| 			<MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> | ||||
| 			<MkButton type="submit" large primary rounded :disabled="signing" style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton> | ||||
| 		</div> | ||||
| 		<div v-if="totpLogin" class="2fa-signin" :class="{ securityKeys: user && user.securityKeys }"> | ||||
|  | @ -85,6 +89,7 @@ import * as os from '@/os.js'; | |||
| import { misskeyApi } from '@/scripts/misskey-api.js'; | ||||
| import { login } from '@/account.js'; | ||||
| import { i18n } from '@/i18n.js'; | ||||
| import { instance } from '@/instance.js'; | ||||
| 
 | ||||
| const signing = ref(false); | ||||
| const user = ref<Misskey.entities.UserDetailed | null>(null); | ||||
|  | @ -98,6 +103,10 @@ const isBackupCode = ref(false); | |||
| const queryingKey = ref(false); | ||||
| let credentialRequest: CredentialRequestOptions | null = null; | ||||
| const passkey_context = ref(''); | ||||
| const hCaptchaResponse = ref<string | null>(null); | ||||
| const mCaptchaResponse = ref<string | null>(null); | ||||
| const reCaptchaResponse = ref<string | null>(null); | ||||
| const turnstileResponse = ref<string | null>(null); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'login', v: any): void; | ||||
|  | @ -227,6 +236,10 @@ function onSubmit(): void { | |||
| 		misskeyApi('signin', { | ||||
| 			username: username.value, | ||||
| 			password: password.value, | ||||
| 			'hcaptcha-response': hCaptchaResponse.value, | ||||
| 			'm-captcha-response': mCaptchaResponse.value, | ||||
| 			'g-recaptcha-response': reCaptchaResponse.value, | ||||
| 			'turnstile-response': turnstileResponse.value, | ||||
| 			token: user.value?.twoFactorEnabled ? token.value : undefined, | ||||
| 		}).then(res => { | ||||
| 			emit('login', res); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue