From 35b8fcdb351572abd0f5cac30bb7101eb3dcc130 Mon Sep 17 00:00:00 2001 From: hailey Date: Thu, 3 Apr 2025 08:53:34 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=EC=9D=BC=EB=B0=98=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=EC=8B=9C=20reload=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/react-contexts/src/middlewares/refresh-session.ts | 1 - packages/react-contexts/src/session-context/provider/hooks.ts | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-contexts/src/middlewares/refresh-session.ts b/packages/react-contexts/src/middlewares/refresh-session.ts index 87ffc0de0c..dddd6982b5 100644 --- a/packages/react-contexts/src/middlewares/refresh-session.ts +++ b/packages/react-contexts/src/middlewares/refresh-session.ts @@ -59,7 +59,6 @@ export function refreshSessionMiddleware(next: NextMiddleware) { * 401 : TP_SE가 유효하지 않고 TP_TK가 유효한 경우 * 403 : TP_TK가 모두 유효하지 않은 경우 */ - const firstTrialResponse = await get('/api/users/session/verify', options) if (firstTrialResponse.status !== 401) { diff --git a/packages/react-contexts/src/session-context/provider/hooks.ts b/packages/react-contexts/src/session-context/provider/hooks.ts index c6e9528d85..7a4ca7573f 100644 --- a/packages/react-contexts/src/session-context/provider/hooks.ts +++ b/packages/react-contexts/src/session-context/provider/hooks.ts @@ -48,7 +48,10 @@ export function useLogout({ const redirectUrl = getRedirectUrl(redirectLocation) window.location.href = redirectUrl + return } + + window.location.reload() }, [clearUserState]) if (type === 'app') { From ae7e11aedf5fdc4a6c7c4e91b676df25d6b4f879 Mon Sep 17 00:00:00 2001 From: guswl98 Date: Tue, 1 Apr 2025 10:25:22 +0900 Subject: [PATCH 2/7] =?UTF-8?q?refresh=EA=B0=80=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B2=BD=EC=9A=B0=EB=A5=BC=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fetcher/src/factories.ts | 14 ++++++++----- packages/fetcher/src/response-handler.ts | 25 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/fetcher/src/factories.ts b/packages/fetcher/src/factories.ts index 6c5e8a118b..16537ce33f 100644 --- a/packages/fetcher/src/factories.ts +++ b/packages/fetcher/src/factories.ts @@ -2,7 +2,11 @@ import { GetServerSidePropsContext } from 'next' import { generateUrl, parseUrl } from '@titicaca/view-utilities' import { HttpResponse, RequestOptions } from './types' -import { captureHttpError } from './response-handler' +import { + captureHttpError, + handle401Error, + NEED_REFRESH_IDENTIFIER, +} from './response-handler' export type BaseFetcher = < SuccessBody, @@ -140,10 +144,10 @@ export function authFetcherize( return firstTrialResponse } - const { status: firstTrialResponseStatus } = - firstTrialResponse as HttpResponse - - if (firstTrialResponseStatus !== 401) { + const checkFirstTrialResponse = handle401Error( + firstTrialResponse as HttpResponse, + ) + if (checkFirstTrialResponse !== NEED_REFRESH_IDENTIFIER) { return firstTrialResponse } diff --git a/packages/fetcher/src/response-handler.ts b/packages/fetcher/src/response-handler.ts index 9092dabef1..170a2e5adb 100644 --- a/packages/fetcher/src/response-handler.ts +++ b/packages/fetcher/src/response-handler.ts @@ -1,6 +1,7 @@ import { withScope, captureException } from '@sentry/nextjs' import { HttpResponse } from './types' +import { NEED_LOGIN_IDENTIFIER } from './factories' export function captureHttpError< Response extends HttpResponse, @@ -13,3 +14,27 @@ export function captureHttpError< }) } } + +export const ACCESS_TOKEN_EXPIRED_EXCEPTION = 'AccessTokenExpiredException' +export const NEED_REFRESH_IDENTIFIER = 'NEED_REFRESH' + +type ResponseWithError = Pick & { + ok: false + parsedBody: { exception: string; message: string; status: string } +} + +export function handle401Error( + response: HttpResponse, +) { + if (!response.ok) { + const errorResponse = response as ResponseWithError + if (errorResponse.status === 401) { + const { exception } = errorResponse.parsedBody + if (exception === ACCESS_TOKEN_EXPIRED_EXCEPTION) { + return NEED_REFRESH_IDENTIFIER + } + return NEED_LOGIN_IDENTIFIER + } + } + return response +} From 35fcb1af75607c7cbb2b551c8d931cbb0e406aec Mon Sep 17 00:00:00 2001 From: guswl98 Date: Tue, 1 Apr 2025 11:32:42 +0900 Subject: [PATCH 3/7] =?UTF-8?q?handle401Error=20export=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fetcher/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fetcher/src/index.ts b/packages/fetcher/src/index.ts index 171fe3b333..cacf355a9f 100644 --- a/packages/fetcher/src/index.ts +++ b/packages/fetcher/src/index.ts @@ -5,7 +5,7 @@ export { addFetchersToGssp } from './add-fetchers-to-gssp' export { fetcher } from './fetcher' export { get, put, post, del } from './methods' export { authGuardedFetchers } from './auth-guarded-methods' -export { captureHttpError } from './response-handler' +export { captureHttpError, handle401Error } from './response-handler' export { sessionRefresh, sessionRefreshOnSSR, From dc63f529afcce8d74de348b61dd2fb626987a32f Mon Sep 17 00:00:00 2001 From: guswl98 Date: Tue, 1 Apr 2025 11:36:53 +0900 Subject: [PATCH 4/7] =?UTF-8?q?handle401Error=EB=A1=9C=20401=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/middlewares/refresh-session.ts | 13 +++++++++++-- packages/review/src/data/graphql/client.ts | 11 +++++++---- .../standard-action-handler/src/converse.tsx | 17 +++++++++-------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/react-contexts/src/middlewares/refresh-session.ts b/packages/react-contexts/src/middlewares/refresh-session.ts index dddd6982b5..c0198a0101 100644 --- a/packages/react-contexts/src/middlewares/refresh-session.ts +++ b/packages/react-contexts/src/middlewares/refresh-session.ts @@ -12,6 +12,10 @@ import { TP_TK, SESSION_KEY as X_SOTO_SESSION, } from '@titicaca/constants' +import { + handle401Error, + NEED_REFRESH_IDENTIFIER, +} from '@titicaca/fetcher/src/response-handler' import { parseApp } from '../user-agent-context' @@ -59,9 +63,14 @@ export function refreshSessionMiddleware(next: NextMiddleware) { * 401 : TP_SE가 유효하지 않고 TP_TK가 유효한 경우 * 403 : TP_TK가 모두 유효하지 않은 경우 */ - const firstTrialResponse = await get('/api/users/session/verify', options) + const firstTrialResponse = await get< + unknown, + { status: number; exception: string; message: string } + >('/api/users/session/verify', options) + + const checkFirstTrialResponse = handle401Error(firstTrialResponse) - if (firstTrialResponse.status !== 401) { + if (checkFirstTrialResponse !== NEED_REFRESH_IDENTIFIER) { const setCookie = firstTrialResponse.headers.get('set-cookie') if (setCookie) { const setCookies = splitCookiesString(setCookie) diff --git a/packages/review/src/data/graphql/client.ts b/packages/review/src/data/graphql/client.ts index 7a90e6e010..ab38df9b0b 100644 --- a/packages/review/src/data/graphql/client.ts +++ b/packages/review/src/data/graphql/client.ts @@ -1,5 +1,6 @@ import { ClientError, request } from 'graphql-request' import { sessionRefresh } from '@titicaca/fetcher' +import { ACCESS_TOKEN_EXPIRED_EXCEPTION } from '@titicaca/fetcher/src/response-handler' import { Requester, getSdk } from './generated' @@ -18,10 +19,12 @@ export async function reviewClient(query: () => Promise) { return response } catch (e) { if (e instanceof ClientError && e.response.status === 401) { - const refreshResponse = await sessionRefresh({}) - if (refreshResponse) { - const newResponse = await query() - return newResponse + if (e.response.exception === ACCESS_TOKEN_EXPIRED_EXCEPTION) { + const refreshResponse = await sessionRefresh({}) + if (refreshResponse) { + const newResponse = await query() + return newResponse + } } } throw e diff --git a/packages/standard-action-handler/src/converse.tsx b/packages/standard-action-handler/src/converse.tsx index 1c72e95061..576368f572 100644 --- a/packages/standard-action-handler/src/converse.tsx +++ b/packages/standard-action-handler/src/converse.tsx @@ -2,6 +2,8 @@ import qs from 'qs' import { createRoot } from 'react-dom/client' import { Modal } from '@titicaca/modals' +import { authGuardedFetchers, NEED_LOGIN_IDENTIFIER } from '../../fetcher/src' + import { ContextOptions, WebActionParams } from './types' const HASH_CONVERSE_MODAL = 'hash.converse-modal' @@ -92,15 +94,17 @@ export function OpenModal({ async function fetchApi( url: string, ): Promise<{ type: ModalType; title: string; description: string }> { - const response = await fetch(url, { - method: 'POST', + const response = await authGuardedFetchers.post< + { title: string; description: string }, + unknown + >(url, { headers: { 'Content-Type': 'application/json', }, }) - if (!response.ok) { - if (response.status === 400 || response.status === 401) { + if (response === NEED_LOGIN_IDENTIFIER || !response.ok) { + if (response === NEED_LOGIN_IDENTIFIER) { return NEED_LOGIN_CONTENT } return { @@ -110,10 +114,7 @@ async function fetchApi( '서비스 이용이 원활하지 않습니다.\n잠시 후 다시 이용해 주세요.', } } else { - const { title, description } = (await response.json()) as { - title: string - description: string - } + const { title, description } = response.parsedBody return { type: 'normal', title, description } } From 9b49f52cc95f0b0917edd3933927b3ce2a277a45 Mon Sep 17 00:00:00 2001 From: guswl98 Date: Tue, 1 Apr 2025 15:09:28 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=EC=9D=BC=EB=B0=98=20Response=EB=8F=84=2040?= =?UTF-8?q?1=20=EC=B2=98=EB=A6=AC=EA=B0=80=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fetcher/src/factories.ts | 7 ++-- packages/fetcher/src/fetcher.ts | 2 +- packages/fetcher/src/index.ts | 2 +- packages/fetcher/src/response-handler.ts | 36 +++++++++++++------ .../src/middlewares/refresh-session.ts | 2 +- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/fetcher/src/factories.ts b/packages/fetcher/src/factories.ts index 16537ce33f..92620915fc 100644 --- a/packages/fetcher/src/factories.ts +++ b/packages/fetcher/src/factories.ts @@ -144,9 +144,10 @@ export function authFetcherize( return firstTrialResponse } - const checkFirstTrialResponse = handle401Error( - firstTrialResponse as HttpResponse, - ) + const checkFirstTrialResponse = await handle401Error< + SuccessBody, + FailureBody + >(firstTrialResponse as HttpResponse) if (checkFirstTrialResponse !== NEED_REFRESH_IDENTIFIER) { return firstTrialResponse } diff --git a/packages/fetcher/src/fetcher.ts b/packages/fetcher/src/fetcher.ts index 4d2322c0bd..f5db0a0041 100644 --- a/packages/fetcher/src/fetcher.ts +++ b/packages/fetcher/src/fetcher.ts @@ -83,7 +83,7 @@ function makeFetchRetryable({ } } -function readResponseBody(response: Response) { +export function readResponseBody(response: Response) { const contentType = response.headers.get('content-type') const jsonParseAvailable = contentType && /json/.test(contentType) diff --git a/packages/fetcher/src/index.ts b/packages/fetcher/src/index.ts index cacf355a9f..375c77478f 100644 --- a/packages/fetcher/src/index.ts +++ b/packages/fetcher/src/index.ts @@ -2,7 +2,7 @@ export * from './types' export { authFetcherize, ssrFetcherize } from './factories' export { NEED_LOGIN_IDENTIFIER } from './factories' export { addFetchersToGssp } from './add-fetchers-to-gssp' -export { fetcher } from './fetcher' +export { fetcher, readResponseBody } from './fetcher' export { get, put, post, del } from './methods' export { authGuardedFetchers } from './auth-guarded-methods' export { captureHttpError, handle401Error } from './response-handler' diff --git a/packages/fetcher/src/response-handler.ts b/packages/fetcher/src/response-handler.ts index 170a2e5adb..77bc6998c4 100644 --- a/packages/fetcher/src/response-handler.ts +++ b/packages/fetcher/src/response-handler.ts @@ -2,6 +2,7 @@ import { withScope, captureException } from '@sentry/nextjs' import { HttpResponse } from './types' import { NEED_LOGIN_IDENTIFIER } from './factories' +import { readResponseBody } from './fetcher' export function captureHttpError< Response extends HttpResponse, @@ -18,23 +19,38 @@ export function captureHttpError< export const ACCESS_TOKEN_EXPIRED_EXCEPTION = 'AccessTokenExpiredException' export const NEED_REFRESH_IDENTIFIER = 'NEED_REFRESH' +interface ErrorResponseBody { + exception: string + message: string + status: string +} + type ResponseWithError = Pick & { ok: false - parsedBody: { exception: string; message: string; status: string } + parsedBody: ErrorResponseBody } -export function handle401Error( - response: HttpResponse, +export async function handle401Error( + response: HttpResponse | Response, ) { - if (!response.ok) { + if (response.ok) { + return response + } + + let exception = '' + + if (response instanceof Response) { + const parsedBody = (await readResponseBody(response)) as ErrorResponseBody + exception = parsedBody.exception + } else { const errorResponse = response as ResponseWithError if (errorResponse.status === 401) { - const { exception } = errorResponse.parsedBody - if (exception === ACCESS_TOKEN_EXPIRED_EXCEPTION) { - return NEED_REFRESH_IDENTIFIER - } - return NEED_LOGIN_IDENTIFIER + exception = errorResponse.parsedBody.exception } } - return response + + if (exception === ACCESS_TOKEN_EXPIRED_EXCEPTION) { + return NEED_REFRESH_IDENTIFIER + } + return NEED_LOGIN_IDENTIFIER } diff --git a/packages/react-contexts/src/middlewares/refresh-session.ts b/packages/react-contexts/src/middlewares/refresh-session.ts index c0198a0101..3bbc02e171 100644 --- a/packages/react-contexts/src/middlewares/refresh-session.ts +++ b/packages/react-contexts/src/middlewares/refresh-session.ts @@ -68,7 +68,7 @@ export function refreshSessionMiddleware(next: NextMiddleware) { { status: number; exception: string; message: string } >('/api/users/session/verify', options) - const checkFirstTrialResponse = handle401Error(firstTrialResponse) + const checkFirstTrialResponse = await handle401Error(firstTrialResponse) if (checkFirstTrialResponse !== NEED_REFRESH_IDENTIFIER) { const setCookie = firstTrialResponse.headers.get('set-cookie') From 769fd7f8276a4002c19b0d7b81d619330d936923 Mon Sep 17 00:00:00 2001 From: guswl98 Date: Tue, 1 Apr 2025 16:56:03 +0900 Subject: [PATCH 6/7] =?UTF-8?q?accessTokenExpired=20=EB=B3=80=EC=88=98=20e?= =?UTF-8?q?xport=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20package.json=EC=97=90?= =?UTF-8?q?=20fetcher=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fetcher/src/index.ts | 7 ++++++- .../react-contexts/src/middlewares/refresh-session.ts | 11 ++++++----- packages/review/src/data/graphql/client.ts | 6 ++++-- packages/standard-action-handler/package.json | 1 + packages/standard-action-handler/src/converse.tsx | 3 +-- pnpm-lock.yaml | 3 +++ 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/fetcher/src/index.ts b/packages/fetcher/src/index.ts index 375c77478f..09acd3e825 100644 --- a/packages/fetcher/src/index.ts +++ b/packages/fetcher/src/index.ts @@ -5,7 +5,12 @@ export { addFetchersToGssp } from './add-fetchers-to-gssp' export { fetcher, readResponseBody } from './fetcher' export { get, put, post, del } from './methods' export { authGuardedFetchers } from './auth-guarded-methods' -export { captureHttpError, handle401Error } from './response-handler' +export { + captureHttpError, + handle401Error, + NEED_REFRESH_IDENTIFIER, + ACCESS_TOKEN_EXPIRED_EXCEPTION, +} from './response-handler' export { sessionRefresh, sessionRefreshOnSSR, diff --git a/packages/react-contexts/src/middlewares/refresh-session.ts b/packages/react-contexts/src/middlewares/refresh-session.ts index 3bbc02e171..d03216370f 100644 --- a/packages/react-contexts/src/middlewares/refresh-session.ts +++ b/packages/react-contexts/src/middlewares/refresh-session.ts @@ -5,17 +5,18 @@ import { NextRequest, NextResponse, } from 'next/server' -import { get, post } from '@titicaca/fetcher' +import { + get, + post, + handle401Error, + NEED_REFRESH_IDENTIFIER, +} from '@titicaca/fetcher' import { parseString, splitCookiesString } from 'set-cookie-parser' import { TP_SE, TP_TK, SESSION_KEY as X_SOTO_SESSION, } from '@titicaca/constants' -import { - handle401Error, - NEED_REFRESH_IDENTIFIER, -} from '@titicaca/fetcher/src/response-handler' import { parseApp } from '../user-agent-context' diff --git a/packages/review/src/data/graphql/client.ts b/packages/review/src/data/graphql/client.ts index ab38df9b0b..368f463956 100644 --- a/packages/review/src/data/graphql/client.ts +++ b/packages/review/src/data/graphql/client.ts @@ -1,6 +1,8 @@ import { ClientError, request } from 'graphql-request' -import { sessionRefresh } from '@titicaca/fetcher' -import { ACCESS_TOKEN_EXPIRED_EXCEPTION } from '@titicaca/fetcher/src/response-handler' +import { + sessionRefresh, + ACCESS_TOKEN_EXPIRED_EXCEPTION, +} from '@titicaca/fetcher' import { Requester, getSdk } from './generated' diff --git a/packages/standard-action-handler/package.json b/packages/standard-action-handler/package.json index e9bc2d057f..227ff19dfa 100644 --- a/packages/standard-action-handler/package.json +++ b/packages/standard-action-handler/package.json @@ -38,6 +38,7 @@ ] }, "dependencies": { + "@titicaca/fetcher": "workspace:*", "@titicaca/modals": "workspace:*", "@titicaca/router": "workspace:*", "@titicaca/scroll-to-element": "workspace:*", diff --git a/packages/standard-action-handler/src/converse.tsx b/packages/standard-action-handler/src/converse.tsx index 576368f572..ca9e4f78fa 100644 --- a/packages/standard-action-handler/src/converse.tsx +++ b/packages/standard-action-handler/src/converse.tsx @@ -1,8 +1,7 @@ import qs from 'qs' import { createRoot } from 'react-dom/client' import { Modal } from '@titicaca/modals' - -import { authGuardedFetchers, NEED_LOGIN_IDENTIFIER } from '../../fetcher/src' +import { authGuardedFetchers, NEED_LOGIN_IDENTIFIER } from '@titicaca/fetcher' import { ContextOptions, WebActionParams } from './types' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40c97e27c9..bb69cfabfe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1611,6 +1611,9 @@ importers: packages/standard-action-handler: dependencies: + '@titicaca/fetcher': + specifier: workspace:* + version: link:../fetcher '@titicaca/modals': specifier: workspace:* version: link:../modals From 54524e764be5923bfeabc80296546aa167b16706 Mon Sep 17 00:00:00 2001 From: guswl98 Date: Wed, 2 Apr 2025 17:21:58 +0900 Subject: [PATCH 7/7] =?UTF-8?q?401=EC=9D=B4=20=EC=95=84=EB=8B=8C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/fetcher/src/response-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fetcher/src/response-handler.ts b/packages/fetcher/src/response-handler.ts index 77bc6998c4..a7e2efc26f 100644 --- a/packages/fetcher/src/response-handler.ts +++ b/packages/fetcher/src/response-handler.ts @@ -33,7 +33,7 @@ type ResponseWithError = Pick & { export async function handle401Error( response: HttpResponse | Response, ) { - if (response.ok) { + if (response.ok || response.status !== 401) { return response }