Skip to content
Merged
13 changes: 13 additions & 0 deletions src/backend/auth-server/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ app.register(fastifySwaggerUi, {
app.register(currentAuthPlugin);
app.register(routes);

app.register(cors, {
origin: false,
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization', 'Refresh-Token'],
});

app
.register(fastifyRedis, {
client: redis,
Expand All @@ -151,6 +157,13 @@ app

app.register(fastifyCookie, {
secret: SECRET_KEY,
hook: 'onRequest',
parseOptions: {
secure: true,
httpOnly: true,
sameSite: 'none',
path: '/',
},
} as FastifyCookieOptions);

app.setErrorHandler((err, req, reply) => {
Expand Down
111 changes: 15 additions & 96 deletions src/backend/auth-server/src/controllers/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FastifyRequest, FastifyReply } from 'fastify';
import { generateHash } from '../../libs/authHelper';
import { generateHash, shortVerifyRefreshToken } from '../../libs/authHelper';
import { ERROR_MESSAGE, REDIS_KEY } from '../../libs/constants';
import { SUCCESS_MESSAGE } from '../../libs/constants';
import { handleError } from '../../libs/errorHelper';
Expand All @@ -23,16 +23,9 @@ function authController() {
return;
}

res.setCookie('refresh_token', values.refreshToken, {
sameSite: true,
httpOnly: true,
secure: true,
path: '/',
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
});

const result = {
accessToken: values.accessToken,
refreshToken: values.refreshToken,
};

await redis.set(REDIS_KEY.refreshToken(values.id), values.refreshToken);
Expand Down Expand Up @@ -67,33 +60,6 @@ function authController() {
}
};

const mobileLogin = async (req: FastifyRequest, res: FastifyReply) => {
const { email, password } = req.body as { email: string; password: string };

const values = await authService.loginWithPassword(email, password);

if (!values) {
handleError(res, ERROR_MESSAGE.notFound);
return;
}

const result = {
accessToken: values.accessToken,
refreshToken: values.refreshToken,
};

await redis.set(REDIS_KEY.refreshToken(values.id), values.refreshToken);

handleSuccess(
res,
{
...SUCCESS_MESSAGE.loginOk,
result,
},
200,
);
};

const register = async (
req: FastifyRequest<{
Body: { email: string; password: string; name: string; nickname: string; birthdate: string };
Expand Down Expand Up @@ -128,20 +94,15 @@ function authController() {

const logout = async (req: FastifyRequest, res: FastifyReply) => {
const id = req.user?.id;
const refreshToken = req.cookies.refresh_token;
const refreshToken = req.headers['refresh-token'] as string;

if (!id || !refreshToken) {
if (!id || !refreshToken || refreshToken === 'null' || refreshToken === '') {
handleError(res, ERROR_MESSAGE.unauthorized);
return;
}

try {
await redis.del(REDIS_KEY.refreshToken(id));

res.clearCookie('refresh_token', {
path: '/',
});

handleSuccess(res, SUCCESS_MESSAGE.logoutOk, 205);
} catch (error) {
handleError(res, ERROR_MESSAGE.badRequest, error);
Expand All @@ -150,7 +111,7 @@ function authController() {

const refresh = async (req: FastifyRequest, res: FastifyReply) => {
const id = req.user?.id;
const refreshToken = req.cookies.refresh_token;
const refreshToken = req.headers['refresh-token'] as string;

if (!refreshToken || !id) {
handleError(res, ERROR_MESSAGE.unauthorized);
Expand All @@ -167,65 +128,20 @@ function authController() {

const result = await authService.refresh(refreshToken, redisRefreshToken);

res.setCookie('refresh_token', result.refreshToken, {
sameSite: 'lax',
httpOnly: true,
secure: false,
path: '/',
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
});

await redis.set(REDIS_KEY.refreshToken(id), result.refreshToken);

handleSuccess(res, {
...SUCCESS_MESSAGE.refreshToken,
result: {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
},
});
} catch (error) {
handleError(res, ERROR_MESSAGE.unauthorized, error);
}
};

const refreshMobile = async (req: FastifyRequest, res: FastifyReply) => {
const id = req.user?.id;
const refreshToken = req.cookies.refresh_token;

if (!refreshToken || !id) {
handleError(res, ERROR_MESSAGE.unauthorized);
return;
}

try {
const redisRefreshToken = await redis.get(REDIS_KEY.refreshToken(id));

if (!redisRefreshToken) {
handleError(res, ERROR_MESSAGE.unauthorized);
return;
}

const result = await authService.refresh(refreshToken, redisRefreshToken);

res.setCookie('refresh_token', result.refreshToken, {
sameSite: 'lax',
httpOnly: true,
secure: false,
path: '/',
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
});

await redis.set(REDIS_KEY.refreshToken(id), result.refreshToken);

handleSuccess(res, {
...SUCCESS_MESSAGE.refreshToken,
result,
});
} catch (error) {
handleError(res, ERROR_MESSAGE.unauthorized, error);
}
};

const verifyToken = async (req: FastifyRequest, res: FastifyReply) => {
const accessToken = req.headers.authorization;
if (!accessToken) {
Expand Down Expand Up @@ -254,15 +170,20 @@ function authController() {
};

const loginStatusCheck = async (req: FastifyRequest, res: FastifyReply) => {
const id = req.user?.id;
const refreshToken = req.cookies.refresh_token;
const refreshToken = req.headers['refresh-token'] as string;

if (!id || !refreshToken) {
if (!refreshToken || refreshToken === 'null' || refreshToken === '') {
handleSuccess(res, SUCCESS_MESSAGE.loginStatusDisabled, 200);
return;
}

handleSuccess(res, SUCCESS_MESSAGE.loginStatusOK, 200);
try {
shortVerifyRefreshToken(refreshToken);
handleSuccess(res, SUCCESS_MESSAGE.loginStatusOK, 200);
} catch (error) {
handleSuccess(res, SUCCESS_MESSAGE.loginStatusDisabled, 200);
return;
}
};

return {
Expand All @@ -272,8 +193,6 @@ function authController() {
refresh,
verifyToken,
healthCheck,
mobileLogin,
refreshMobile,
loginStatusCheck,
};
}
Expand Down
17 changes: 1 addition & 16 deletions src/backend/auth-server/src/routes/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { ZodTypeProvider } from 'fastify-type-provider-zod';
import {
healthCheckSchema,
logoutSchema,
mobileSignInSchema,
refreshTokenMobileSchema,
refreshTokenSchema,
registerSchema,
signInSchema,
Expand All @@ -18,6 +16,7 @@ const authRoute = async (app: FastifyInstance) => {
app.withTypeProvider<ZodTypeProvider>().route({
method: 'GET',
url: '/status-check',
preHandler: [verifySignIn],
schema: loginStatusCheckSchema,
handler: authController.loginStatusCheck,
});
Expand All @@ -29,13 +28,6 @@ const authRoute = async (app: FastifyInstance) => {
handler: authController.login,
});

app.withTypeProvider<ZodTypeProvider>().route({
method: 'POST',
url: '/mobile-login',
schema: mobileSignInSchema,
handler: authController.mobileLogin,
});

app.withTypeProvider<ZodTypeProvider>().route({
method: 'POST',
url: '/register',
Expand All @@ -59,13 +51,6 @@ const authRoute = async (app: FastifyInstance) => {
handler: authController.refresh,
});

app.withTypeProvider<ZodTypeProvider>().route({
method: 'POST',
url: '/refresh-mobile',
schema: refreshTokenMobileSchema,
handler: authController.refreshMobile,
});

app.withTypeProvider<ZodTypeProvider>().route({
method: 'GET',
url: '/verify-token',
Expand Down
52 changes: 10 additions & 42 deletions src/backend/auth-server/src/schema/authSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,6 @@ const signInSchema = {
email: z.string().email(),
password: z.string(),
}),
response: {
200: z.object({
code: z.string().default('AUTH100'),
message: z.string().default('Login Ok!'),
result: z.object({
accessToken: z.string(),
}),
}),
400: commonResponseSchemaOmitResult,
},
};

const mobileSignInSchema = {
tags: ['auth'],
description: '모바일 로그인 합니다.',
body: z.object({
email: z.string().email(),
password: z.string(),
}),
response: {
200: z.object({
code: z.string().default('AUTH100'),
Expand Down Expand Up @@ -81,27 +62,9 @@ const registerSchema = {

const refreshTokenSchema = {
tags: ['auth'],
security: [{ bearerAuth: [] }],
response: {
201: z.object({
code: z.string().default('AUTH102'),
message: z.string().default('refresh success'),
result: z.object({
accessToken: z.string(),
}),
}),
400: commonResponseSchemaOmitResult,
},
description: `
리프레시 토큰은 쿠키('refresh_token')로 자동 처리됩니다.
Swagger UI에서 테스트하려면 브라우저 쿠키가 있어야 합니다.
1. 먼저 로그인하여 쿠키 설정
2. 이 엔드포인트 호출하여 새 액세스 토큰 발급
`,
};

const refreshTokenMobileSchema = {
tags: ['auth'],
headers: z.object({
'refresh-token': z.string(),
}),
security: [{ bearerAuth: [] }],
response: {
201: z.object({
Expand All @@ -125,6 +88,9 @@ const refreshTokenMobileSchema = {
const logoutSchema = {
tags: ['auth'],
description: '로그아웃 합니다.',
headers: z.object({
'refresh-token': z.string(),
}),
security: [{ bearerAuth: [] }],
response: {
205: z.object({
Expand Down Expand Up @@ -195,10 +161,14 @@ const healthCheckSchema = {
const loginStatusCheckSchema = {
tags: ['auth'],
description: '로그인 상태를 확인 합니다.',
security: [{ bearerAuth: [] }],
response: {
200: z.object({
code: z.string().default('AUTH109'),
message: z.string().default('login status check success!'),
result: z.object({
status: z.boolean(),
}),
}),
401: commonResponseSchema,
},
Expand All @@ -207,11 +177,9 @@ const loginStatusCheckSchema = {
export {
logoutSchema,
refreshTokenSchema,
refreshTokenMobileSchema,
verifyTokenSchema,
registerSchema,
signInSchema,
mobileSignInSchema,
verifyEmailSchema,
tokenDecodeSchema,
healthCheckSchema,
Expand Down