-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
117 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,9 @@ | ||
import React, { PropsWithChildren } from 'react' | ||
import { userInfoService } from '@vook-client/api' | ||
import { cookies } from 'next/headers' | ||
import { redirect } from 'next/navigation' | ||
import { UserStatus } from 'node_modules/@vook-client/api/src/services/useUserInfoQuery/model' | ||
|
||
import { loginLayout } from './layout.css' | ||
|
||
const Layout = async ({ children }: PropsWithChildren) => { | ||
const cookieStore = cookies() | ||
|
||
const accessToken = cookieStore.get('access')?.value | ||
const refreshToken = cookieStore.get('access')?.value | ||
|
||
if (!accessToken || !refreshToken) { | ||
return <div className={loginLayout}>{children}</div> | ||
} | ||
|
||
const userInfo = await userInfoService.getUserInfo({ | ||
access: accessToken || '', | ||
refresh: refreshToken || '', | ||
}) | ||
|
||
if (userInfo.result.status === UserStatus.SocialLoginCompleted) { | ||
redirect('/signup') | ||
} | ||
|
||
return null | ||
const Layout = ({ children }: PropsWithChildren) => { | ||
return <div className={loginLayout}>{children}</div> | ||
} | ||
|
||
export default Layout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,85 @@ | ||
import { NextRequest, NextResponse } from 'next/server' | ||
import { | ||
UserInfoResponse, | ||
UserStatus, | ||
} from 'node_modules/@vook-client/api/src/services/useUserInfoQuery/model' | ||
|
||
const beforeLoginMatcher = (pathname: string) => { | ||
const beforeLoginPaths = ['/login', '/signup'] | ||
return beforeLoginPaths.some((path) => pathname.includes(path)) | ||
} | ||
import { UserInfoResponse, UserStatus } from 'node_modules/@vook-client/api/' | ||
|
||
const beforeLoginMiddleware = async (req: NextRequest) => { | ||
const accessToken = req.cookies.get('access') | ||
const refreshToken = req.cookies.get('refresh') | ||
/** | ||
* NOTE | ||
* 1. 액세스 토큰과 리프레시 토큰이 모두 없는 경우 => 로그인 페이지로 리다이렉트 | ||
* 2. 액세스 토큰만 있는 경우 => 로그인 페이지로 리다이렉트 | ||
* 3. 리프레시 토큰만 있는 경우 => 리프레시 토큰으로 액세스 토큰을 갱신 | ||
* 4. 액세스 토큰과 리프레시 토큰이 모두 있는 경우 => 액세스 토큰이 유효한지 확인 | ||
*/ | ||
|
||
// 토큰이 모두 없는 경우 | ||
if (!accessToken && !refreshToken) { | ||
return | ||
} | ||
const onlyRegisteredMatch = ['/'] | ||
|
||
if (refreshToken) { | ||
// TODO: 리프레시 토큰을 이용해 엑세스 토큰 재발급 | ||
} | ||
const isOnlyRegistered = async ( | ||
req: NextRequest, | ||
finalResponse: NextResponse, | ||
) => { | ||
const accessToken = req.cookies.get('access')?.value | ||
const refreshToken = req.cookies.get('refresh')?.value | ||
|
||
if ((accessToken && !refreshToken) || !accessToken || !refreshToken) { | ||
return | ||
// 1 & 2 | ||
if ((!accessToken && !refreshToken) || (accessToken && !refreshToken)) { | ||
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_DOMAIN}/login`) | ||
} | ||
|
||
try { | ||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/user/info`, { | ||
// 3 | ||
if (!accessToken && refreshToken) { | ||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/refresh`, { | ||
headers: { | ||
Authorization: accessToken.value, | ||
'Content-Type': 'application/json', | ||
Accept: 'application/json', | ||
'X-Refresh-Authorization': refreshToken, | ||
}, | ||
}) | ||
|
||
if (res.status === 401) { | ||
if ( | ||
res.ok && | ||
res.headers.get('Authorization') && | ||
res.headers.get('X-Refresh-Authorization') | ||
) { | ||
finalResponse.cookies.set('access', res.headers.get('Authorization')!) | ||
finalResponse.cookies.set( | ||
'refresh', | ||
res.headers.get('X-Refresh-Authorization')!, | ||
) | ||
} else { | ||
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_DOMAIN}/login`) | ||
} | ||
} | ||
|
||
if (res.ok && res.status === 200) { | ||
const userInfo = (await res.json()) as UserInfoResponse | ||
const userInfoRes = await fetch( | ||
`${process.env.NEXT_PUBLIC_API_URL}/user/info`, | ||
{ | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Accept: 'application/json', | ||
Authorization: | ||
finalResponse.cookies.get('access')?.value || accessToken || '', | ||
}, | ||
}, | ||
) | ||
|
||
if (userInfoRes.ok) { | ||
const userInfo = (await userInfoRes.json()) as UserInfoResponse | ||
|
||
if ( | ||
[UserStatus.SocialLoginCompleted, UserStatus.Registered].includes( | ||
userInfo.result.status, | ||
) | ||
) { | ||
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_DOMAIN}/`) | ||
} | ||
// 회원가입이 완료되지 않은 경우 | ||
if (userInfo.result.status === UserStatus.SocialLoginCompleted) { | ||
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_DOMAIN}/login`) | ||
} else { | ||
return finalResponse | ||
} | ||
} catch { | ||
} else { | ||
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_DOMAIN}/login`) | ||
} | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/require-await | ||
export async function middleware(req: NextRequest) { | ||
if (beforeLoginMatcher(req.nextUrl.pathname)) { | ||
return beforeLoginMiddleware(req) | ||
const response = NextResponse.next() | ||
|
||
if (onlyRegisteredMatch.includes(req.nextUrl.pathname)) { | ||
await isOnlyRegistered(req, response) | ||
} | ||
|
||
return response | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,33 @@ | ||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query' | ||
|
||
import { CustomQueryOptions } from '../../shared/type' | ||
import { CustomQueryOptions, Tokens } from '../../shared/type' | ||
|
||
import { userInfoService } from './userInfoService' | ||
import { UserInfoResponse } from './model' | ||
|
||
export const userInfoQueryOptions = { | ||
getUserInfo: () => ({ | ||
getUserInfo: (token: Tokens) => ({ | ||
queryKey: [], | ||
queryFn: () => userInfoService.getUserInfo(), | ||
queryFn: () => userInfoService.getUserInfo(token), | ||
}), | ||
} | ||
|
||
export const useUserInfoQuery = ( | ||
token: Tokens, | ||
queryOptions: CustomQueryOptions<UserInfoResponse> = {}, | ||
) => { | ||
return useQuery<UserInfoResponse>({ | ||
...userInfoQueryOptions.getUserInfo(), | ||
...userInfoQueryOptions.getUserInfo(token), | ||
...queryOptions, | ||
}) | ||
} | ||
|
||
export const useUserInfoSuspenseQuery = ( | ||
token: Tokens, | ||
queryOptions: CustomQueryOptions<UserInfoResponse> = {}, | ||
) => { | ||
return useSuspenseQuery<UserInfoResponse>({ | ||
...userInfoQueryOptions.getUserInfo(), | ||
...userInfoQueryOptions.getUserInfo(token), | ||
...queryOptions, | ||
}) | ||
} |
17 changes: 3 additions & 14 deletions
17
packages/api/src/services/useUserInfoQuery/userInfoService.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters