diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2024-10-05 12:03:47 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-05 12:03:47 +0900 |
| commit | ae3c155490d9b5a574c45309744ba2a0cbe78932 (patch) | |
| tree | 95425995a0c47d2861ab054e2a48ea1ab974b538 /packages | |
| parent | :art: (diff) | |
| download | sharkey-ae3c155490d9b5a574c45309744ba2a0cbe78932.tar.gz sharkey-ae3c155490d9b5a574c45309744ba2a0cbe78932.tar.bz2 sharkey-ae3c155490d9b5a574c45309744ba2a0cbe78932.zip | |
fix: signin の資格情報が足りないだけの場合はエラーにせず200を返すように (#14700)
* fix: signin の資格情報が足りないだけの場合はエラーにせず200を返すように
* run api extractor
* fix
* fix
* fix test
* /signin -> /signin-flow
* fix
* fix lint
* rename
* fix
* fix
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/backend/src/server/api/ApiServerService.ts | 2 | ||||
| -rw-r--r-- | packages/backend/src/server/api/SigninApiService.ts | 66 | ||||
| -rw-r--r-- | packages/backend/src/server/api/SigninService.ts | 6 | ||||
| -rw-r--r-- | packages/backend/test/e2e/2fa.ts | 71 | ||||
| -rw-r--r-- | packages/backend/test/e2e/endpoints.ts | 8 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSignin.vue | 236 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSignupDialog.form.vue | 11 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkSignupDialog.vue | 4 | ||||
| -rw-r--r-- | packages/misskey-js/etc/misskey-js.api.md | 24 | ||||
| -rw-r--r-- | packages/misskey-js/src/api.types.ts | 10 | ||||
| -rw-r--r-- | packages/misskey-js/src/entities.ts | 22 |
11 files changed, 228 insertions, 232 deletions
diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 356e145681..6b760c258b 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -133,7 +133,7 @@ export class ApiServerService { 'turnstile-response'?: string; 'm-captcha-response'?: string; }; - }>('/signin', (request, reply) => this.signinApiService.signin(request, reply)); + }>('/signin-flow', (request, reply) => this.signinApiService.signin(request, reply)); fastify.post<{ Body: { diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 81684beb3c..0d24ffa56a 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -5,8 +5,8 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; -import * as OTPAuth from 'otpauth'; import { IsNull } from 'typeorm'; +import * as Misskey from 'misskey-js'; import { DI } from '@/di-symbols.js'; import type { MiMeta, @@ -26,27 +26,9 @@ 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, PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types'; +import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; import type { FastifyReply, FastifyRequest } from 'fastify'; -/** - * next を指定すると、次にクライアント側で行うべき処理を指定できる。 - * - * - `captcha`: パスワードと、(有効になっている場合は)CAPTCHAを求める - * - `password`: パスワードを求める - * - `totp`: ワンタイムパスワードを求める - * - `passkey`: WebAuthn認証を求める(WebAuthnに対応していないブラウザの場合はワンタイムパスワード) - */ - -type SigninErrorResponse = { - id: string; - next?: 'captcha' | 'password' | 'totp'; -} | { - id: string; - next: 'passkey'; - authRequest: PublicKeyCredentialRequestOptionsJSON; -}; - @Injectable() export class SigninApiService { constructor( @@ -101,7 +83,7 @@ export class SigninApiService { const password = body['password']; const token = body['token']; - function error(status: number, error: SigninErrorResponse) { + function error(status: number, error: { id: string }) { reply.code(status); return { error }; } @@ -152,21 +134,17 @@ export class SigninApiService { const securityKeysAvailable = await this.userSecurityKeysRepository.countBy({ userId: user.id }).then(result => result >= 1); if (password == null) { - reply.code(403); + reply.code(200); if (profile.twoFactorEnabled) { return { - error: { - id: '144ff4f8-bd6c-41bc-82c3-b672eb09efbf', - next: 'password', - }, - } satisfies { error: SigninErrorResponse }; + finished: false, + next: 'password', + } satisfies Misskey.entities.SigninFlowResponse; } else { return { - error: { - id: '144ff4f8-bd6c-41bc-82c3-b672eb09efbf', - next: 'captcha', - }, - } satisfies { error: SigninErrorResponse }; + finished: false, + next: 'captcha', + } satisfies Misskey.entities.SigninFlowResponse; } } @@ -178,7 +156,7 @@ export class SigninApiService { // Compare password const same = await bcrypt.compare(password, profile.password!); - const fail = async (status?: number, failure?: SigninErrorResponse) => { + const fail = async (status?: number, failure?: { id: string; }) => { // Append signin history await this.signinsRepository.insert({ id: this.idService.gen(), @@ -268,27 +246,23 @@ export class SigninApiService { const authRequest = await this.webAuthnService.initiateAuthentication(user.id); - reply.code(403); + reply.code(200); return { - error: { - id: '06e661b9-8146-4ae3-bde5-47138c0ae0c4', - next: 'passkey', - authRequest, - }, - } satisfies { error: SigninErrorResponse }; + finished: false, + next: 'passkey', + authRequest, + } satisfies Misskey.entities.SigninFlowResponse; } else { if (!same || !profile.twoFactorEnabled) { return await fail(403, { id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', }); } else { - reply.code(403); + reply.code(200); return { - error: { - id: '144ff4f8-bd6c-41bc-82c3-b672eb09efbf', - next: 'totp', - }, - } satisfies { error: SigninErrorResponse }; + finished: false, + next: 'totp', + } satisfies Misskey.entities.SigninFlowResponse; } } // never get here diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 4b041f373f..640356b50c 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import * as Misskey from 'misskey-js'; import { DI } from '@/di-symbols.js'; import type { SigninsRepository, UserProfilesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; @@ -57,9 +58,10 @@ export class SigninService { reply.code(200); return { + finished: true, id: user.id, - i: user.token, - }; + i: user.token!, + } satisfies Misskey.entities.SigninFlowResponse; } } diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts index 88c32b4346..48e1bababb 100644 --- a/packages/backend/test/e2e/2fa.ts +++ b/packages/backend/test/e2e/2fa.ts @@ -136,7 +136,7 @@ describe('2要素認証', () => { keyName: string, credentialId: Buffer, requestOptions: PublicKeyCredentialRequestOptionsJSON, - }): misskey.entities.SigninRequest => { + }): misskey.entities.SigninFlowRequest => { // AuthenticatorAssertionResponse.authenticatorData // https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAssertionResponse/authenticatorData const authenticatorData = Buffer.concat([ @@ -196,22 +196,21 @@ describe('2要素認証', () => { }, alice); assert.strictEqual(doneResponse.status, 200); - const signinWithoutTokenResponse = await api('signin', { + const signinWithoutTokenResponse = await api('signin-flow', { ...signinParam(), }); - assert.strictEqual(signinWithoutTokenResponse.status, 403); + assert.strictEqual(signinWithoutTokenResponse.status, 200); assert.deepStrictEqual(signinWithoutTokenResponse.body, { - error: { - id: '144ff4f8-bd6c-41bc-82c3-b672eb09efbf', - next: 'totp', - }, + finished: false, + next: 'totp', }); - const signinResponse = await api('signin', { + const signinResponse = await api('signin-flow', { ...signinParam(), token: otpToken(registerResponse.body.secret), }); assert.strictEqual(signinResponse.status, 200); + assert.strictEqual(signinResponse.body.finished, true); assert.notEqual(signinResponse.body.i, undefined); // 後片付け @@ -252,29 +251,23 @@ describe('2要素認証', () => { assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url')); assert.strictEqual(keyDoneResponse.body.name, keyName); - const signinResponse = await api('signin', { + const signinResponse = await api('signin-flow', { ...signinParam(), }); - const signinResponseBody = signinResponse.body as unknown as { - error: { - id: string; - next: 'passkey'; - authRequest: PublicKeyCredentialRequestOptionsJSON; - }; - }; - assert.strictEqual(signinResponse.status, 403); - assert.strictEqual(signinResponseBody.error.id, '06e661b9-8146-4ae3-bde5-47138c0ae0c4'); - assert.strictEqual(signinResponseBody.error.next, 'passkey'); - assert.notEqual(signinResponseBody.error.authRequest.challenge, undefined); - assert.notEqual(signinResponseBody.error.authRequest.allowCredentials, undefined); - assert.strictEqual(signinResponseBody.error.authRequest.allowCredentials && signinResponseBody.error.authRequest.allowCredentials[0]?.id, credentialId.toString('base64url')); + assert.strictEqual(signinResponse.status, 200); + assert.strictEqual(signinResponse.body.finished, false); + assert.strictEqual(signinResponse.body.next, 'passkey'); + assert.notEqual(signinResponse.body.authRequest.challenge, undefined); + assert.notEqual(signinResponse.body.authRequest.allowCredentials, undefined); + assert.strictEqual(signinResponse.body.authRequest.allowCredentials && signinResponse.body.authRequest.allowCredentials[0]?.id, credentialId.toString('base64url')); - const signinResponse2 = await api('signin', signinWithSecurityKeyParam({ + const signinResponse2 = await api('signin-flow', signinWithSecurityKeyParam({ keyName, credentialId, - requestOptions: signinResponseBody.error.authRequest, + requestOptions: signinResponse.body.authRequest, })); assert.strictEqual(signinResponse2.status, 200); + assert.strictEqual(signinResponse2.body.finished, true); assert.notEqual(signinResponse2.body.i, undefined); // 後片付け @@ -320,32 +313,26 @@ describe('2要素認証', () => { assert.strictEqual(iResponse.status, 200); assert.strictEqual(iResponse.body.usePasswordLessLogin, true); - const signinResponse = await api('signin', { + const signinResponse = await api('signin-flow', { ...signinParam(), password: '', }); - const signinResponseBody = signinResponse.body as unknown as { - error: { - id: string; - next: 'passkey'; - authRequest: PublicKeyCredentialRequestOptionsJSON; - }; - }; - assert.strictEqual(signinResponse.status, 403); - assert.strictEqual(signinResponseBody.error.id, '06e661b9-8146-4ae3-bde5-47138c0ae0c4'); - assert.strictEqual(signinResponseBody.error.next, 'passkey'); - assert.notEqual(signinResponseBody.error.authRequest.challenge, undefined); - assert.notEqual(signinResponseBody.error.authRequest.allowCredentials, undefined); + assert.strictEqual(signinResponse.status, 200); + assert.strictEqual(signinResponse.body.finished, false); + assert.strictEqual(signinResponse.body.next, 'passkey'); + assert.notEqual(signinResponse.body.authRequest.challenge, undefined); + assert.notEqual(signinResponse.body.authRequest.allowCredentials, undefined); - const signinResponse2 = await api('signin', { + const signinResponse2 = await api('signin-flow', { ...signinWithSecurityKeyParam({ keyName, credentialId, - requestOptions: signinResponseBody.error.authRequest, + requestOptions: signinResponse.body.authRequest, } as any), password: '', }); assert.strictEqual(signinResponse2.status, 200); + assert.strictEqual(signinResponse2.body.finished, true); assert.notEqual(signinResponse2.body.i, undefined); // 後片付け @@ -450,11 +437,12 @@ describe('2要素認証', () => { assert.strictEqual(afterIResponse.status, 200); assert.strictEqual(afterIResponse.body.securityKeys, false); - const signinResponse = await api('signin', { + const signinResponse = await api('signin-flow', { ...signinParam(), token: otpToken(registerResponse.body.secret), }); assert.strictEqual(signinResponse.status, 200); + assert.strictEqual(signinResponse.body.finished, true); assert.notEqual(signinResponse.body.i, undefined); // 後片付け @@ -485,10 +473,11 @@ describe('2要素認証', () => { }, alice); assert.strictEqual(unregisterResponse.status, 204); - const signinResponse = await api('signin', { + const signinResponse = await api('signin-flow', { ...signinParam(), }); assert.strictEqual(signinResponse.status, 200); + assert.strictEqual(signinResponse.body.finished, true); assert.notEqual(signinResponse.body.i, undefined); // 後片付け diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts index 5aaec7f6f9..b91d77c398 100644 --- a/packages/backend/test/e2e/endpoints.ts +++ b/packages/backend/test/e2e/endpoints.ts @@ -66,9 +66,9 @@ describe('Endpoints', () => { }); }); - describe('signin', () => { + describe('signin-flow', () => { test('間違ったパスワードでサインインできない', async () => { - const res = await api('signin', { + const res = await api('signin-flow', { username: 'test1', password: 'bar', }); @@ -77,7 +77,7 @@ describe('Endpoints', () => { }); test('クエリをインジェクションできない', async () => { - const res = await api('signin', { + const res = await api('signin-flow', { username: 'test1', // @ts-expect-error password must be string password: { @@ -89,7 +89,7 @@ describe('Endpoints', () => { }); test('正しい情報でサインインできる', async () => { - const res = await api('signin', { + const res = await api('signin-flow', { username: 'test1', password: 'test1', }); diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index 03dd61f6c6..26e1ac516c 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -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), + }); } } diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 38cac7f644..ff096dc729 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -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 { diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue index 97310d32a6..4cccd99492 100644 --- a/packages/frontend/src/components/MkSignupDialog.vue +++ b/packages/frontend/src/components/MkSignupDialog.vue @@ -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(); } diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 9ad784c296..732352abd8 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1158,9 +1158,9 @@ export type Endpoints = Overwrite<Endpoints_2, { req: SignupPendingRequest; res: SignupPendingResponse; }; - 'signin': { - req: SigninRequest; - res: SigninResponse; + 'signin-flow': { + req: SigninFlowRequest; + res: SigninFlowResponse; }; 'signin-with-passkey': { req: SigninWithPasskeyRequest; @@ -1208,11 +1208,11 @@ declare namespace entities { SignupResponse, SignupPendingRequest, SignupPendingResponse, - SigninRequest, + SigninFlowRequest, + SigninFlowResponse, SigninWithPasskeyRequest, SigninWithPasskeyInitResponse, SigninWithPasskeyResponse, - SigninResponse, PartialRolePolicyOverride, EmptyRequest, EmptyResponse, @@ -3038,7 +3038,7 @@ type ServerStatsLog = ServerStats[]; type Signin = components['schemas']['Signin']; // @public (undocumented) -type SigninRequest = { +type SigninFlowRequest = { username: string; password?: string; token?: string; @@ -3050,9 +3050,17 @@ type SigninRequest = { }; // @public (undocumented) -type SigninResponse = { +type SigninFlowResponse = { + finished: true; id: User['id']; i: string; +} | { + finished: false; + next: 'captcha' | 'password' | 'totp'; +} | { + finished: false; + next: 'passkey'; + authRequest: PublicKeyCredentialRequestOptionsJSON; }; // @public (undocumented) @@ -3069,7 +3077,7 @@ type SigninWithPasskeyRequest = { // @public (undocumented) type SigninWithPasskeyResponse = { - signinResponse: SigninResponse; + signinResponse: SigninFlowResponse; }; // @public (undocumented) diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index cef5ab8861..838949f8e1 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -3,8 +3,8 @@ import { UserDetailed } from './autogen/models.js'; import { AdminRolesCreateRequest, AdminRolesCreateResponse, UsersShowRequest } from './autogen/entities.js'; import { PartialRolePolicyOverride, - SigninRequest, - SigninResponse, + SigninFlowRequest, + SigninFlowResponse, SigninWithPasskeyInitResponse, SigninWithPasskeyRequest, SigninWithPasskeyResponse, @@ -81,9 +81,9 @@ export type Endpoints = Overwrite< res: SignupPendingResponse; }, // api.jsonには載せないものなのでここで定義 - 'signin': { - req: SigninRequest; - res: SigninResponse; + 'signin-flow': { + req: SigninFlowRequest; + res: SigninFlowResponse; }, 'signin-with-passkey': { req: SigninWithPasskeyRequest; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 98ac50e5a1..8bbc9c113b 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -267,7 +267,7 @@ export type SignupPendingResponse = { i: string, }; -export type SigninRequest = { +export type SigninFlowRequest = { username: string; password?: string; token?: string; @@ -278,6 +278,19 @@ export type SigninRequest = { 'm-captcha-response'?: string | null; }; +export type SigninFlowResponse = { + finished: true; + id: User['id']; + i: string; +} | { + finished: false; + next: 'captcha' | 'password' | 'totp'; +} | { + finished: false; + next: 'passkey'; + authRequest: PublicKeyCredentialRequestOptionsJSON; +}; + export type SigninWithPasskeyRequest = { credential?: AuthenticationResponseJSON; context?: string; @@ -289,12 +302,7 @@ export type SigninWithPasskeyInitResponse = { }; export type SigninWithPasskeyResponse = { - signinResponse: SigninResponse; -}; - -export type SigninResponse = { - id: User['id'], - i: string, + signinResponse: SigninFlowResponse; }; type Values<T extends Record<PropertyKey, unknown>> = T[keyof T]; |