카카오 OTP 만료 시각을 한국 시간으로 표시

This commit is contained in:
2026-08-30 01:37:58 +00:00
parent 7377aa62ed
commit d68209879b
2 changed files with 8 additions and 2 deletions
@@ -2,6 +2,8 @@ import { randomInt } from 'node:crypto';
import { addDays, addSeconds, isAfter, isValid, parseISO } from 'date-fns'; import { addDays, addSeconds, isAfter, isValid, parseISO } from 'date-fns';
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
import type { KakaoOAuthClient, KakaoOAuthToken, KakaoUserInfo } from './kakaoClient.js'; import type { KakaoOAuthClient, KakaoOAuthToken, KakaoUserInfo } from './kakaoClient.js';
import type { OAuthSessionStore } from './oauthSessionStore.js'; import type { OAuthSessionStore } from './oauthSessionStore.js';
import type { UserOAuthInfo, UserRecord, UserRepository } from './userRepository.js'; import type { UserOAuthInfo, UserRecord, UserRepository } from './userRepository.js';
@@ -221,7 +223,7 @@ export const requireKakaoTalkProof = async (options: {
try { try {
await options.kakaoClient.sendTalkMessage( await options.kakaoClient.sendTalkMessage(
options.accessToken, options.accessToken,
`인증 코드는 ${challenge.code} 입니다. ${challenge.expiresAt} 이내에 입력해 주세요.`, `인증 코드는 ${challenge.code} 입니다. ${formatServerDateTime(challenge.expiresAt)} (한국 시간) 이내에 입력해 주세요.`,
options.publicBaseUrl options.publicBaseUrl
); );
} catch (error) { } catch (error) {
@@ -253,7 +255,7 @@ export const verifyKakaoTalkChallenge = async (options: {
if (result.status === 'locked') { if (result.status === 'locked') {
throw new KakaoVerificationError( throw new KakaoVerificationError(
'IDENTITY_MISMATCH', 'IDENTITY_MISMATCH',
`인증 실패 횟수를 초과했습니다. ${result.expiresAt}까지 기다려 주세요.` `인증 실패 횟수를 초과했습니다. ${formatServerDateTime(result.expiresAt)} (한국 시간)까지 기다려 주세요.`
); );
} }
if (result.status === 'mismatch') { if (result.status === 'mismatch') {
+4
View File
@@ -14,6 +14,7 @@ import { InMemoryProfileStatusService } from '../src/lobby/profileStatusService.
import { appRouter } from '../src/router.js'; import { appRouter } from '../src/router.js';
import type { GatewayPrismaClient } from '@sammo-ts/infra'; import type { GatewayPrismaClient } from '@sammo-ts/infra';
import { decryptGameSessionToken, type UserSanctions } from '@sammo-ts/common/auth/gameToken'; import { decryptGameSessionToken, type UserSanctions } from '@sammo-ts/common/auth/gameToken';
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
import { createPasswordEnvelopeService } from '../src/auth/passwordEnvelope.js'; import { createPasswordEnvelopeService } from '../src/auth/passwordEnvelope.js';
import type { GatewayProfileRepository } from '../src/orchestrator/profileRepository.js'; import type { GatewayProfileRepository } from '../src/orchestrator/profileRepository.js';
@@ -987,6 +988,9 @@ describe('gateway auth flow', () => {
}); });
const code = sentTalkMessages[0]?.match(/인증 코드는 (\d{4})/)?.[1]; const code = sentTalkMessages[0]?.match(/인증 코드는 (\d{4})/)?.[1];
expect(code).toBeTruthy(); expect(code).toBeTruthy();
expect(sentTalkMessages[0]).toBe(
`인증 코드는 ${code} 입니다. ${formatServerDateTime(login.expiresAt)} (한국 시간) 이내에 입력해 주세요.`
);
const completed = await caller.auth.kakaoOtp({ challengeId: login.challengeId, code: code! }); const completed = await caller.auth.kakaoOtp({ challengeId: login.challengeId, code: code! });
expect(completed.validUntil).toBeTruthy(); expect(completed.validUntil).toBeTruthy();
expect((await users.findById(user.id))?.kakaoTalkVerifiedUntil).toBe(completed.validUntil); expect((await users.findById(user.id))?.kakaoTalkVerifiedUntil).toBe(completed.validUntil);