fix(auth): resume Kakao OTP during auto login
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { assert } from 'chai';
|
||||
import { classifyAutoLoginFailure, OTP_REQUIRED_MESSAGE } from '../ts/gateway/loginFlow';
|
||||
|
||||
describe('automatic login failure flow', () => {
|
||||
it('prompts for OTP without treating the challenge as a terminal login failure', () => {
|
||||
assert.equal(
|
||||
classifyAutoLoginFailure({
|
||||
result: false,
|
||||
silent: false,
|
||||
reqOTP: true,
|
||||
reason: '인증 코드를 입력해주세요',
|
||||
}, 0),
|
||||
'prompt_otp'
|
||||
);
|
||||
assert.equal(OTP_REQUIRED_MESSAGE, '인증 코드 입력이 필요합니다.');
|
||||
});
|
||||
|
||||
it('keeps the existing retry and alert behavior for non-OTP failures', () => {
|
||||
assert.equal(
|
||||
classifyAutoLoginFailure({
|
||||
result: false,
|
||||
silent: false,
|
||||
reason: '자동 로그인: 절차 오류',
|
||||
}, 0),
|
||||
'retry'
|
||||
);
|
||||
assert.equal(
|
||||
classifyAutoLoginFailure({
|
||||
result: false,
|
||||
silent: false,
|
||||
reason: '로그인할 수 없습니다.',
|
||||
}, 1),
|
||||
'alert'
|
||||
);
|
||||
assert.equal(
|
||||
classifyAutoLoginFailure({
|
||||
result: false,
|
||||
silent: true,
|
||||
reason: 'failed',
|
||||
}, 1),
|
||||
'silent'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -36,5 +36,6 @@ export type AutoLoginResponse = {
|
||||
export type AutoLoginFailed = {
|
||||
result: false,
|
||||
silent: boolean,
|
||||
reqOTP?: boolean,
|
||||
reason: string,
|
||||
}
|
||||
}
|
||||
|
||||
+28
-25
@@ -12,8 +12,9 @@ import { delay } from '@util/delay';
|
||||
import { Modal } from 'bootstrap';
|
||||
import '@/gateway/common';
|
||||
import { isString } from 'lodash-es';
|
||||
import { SammoRootAPI, type InvalidResponse } from '@/SammoRootAPI';
|
||||
import type { LoginFailed, LoginResponse, LoginResponseWithKakao, OTPResponse } from '@/defs/API/Login';
|
||||
import { SammoRootAPI, type InvalidResponse } from '@/SammoRootAPI';
|
||||
import type { LoginFailed, LoginResponse, LoginResponseWithKakao, OTPResponse } from '@/defs/API/Login';
|
||||
import { classifyAutoLoginFailure, OTP_REQUIRED_MESSAGE } from '@/gateway/loginFlow';
|
||||
declare global {
|
||||
interface Window {
|
||||
getOAuthToken: (mode: string, scope_list: string[]) => void;
|
||||
@@ -51,9 +52,20 @@ function getToken(): [number, string] | undefined {
|
||||
return token;
|
||||
}
|
||||
|
||||
function resetToken() {
|
||||
localStorage.removeItem(LOGIN_TOKEN_KEY);
|
||||
}
|
||||
function resetToken() {
|
||||
localStorage.removeItem(LOGIN_TOKEN_KEY);
|
||||
}
|
||||
|
||||
function showOTPModal() {
|
||||
const modalEl = unwrap(document.querySelector('#modalOTP'));
|
||||
if (!modalOTP) {
|
||||
modalOTP = new Modal(modalEl);
|
||||
modalEl.addEventListener('shown.bs.modal', function () {
|
||||
unwrap(document.querySelector<HTMLElement>('#otp_code')).focus();
|
||||
});
|
||||
}
|
||||
modalOTP.show();
|
||||
}
|
||||
|
||||
async function tryAutoLogin() {
|
||||
try {
|
||||
@@ -93,13 +105,18 @@ async function tryAutoLogin() {
|
||||
}, true);
|
||||
|
||||
if (!loginResult.result) {
|
||||
if (loginResult.reason === '자동 로그인: 절차 오류' && attempt === 0) {
|
||||
const failureAction = classifyAutoLoginFailure(loginResult, attempt);
|
||||
if (failureAction === 'retry') {
|
||||
console.warn('auto login failed by procedure error. retrying once.');
|
||||
await delay(150);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!loginResult.silent) {
|
||||
if (failureAction === 'prompt_otp') {
|
||||
alert(OTP_REQUIRED_MESSAGE);
|
||||
showOTPModal();
|
||||
return;
|
||||
}
|
||||
if (failureAction === 'alert') {
|
||||
alert(loginResult.reason);
|
||||
}
|
||||
console.error(loginResult.reason);
|
||||
@@ -208,14 +225,7 @@ async function doLoginUsingOAuth() {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalEl = unwrap(document.querySelector('#modalOTP'))
|
||||
if (!modalOTP) {
|
||||
modalOTP = new Modal(modalEl);
|
||||
modalEl.addEventListener('shown.bs.modal', function () {
|
||||
unwrap(document.querySelector<HTMLElement>('#otp_code')).focus();
|
||||
});
|
||||
}
|
||||
modalOTP.show();
|
||||
showOTPModal();
|
||||
}
|
||||
|
||||
function postOAuthResult(mode: string) {
|
||||
@@ -314,14 +324,7 @@ $(async function ($) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalEl = unwrap(document.querySelector('#modalOTP'))
|
||||
if (!modalOTP) {
|
||||
modalOTP = new Modal(modalEl);
|
||||
modalEl.addEventListener('shown.bs.modal', function () {
|
||||
unwrap(document.querySelector<HTMLElement>('#otp_code')).focus();
|
||||
});
|
||||
}
|
||||
modalOTP.show();
|
||||
showOTPModal();
|
||||
});
|
||||
|
||||
$('#otp_form').on('submit', async function (e) {
|
||||
@@ -393,4 +396,4 @@ window.fitIframe = function () {
|
||||
const iframe = unwrap(document.querySelector<HTMLIFrameElement>('#running_map'));//TODO: 근황 여러개 볼 수 있도록?
|
||||
const scrollHeight = unwrap(iframe.contentWindow).document.body.scrollHeight;
|
||||
iframe.style.height = `${scrollHeight}px`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { AutoLoginFailed } from '@/defs/API/Login';
|
||||
|
||||
export const OTP_REQUIRED_MESSAGE = '인증 코드 입력이 필요합니다.';
|
||||
|
||||
export type AutoLoginFailureAction = 'retry' | 'prompt_otp' | 'alert' | 'silent';
|
||||
|
||||
export function classifyAutoLoginFailure(result: AutoLoginFailed, attempt: number): AutoLoginFailureAction {
|
||||
if (result.reason === '자동 로그인: 절차 오류' && attempt === 0) {
|
||||
return 'retry';
|
||||
}
|
||||
if (result.reqOTP) {
|
||||
return 'prompt_otp';
|
||||
}
|
||||
if (result.silent) {
|
||||
return 'silent';
|
||||
}
|
||||
return 'alert';
|
||||
}
|
||||
Reference in New Issue
Block a user