Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

로그인 시 cookie에 role저장, middleware에서 접근제어 #73

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/constants/cookie.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const COOKIE_KEY = {
ACCESS_TOKEN: 'depmat',
REFRESH_TOKEN: 'depmrt',
ROLE: 'depmrole',
};
1 change: 0 additions & 1 deletion src/features/admin/attendance/UserItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ function UserItem(props: AttendanceItemType) {
const onChange = (value: ATTENDANCE_STATUS) => {
// optimistic update
setStatus(value);
console.log('value: ', value);

// api call
mutate({ attendanceId: props.attendanceId, attendanceStatus: value });
Expand Down
6 changes: 3 additions & 3 deletions src/features/login/CertifyStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useState } from 'react';
import { useRouter } from 'next/router';

import { usePostLogin } from '@/hooks/apis/auth/usePostLogin';
import { getUserRoleByToken } from '@/hooks/apis/user/useGetInfo';

import LoginLayout from './LoginLayout';
import PasswordInput from './PasswordInput';
Expand All @@ -22,8 +21,9 @@ function CertifyStep(props: Props) {
const isDisabled = value.length !== PASSWORD_LENGTH || Boolean(error);

const { mutate } = usePostLogin({
onSuccess: async ({ accessToken }) => {
const role = await getUserRoleByToken(accessToken);
onSuccess: async (data) => {
const role = data.member.generations[0].role;

if (role === 'ORGANIZER') {
router.replace('/admin/attendance');
} else {
Expand Down
8 changes: 4 additions & 4 deletions src/features/login/JoinCompleteStep.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import Image from 'next/image';
import { useRouter } from 'next/router';
import Cookies from 'js-cookie';
import styled from 'styled-components';

import { useGetInfo } from '@/hooks/apis/user/useGetInfo';

import LoginLayout from './LoginLayout';
import Img from './welcome-2.png';

function JoinCompleteStep() {
const router = useRouter();
const { data } = useGetInfo();

const onNext = () => {
if (data?.generations[0].role === 'ORGANIZER') {
const roleData = Cookies.get('role');

if (roleData?.includes('ORGANIZER')) {
router.replace('/admin/attendance');
} else {
router.replace('/');
Expand Down
5 changes: 5 additions & 0 deletions src/hooks/apis/auth/usePostLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Cookies from 'js-cookie';
import type { CustomError } from '@/apis';
import { api } from '@/apis';
import { COOKIE_KEY } from '@/constants/cookie';
import type { UserInfo } from '@/hooks/apis/user/user';

interface PostLoginRequest {
email: string;
Expand All @@ -14,6 +15,7 @@ interface PostLoginRequest {
interface PostLoginResponse {
accessToken: string;
refreshToken: string;
member: UserInfo;
}

const postLogin = async (request: PostLoginRequest): Promise<PostLoginResponse> => {
Expand All @@ -27,6 +29,9 @@ export const usePostLogin = (options?: UseMutationOptions<PostLoginResponse, Cus
onSuccess: async (data, ...rest) => {
Cookies.set(COOKIE_KEY.ACCESS_TOKEN, data.accessToken, { expires: 1 });
Cookies.set(COOKIE_KEY.REFRESH_TOKEN, data.refreshToken, { expires: 7 });
Cookies.set(COOKIE_KEY.ROLE, `${data.member.generations[0].generationId}-${data.member.generations[0].role}`, {
expires: 1,
});

options?.onSuccess?.({ ...data }, ...rest);
},
Expand Down
20 changes: 6 additions & 14 deletions src/hooks/apis/user/useGetInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,13 @@ import { useQuery } from '@tanstack/react-query';
import type { CustomError } from '@/apis';
import { api } from '@/apis';

type Role = 'ORGANIZER' | 'MEMBER';

interface GetInfoResponse {
id: string;
name: string;
email: string;
generations: {
generationId: number;
role: Role;
position: string;
}[];
}
const GET_INFO_URL = '/v1/me';

import type { Role, UserInfo } from './user';

type GetInfoResponse = UserInfo;
export const getInfoByToken = async (token: string): Promise<GetInfoResponse> => {
return await api.get<GetInfoResponse>('/v1/me', {
return await api.get<GetInfoResponse>(GET_INFO_URL, {
headers: {
Authorization: `Bearer ${token}`,
},
Expand All @@ -31,7 +23,7 @@ export const getUserRoleByToken = async (token: string): Promise<Role> => {
return generations[0].role;
};

const getInfo = () => api.get<GetInfoResponse>('/v1/me');
export const getInfo = () => api.get<GetInfoResponse>(GET_INFO_URL);

export const useGetInfo = (options?: UseQueryOptions<GetInfoResponse, CustomError>) =>
useQuery({
Expand Down
12 changes: 12 additions & 0 deletions src/hooks/apis/user/user.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type Role = 'ORGANIZER' | 'MEMBER';

export interface UserInfo {
id: string;
name: string;
email: string;
generations: {
generationId: number;
role: Role;
position: string;
}[];
}
7 changes: 7 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export async function middleware(request: NextRequest) {
}
}

if (request.nextUrl.pathname.startsWith('/admin')) {
const role = request.cookies.get(COOKIE_KEY.ROLE)?.value;
if (!role?.includes('ORGANIZER')) {
return NextResponse.redirect(new URL('/', request.url));
}
}

return response;
}

Expand Down
69 changes: 0 additions & 69 deletions src/pages/admin/attendance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import styled from 'styled-components';
import { BottomNav } from '@/components/BottomNav';
import IconButton from '@/components/Button/IconButton';
import Layout from '@/components/Layout';
import { ATTENDANCE_STATUS } from '@/constants/attendance';
import { ADMIN_NAV_ITEMS } from '@/constants/bottomNav';
import TeamSelect from '@/features/admin/attendance/TeamSelect';
import UserItem from '@/features/admin/attendance/UserItem';
Expand Down Expand Up @@ -125,71 +124,3 @@ const UserSection = styled.section`
border-top: 1px solid ${({ theme }) => theme.color.gray_200};
}
`;

const DUMMY_DATA: {
id: number;
name: string;
position: string;
status: ATTENDANCE_STATUS;
}[] = [
{
id: 1,
name: '김민수',
position: '개발자',
status: ATTENDANCE_STATUS.출석대기,
},
{
id: 2,
name: '홍길동',
position: '디자이너',
status: ATTENDANCE_STATUS.지각,
},
{
id: 3,
name: '이영희',
position: '디자이너',
status: ATTENDANCE_STATUS.출석,
},
{
id: 4,
name: '박철수',
position: '디자이너',
status: ATTENDANCE_STATUS.결석,
},
{
id: 5,
name: '김지영',
position: '디자이너',
status: ATTENDANCE_STATUS.출석대기,
},
{
id: 6,
name: '이승호',
position: '디자이너',
status: ATTENDANCE_STATUS.출석대기,
},
{
id: 7,
name: '박지민',
position: '디자이너',
status: ATTENDANCE_STATUS.출석대기,
},
{
id: 8,
name: '박지민',
position: '디자이너',
status: ATTENDANCE_STATUS.출석대기,
},
{
id: 9,
name: '박지민',
position: '디자이너',
status: ATTENDANCE_STATUS.출석대기,
},
{
id: 10,
name: '박지민',
position: '디자이너',
status: ATTENDANCE_STATUS.출석대기,
},
];
1 change: 0 additions & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const Home = () => {

// NOTE: 유저 정보 가져오기
const { data } = useGetInfo();
console.log('data: ', data);

const handleClickCheckIn = () => {
mutate();
Expand Down