fix: signin の資格情報が足りないだけの場合はエラーにせず200を返すように (#14700)
* fix: signin の資格情報が足りないだけの場合はエラーにせず200を返すように * run api extractor * fix * fix * fix test * /signin -> /signin-flow * fix * fix lint * rename * fix * fix
This commit is contained in:
		| @@ -83,7 +83,7 @@ import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/br | ||||
| import type { OpenOnRemoteOptions } from '@/scripts/please-login.js'; | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'login', v: Misskey.entities.SigninResponse): void; | ||||
| 	(ev: 'login', v: Misskey.entities.SigninFlowResponse): void; | ||||
| }>(); | ||||
|  | ||||
| const props = withDefaults(defineProps<{ | ||||
| @@ -212,23 +212,63 @@ async function onTotpSubmitted(token: string) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| async function tryLogin(req: Partial<Misskey.entities.SigninRequest>): Promise<Misskey.entities.SigninResponse> { | ||||
| async function tryLogin(req: Partial<Misskey.entities.SigninFlowRequest>): Promise<Misskey.entities.SigninFlowResponse> { | ||||
| 	const _req = { | ||||
| 		username: req.username ?? userInfo.value?.username, | ||||
| 		...req, | ||||
| 	}; | ||||
|  | ||||
| 	function assertIsSigninRequest(x: Partial<Misskey.entities.SigninRequest>): x is Misskey.entities.SigninRequest { | ||||
| 	function assertIsSigninFlowRequest(x: Partial<Misskey.entities.SigninFlowRequest>): x is Misskey.entities.SigninFlowRequest { | ||||
| 		return x.username != null; | ||||
| 	} | ||||
|  | ||||
| 	if (!assertIsSigninRequest(_req)) { | ||||
| 	if (!assertIsSigninFlowRequest(_req)) { | ||||
| 		throw new Error('Invalid request'); | ||||
| 	} | ||||
|  | ||||
| 	return await misskeyApi('signin', _req).then(async (res) => { | ||||
| 		emit('login', res); | ||||
| 		await onLoginSucceeded(res); | ||||
| 	return await misskeyApi('signin-flow', _req).then(async (res) => { | ||||
| 		if (res.finished) { | ||||
| 			emit('login', res); | ||||
| 			await onLoginSucceeded(res); | ||||
| 		} else { | ||||
| 			switch (res.next) { | ||||
| 				case 'captcha': { | ||||
| 					needCaptcha.value = true; | ||||
| 					page.value = 'password'; | ||||
| 					break; | ||||
| 				} | ||||
| 				case 'password': { | ||||
| 					needCaptcha.value = false; | ||||
| 					page.value = 'password'; | ||||
| 					break; | ||||
| 				} | ||||
| 				case 'totp': { | ||||
| 					page.value = 'totp'; | ||||
| 					break; | ||||
| 				} | ||||
| 				case 'passkey': { | ||||
| 					if (webAuthnSupported()) { | ||||
| 						credentialRequest.value = parseRequestOptionsFromJSON({ | ||||
| 							publicKey: res.authRequest, | ||||
| 						}); | ||||
| 						page.value = 'passkey'; | ||||
| 					} else { | ||||
| 						page.value = 'totp'; | ||||
| 					} | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (doingPasskeyFromInputPage.value === true) { | ||||
| 				doingPasskeyFromInputPage.value = false; | ||||
| 				page.value = 'input'; | ||||
| 				password.value = ''; | ||||
| 			} | ||||
| 			passwordPageEl.value?.resetCaptcha(); | ||||
| 			nextTick(() => { | ||||
| 				waiting.value = false; | ||||
| 			}); | ||||
| 		} | ||||
| 		return res; | ||||
| 	}).catch((err) => { | ||||
| 		onSigninApiError(err); | ||||
| @@ -236,7 +276,7 @@ async function tryLogin(req: Partial<Misskey.entities.SigninRequest>): Promise<M | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| async function onLoginSucceeded(res: Misskey.entities.SigninResponse) { | ||||
| async function onLoginSucceeded(res: Misskey.entities.SigninFlowResponse & { finished: true; }) { | ||||
| 	if (props.autoSet) { | ||||
| 		await login(res.i); | ||||
| 	} | ||||
| @@ -245,112 +285,82 @@ async function onLoginSucceeded(res: Misskey.entities.SigninResponse) { | ||||
| function onSigninApiError(err?: any): void { | ||||
| 	const id = err?.id ?? null; | ||||
|  | ||||
| 	if (typeof err === 'object' && 'next' in err) { | ||||
| 		switch (err.next) { | ||||
| 			case 'captcha': { | ||||
| 				needCaptcha.value = true; | ||||
| 				page.value = 'password'; | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'password': { | ||||
| 				needCaptcha.value = false; | ||||
| 				page.value = 'password'; | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'totp': { | ||||
| 				page.value = 'totp'; | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'passkey': { | ||||
| 				if (webAuthnSupported() && 'authRequest' in err) { | ||||
| 					credentialRequest.value = parseRequestOptionsFromJSON({ | ||||
| 						publicKey: err.authRequest, | ||||
| 					}); | ||||
| 					page.value = 'passkey'; | ||||
| 				} else { | ||||
| 					page.value = 'totp'; | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 	switch (id) { | ||||
| 		case '6cc579cc-885d-43d8-95c2-b8c7fc963280': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.noSuchUser, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 	} else { | ||||
| 		switch (id) { | ||||
| 			case '6cc579cc-885d-43d8-95c2-b8c7fc963280': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.noSuchUser, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.incorrectPassword, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'e03a5f46-d309-4865-9b69-56282d94e1eb': { | ||||
| 				showSuspendedDialog(); | ||||
| 				break; | ||||
| 			} | ||||
| 			case '22d05606-fbcf-421a-a2db-b32610dcfd1b': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.rateLimitExceeded, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.incorrectTotp, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case '36b96a7d-b547-412d-aeed-2d611cdc8cdc': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.unknownWebAuthnKey, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case '93b86c4b-72f9-40eb-9815-798928603d1e': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.passkeyVerificationFailed, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'b18c89a7-5b5e-4cec-bb5b-0419f332d430': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.passkeyVerificationFailed, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case '2d84773e-f7b7-4d0b-8f72-bb69b584c912': { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: i18n.ts.passkeyVerificationSucceededButPasswordlessLoginDisabled, | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			default: { | ||||
| 				console.error(err); | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					title: i18n.ts.loginFailed, | ||||
| 					text: JSON.stringify(err), | ||||
| 				}); | ||||
| 			} | ||||
| 		case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.incorrectPassword, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'e03a5f46-d309-4865-9b69-56282d94e1eb': { | ||||
| 			showSuspendedDialog(); | ||||
| 			break; | ||||
| 		} | ||||
| 		case '22d05606-fbcf-421a-a2db-b32610dcfd1b': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.rateLimitExceeded, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.incorrectTotp, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		case '36b96a7d-b547-412d-aeed-2d611cdc8cdc': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.unknownWebAuthnKey, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		case '93b86c4b-72f9-40eb-9815-798928603d1e': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.passkeyVerificationFailed, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'b18c89a7-5b5e-4cec-bb5b-0419f332d430': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.passkeyVerificationFailed, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		case '2d84773e-f7b7-4d0b-8f72-bb69b584c912': { | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: i18n.ts.passkeyVerificationSucceededButPasswordlessLoginDisabled, | ||||
| 			}); | ||||
| 			break; | ||||
| 		} | ||||
| 		default: { | ||||
| 			console.error(err); | ||||
| 			os.alert({ | ||||
| 				type: 'error', | ||||
| 				title: i18n.ts.loginFailed, | ||||
| 				text: JSON.stringify(err), | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -98,7 +98,7 @@ const props = withDefaults(defineProps<{ | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'signup', user: Misskey.entities.SigninResponse): void; | ||||
| 	(ev: 'signup', user: Misskey.entities.SigninFlowResponse): void; | ||||
| 	(ev: 'signupEmailPending'): void; | ||||
| }>(); | ||||
|  | ||||
| @@ -269,14 +269,19 @@ async function onSubmit(): Promise<void> { | ||||
| 			}); | ||||
| 			emit('signupEmailPending'); | ||||
| 		} else { | ||||
| 			const res = await misskeyApi('signin', { | ||||
| 			const res = await misskeyApi('signin-flow', { | ||||
| 				username: username.value, | ||||
| 				password: password.value, | ||||
| 			}); | ||||
| 			emit('signup', res); | ||||
|  | ||||
| 			if (props.autoSet) { | ||||
| 			if (props.autoSet && res.finished) { | ||||
| 				return login(res.i); | ||||
| 			} else { | ||||
| 				os.alert({ | ||||
| 					type: 'error', | ||||
| 					text: i18n.ts.somethingHappened, | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} catch { | ||||
|   | ||||
| @@ -47,7 +47,7 @@ const props = withDefaults(defineProps<{ | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'done', res: Misskey.entities.SigninResponse): void; | ||||
| 	(ev: 'done', res: Misskey.entities.SigninFlowResponse): void; | ||||
| 	(ev: 'closed'): void; | ||||
| }>(); | ||||
|  | ||||
| @@ -55,7 +55,7 @@ const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); | ||||
|  | ||||
| const isAcceptedServerRule = ref(false); | ||||
|  | ||||
| function onSignup(res: Misskey.entities.SigninResponse) { | ||||
| function onSignup(res: Misskey.entities.SigninFlowResponse) { | ||||
| 	emit('done', res); | ||||
| 	dialog.value?.close(); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 かっこかり
					かっこかり