issue #39
All checks were successful
Test CI / build (push) Successful in 1m23s

- 자동 로그인 로직 cookie 로 변경
This commit is contained in:
2025-12-07 22:46:01 +09:00
parent 91e4f987ea
commit abee778691
13 changed files with 239 additions and 70 deletions

28
certs/localhost+2-key.pem Normal file
View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1GqPn9O+FRM+a
hQvEAjgjI7HXdRdeRJsX3K2BqNGYBR8Lat4fMskY4Es7WYNCDl9d/fFQd+K/gpjj
sRnX0Bf3CYjVNlhTqdwoOFBPGUQ4i8fuSRM4rDvAq4enb+7RVWjE53MGgqQ0RdRQ
Hyx95pEWTz0FzpNlzOzEGEdv8zBwbwgZBU73F4aCDNY0FLaaKepr1/NqdUA8xZYo
sAbJvIkWJ/QS2F2/WwQZYQ3TLWtg4/2uDpGWbpQUwRnWZ7ma+Gcz/hJwQhoml4q4
bHXrIAsCz/NFNYs7K5wCmIdTjLw2PcETJSEQMbGSk7CMIuiUd0ShVDfpBROIAlmF
XAheZ/lNAgMBAAECggEAT1H7t/xva89XnjXnkVHnhHx9yABg28jwpOLim4d1RT/4
+Oc1ojR8H4kdakEqXCQvYNt4deYMShTJIfDPgNaDqI9kfv3ucbZT1snTYtGOL7YJ
OzSGVqwY/6ohIBTGZKkj2hoFJzTQ9pQfCXid5Aa4RS0vbPutU0kN6lU39LBu5s79
8MGGu70Qcd63BRpyOxKbbWCbZ0S/7JRShng0GA8ILBvMCZdzZ7RZktpa+bA7Q6i7
PM5zxxmxygmFQXOAizTB2KoeLEu/qb44kypK6aw9nMKEZebqzuF5bOFMlCR67Cfx
QblFW0JQckef7DCc4ThPEnAXwEPSlsv2P//dbX1TgQKBgQDctIA/GZmzSXVtsUws
aPPeAvKMWGFSGsq/9rcy2G7KBTHBQW5/763T6N7HKVpZwRaabiSxgrZekd6d9oTP
XWpmnFQZgtLRtXLPXilCK8Udoi6HejGuXWvviFdfPUqhPh3tw6uCaFO7qT4pUiwO
hTq4W5Cm+xy/q12m7cEDfoPeMwKBgQDSEOdQY5f5C6YrZvG2zrTZUvS8VW4NFhkY
vSZBGqiACYGKq/7erMoAdtLrcYBnEMiLufn+tSInflw2I+ALEo4r2lcBAQncXuWj
gnpM+DpTwRfZmGYs3jXA5eVRRzG0FBFLFyiBE62f0tvJjALB7V7vfpCswJGvPIyb
p2P16jZKfwKBgQCn4VkoJlYGzZrYTKPvqAnQV5ed7+BfbufIq2dg8scbPmZBZX8j
K/KinaFQB4Glgj2qTJv2tsH4H6chqxINFjbIRKOoIB4yzH2/hRWHMvomd2ZDQUyn
IILo2mHznRC2pCRp5owAj1EaDzusfMfsZ6Vp9KSMj7inhzeesX0/Ji4yhwKBgHgh
sJc5jYSgU9RIV/0qcyRBm7JEzN3xAEM0kLb0rt4iEZIjUGs5t3/SdEavLzZB096M
adpu7exWCBfyJkNOxj1v7Qem92OuZXc/u/9eicSyDZij3fLU1TrOfnkf1N3eCBHA
WaqPfWCELqsxRbZvsDYYVFZm/imP3/14GeNdoNSzAoGALFD3YdZ5bIETcD8hwXd7
P9HXPQZag919fFe0dhPDauC5dgvgfLF2d88q0MPCqMK6ngTMVzrMh4X/u0XH1N3v
LiQEBp4P0EX1z5DEj8eLajV0JgOyt1zzymZ3Jk7DDyfcYl+bVetB2R9Gd3MuV98t
Sz72wO/jh1/w+SN07F9VS0w=
-----END PRIVATE KEY-----

26
certs/localhost+2.pem Normal file
View File

@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIETjCCAragAwIBAgIQJ0x1ttig1msjzZOJWJwnITANBgkqhkiG9w0BAQsFADB7
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKDAmBgNVBAsMH+uwse2W
peuLqFxiYWVreWFuZ2RhbkDrsLHtlqXri6gxLzAtBgNVBAMMJm1rY2VydCDrsLHt
lqXri6hcYmFla3lhbmdkYW5A67Cx7Zal64uoMB4XDTI1MTIwNzEwMDIyNVoXDTI4
MDMwNzEwMDIyNVowUzEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRp
ZmljYXRlMSgwJgYDVQQLDB/rsLHtlqXri6hcYmFla3lhbmdkYW5A67Cx7Zal64uo
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtRqj5/TvhUTPmoULxAI4
IyOx13UXXkSbF9ytgajRmAUfC2reHzLJGOBLO1mDQg5fXf3xUHfiv4KY47EZ19AX
9wmI1TZYU6ncKDhQTxlEOIvH7kkTOKw7wKuHp2/u0VVoxOdzBoKkNEXUUB8sfeaR
Fk89Bc6TZczsxBhHb/MwcG8IGQVO9xeGggzWNBS2minqa9fzanVAPMWWKLAGybyJ
Fif0Ethdv1sEGWEN0y1rYOP9rg6Rlm6UFMEZ1me5mvhnM/4ScEIaJpeKuGx16yAL
As/zRTWLOyucApiHU4y8Nj3BEyUhEDGxkpOwjCLolHdEoVQ36QUTiAJZhVwIXmf5
TQIDAQABo3YwdDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
HwYDVR0jBBgwFoAU9/lqZ9lo2f1oLg+JnBYubfpE4OEwLAYDVR0RBCUwI4IJbG9j
YWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IB
gQBNsZYSSGE6m3ve8bPGISSdlSU/pi1GVqOC4xxVD8JUeGNZeYAv8AJQ6w/496wK
KTFL6PDOavHW37mWBEgz+fZe2AjOZK/hz/eOcKHTFhVRZo1snt5VuLk4PtAGmgn8
xyyQUz/2wwlTqb0AgLrt1hLTnbSIWvBnFl3VdCnH0E2xsIPZUMFzcjVHTERJWvAS
IanurnpeO/W3uNduu7UmGk03GDzTG8dXwVsSSE/HoXxscXSIP9qMvKOPeQvucd+X
XkaYUkbjDhwoKqDB0rDmvbPkFcsAGuq8qpbPPavhoXgtdqO30lZfbTPWPq1qz99S
nW9ihMfmwarw5s/LCXiQO70nMbcZMZ6UAqEhX4UUfGD0j5jHe3fPOyT4v0lLfmxp
P4eoSiWoIfp/f9ZBn9zca5km9iGNT+n1Jrt6fOTIycPVu2gE7S4qXcHhVHWondle
bMFLGbjZ75Qwc9HdnoY7Do8Vj+CPvSfhAAPehPf8D1GVlazmiuHql4fny1qbGw//
YEQ=
-----END CERTIFICATE-----

View File

@@ -9,5 +9,13 @@ export default defineConfig({
out: "./drizzle", out: "./drizzle",
dbCredentials: { dbCredentials: {
url: process.env.PG_DATABASE_URL! url: process.env.PG_DATABASE_URL!
} },
tablesFilter: [
'account',
'schedule',
'comment',
'follow',
'favorite',
'participant'
]
}); });

View File

@@ -18,8 +18,8 @@ export const commentRelations = relations(comment, ({one, many}) => ({
export const accountRelations = relations(account, ({many}) => ({ export const accountRelations = relations(account, ({many}) => ({
comments: many(comment), comments: many(comment),
schedules: many(schedule),
participants: many(participant), participants: many(participant),
schedules: many(schedule),
favorites: many(favorite), favorites: many(favorite),
follows_follower: many(follow, { follows_follower: many(follow, {
relationName: "follow_follower_account_id" relationName: "follow_follower_account_id"
@@ -29,15 +29,6 @@ export const accountRelations = relations(account, ({many}) => ({
}), }),
})); }));
export const scheduleRelations = relations(schedule, ({one, many}) => ({
account: one(account, {
fields: [schedule.owner],
references: [account.id]
}),
participants: many(participant),
favorites: many(favorite),
}));
export const participantRelations = relations(participant, ({one}) => ({ export const participantRelations = relations(participant, ({one}) => ({
schedule: one(schedule, { schedule: one(schedule, {
fields: [participant.scheduleId], fields: [participant.scheduleId],
@@ -49,6 +40,15 @@ export const participantRelations = relations(participant, ({one}) => ({
}), }),
})); }));
export const scheduleRelations = relations(schedule, ({one, many}) => ({
participants: many(participant),
account: one(account, {
fields: [schedule.owner],
references: [account.id]
}),
favorites: many(favorite),
}));
export const favoriteRelations = relations(favorite, ({one}) => ({ export const favoriteRelations = relations(favorite, ({one}) => ({
schedule: one(schedule, { schedule: one(schedule, {
fields: [favorite.scheduleId], fields: [favorite.scheduleId],

View File

@@ -1,7 +1,23 @@
import { pgTable, foreignKey, uuid, text, date, boolean, index, varchar, primaryKey } from "drizzle-orm/pg-core" import { pgTable, foreignKey, uuid, text, date, boolean, varchar, index, time, primaryKey, pgSequence } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm" import { sql } from "drizzle-orm"
export const versionIdSeq = pgSequence("version_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const accessTokenIdSeq = pgSequence("access_token_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const oauth2ApplicationIdSeq = pgSequence("oauth2_application_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const oauth2AuthorizationCodeIdSeq = pgSequence("oauth2_authorization_code_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const oauth2GrantIdSeq = pgSequence("oauth2_grant_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const loginSourceIdSeq = pgSequence("login_source_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const twoFactorIdSeq = pgSequence("two_factor_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const webauthnCredentialIdSeq = pgSequence("webauthn_credential_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const dbfsMetaIdSeq = pgSequence("dbfs_meta_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const dbfsDataIdSeq = pgSequence("dbfs_data_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const noticeIdSeq = pgSequence("notice_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const systemSettingIdSeq = pgSequence("system_setting_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const badgeIdSeq = pgSequence("badge_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const userBadgeIdSeq = pgSequence("user_badge_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const userBlockingIdSeq = pgSequence("user_blocking_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const emailAddressIdSeq = pgSequence("email_address_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const comment = pgTable("comment", { export const comment = pgTable("comment", {
id: uuid().primaryKey().notNull(), id: uuid().primaryKey().notNull(),
@@ -23,30 +39,6 @@ export const comment = pgTable("comment", {
}), }),
]); ]);
export const schedule = pgTable("schedule", {
id: uuid().primaryKey().notNull(),
name: varchar(),
startAt: date("start_at"),
endAt: date("end_at"),
status: varchar(),
content: text(),
isDeleted: boolean("is_deleted").default(false),
type: varchar(),
createdAt: date("created_at"),
owner: uuid(),
}, (table) => [
index("schedule_enddatetime_idx").using("btree", table.endAt.asc().nullsLast().op("date_ops")),
index("schedule_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops"), table.content.asc().nullsLast().op("text_ops")),
index("schedule_startdatetime_idx").using("btree", table.startAt.asc().nullsLast().op("date_ops")),
index("schedule_status_idx").using("btree", table.status.asc().nullsLast().op("text_ops")),
index("schedule_type_idx").using("btree", table.type.asc().nullsLast().op("text_ops")),
foreignKey({
columns: [table.owner],
foreignColumns: [account.id],
name: "schedule_user_fk"
}),
]);
export const account = pgTable("account", { export const account = pgTable("account", {
name: varchar().notNull(), name: varchar().notNull(),
email: varchar().notNull(), email: varchar().notNull(),
@@ -79,6 +71,33 @@ export const participant = pgTable("participant", {
}), }),
]); ]);
export const schedule = pgTable("schedule", {
id: uuid().primaryKey().notNull(),
name: varchar(),
startDate: date("start_date"),
endDate: date("end_date"),
status: varchar(),
content: text(),
isDeleted: boolean("is_deleted").default(false),
type: varchar(),
createdAt: date("created_at"),
owner: uuid(),
style: varchar(),
startTime: time("start_time"),
endTime: time("end_time"),
}, (table) => [
index("schedule_enddatetime_idx").using("btree", table.endDate.asc().nullsLast().op("date_ops")),
index("schedule_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops"), table.content.asc().nullsLast().op("text_ops")),
index("schedule_startdatetime_idx").using("btree", table.startDate.asc().nullsLast().op("date_ops")),
index("schedule_status_idx").using("btree", table.status.asc().nullsLast().op("text_ops")),
index("schedule_type_idx").using("btree", table.type.asc().nullsLast().op("text_ops")),
foreignKey({
columns: [table.owner],
foreignColumns: [account.id],
name: "schedule_user_fk"
}),
]);
export const favorite = pgTable("favorite", { export const favorite = pgTable("favorite", {
isDeleted: boolean("is_deleted").default(false), isDeleted: boolean("is_deleted").default(false),
createdAt: date("created_at"), createdAt: date("created_at"),

View File

@@ -20,9 +20,13 @@
"test:watch": "jest --watch", "test:watch": "jest --watch",
"test:cov": "jest --coverage", "test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json",
"drizzle-pull:local": "dotenv -e .env.local -- drizzle-kit pull",
"drizzle-pull:dev": "dotenv -e .env.dev -- drizzle-kit pull",
"drizzle-pull:prod": "dotenv -e .env.prod -- drizzle-kit pull"
}, },
"dependencies": { "dependencies": {
"@fastify/cookie": "^11.0.2",
"@nestjs/class-transformer": "^0.4.0", "@nestjs/class-transformer": "^0.4.0",
"@nestjs/class-validator": "^0.13.4", "@nestjs/class-validator": "^0.13.4",
"@nestjs/common": "^11.0.1", "@nestjs/common": "^11.0.1",
@@ -64,6 +68,7 @@
"@types/pg": "^8.15.6", "@types/pg": "^8.15.6",
"@types/supertest": "^6.0.2", "@types/supertest": "^6.0.2",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"dotenv-cli": "^11.0.0",
"eslint": "^9.18.0", "eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1", "eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2", "eslint-plugin-prettier": "^5.2.2",

View File

@@ -5,11 +5,23 @@ import {
NestFastifyApplication NestFastifyApplication
} from '@nestjs/platform-fastify'; } from '@nestjs/platform-fastify';
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter'; import { AllExceptionsFilter } from './common/filters/all-exceptions.filter';
import fastifyCookie from '@fastify/cookie';
import * as path from 'path';
import * as fs from 'fs';
async function bootstrap() { async function bootstrap() {
const isProd = process.env.NODE_ENV === 'prod';
let httpsOptions = {};
if (!isProd) {
const certPath = path.join(__dirname, "..\\..", "certs");
httpsOptions = {
key: fs.readFileSync(path.join(certPath, 'localhost+2-key.pem')),
cert: fs.readFileSync(path.join(certPath, 'localhost+2.pem'))
};
}
const app = await NestFactory.create<NestFastifyApplication>( const app = await NestFactory.create<NestFastifyApplication>(
AppModule, AppModule,
new FastifyAdapter() new FastifyAdapter({ https: httpsOptions })
); );
app.enableCors({ app.enableCors({
origin: (origin, callback) => { origin: (origin, callback) => {
@@ -30,6 +42,9 @@ async function bootstrap() {
app.enableShutdownHooks(); app.enableShutdownHooks();
app.useGlobalFilters(new AllExceptionsFilter()); app.useGlobalFilters(new AllExceptionsFilter());
app.register(fastifyCookie, {
secret: process.env.JWT_SECRET
});
await app.listen(process.env.PORT ?? 3000, '0.0.0.0', () => { process.env.NODE_ENV !== 'prod' && console.log(`servier is running on ${process.env.PORT}`) }); await app.listen(process.env.PORT ?? 3000, '0.0.0.0', () => { process.env.NODE_ENV !== 'prod' && console.log(`servier is running on ${process.env.PORT}`) });
} }

View File

@@ -5,19 +5,22 @@ import { JwtService } from '@nestjs/jwt';
export class AuthService { export class AuthService {
constructor(private readonly jwtService: JwtService) {} constructor(private readonly jwtService: JwtService) {}
generateTokens(payload: any) { generateTokens(id: string) {
const accessToken = this.jwtService.sign(payload, { expiresIn: '5s' }); const accessToken = this.jwtService.sign({id: id}, { expiresIn: '5s' });
const refreshToken = this.jwtService.sign({id: payload.id}, { expiresIn: '7d' }); const refreshToken = this.jwtService.sign({id: id}, { expiresIn: '7d' });
return { accessToken, refreshToken }; return { accessToken, refreshToken };
} }
refreshTokens(refreshToken: string) { refreshTokens(id: string) {
try { try {
const payload = this.jwtService.verify(refreshToken); return this.generateTokens(id);
return this.generateTokens(payload);
} catch (e) { } catch (e) {
throw new UnauthorizedException('Invalid Refresh Token'); throw new UnauthorizedException('Invalid Refresh Token');
} }
} }
validateToken(token: string) {
return this.jwtService.verify(token);
}
} }

View File

@@ -1,26 +1,33 @@
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport'; import { PassportStrategy } from '@nestjs/passport';
import { FastifyRequest } from 'fastify';
import { ExtractJwt, Strategy } from 'passport-jwt'; import { ExtractJwt, Strategy } from 'passport-jwt';
const extractJwtFromCookie = (req: FastifyRequest | any): string | null => {
if (req.cookies && req.cookies['refresh_token']) {
return req.cookies['refresh_token'];
}
return null;
}
@Injectable() @Injectable()
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh-token') { export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh-token') {
constructor(configService: ConfigService) { constructor(configService: ConfigService) {
super({ super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromExtractors([extractJwtFromCookie]),
secretOrKey: configService.get<string>('JWT_SECRET')!, secretOrKey: configService.get<string>('JWT_SECRET')!,
passReqToCallback: true passReqToCallback: true
}); });
} }
async validate(payload: any) { async validate(req: FastifyRequest, payload: any) {
const token = ExtractJwt.fromAuthHeaderAsBearerToken(); const refreshToken = req.cookies['refresh_token'];
if (!refreshToken) throw new UnauthorizedException('Invalid Refresh Token');
if (!token) throw new UnauthorizedException('Invalid Refresh Token');
return { return {
id: payload.id, id: payload.id,
token refreshToken
}; };
} }
} }

View File

@@ -1,9 +1,10 @@
import { Body, Controller, Get, Headers, Post, Query, Req, UseGuards } from "@nestjs/common"; import { Body, Controller, Get, Headers, Post, Query, Req, Res, UseGuards } from "@nestjs/common";
import { AccountService } from "./account.service"; import { AccountService } from "./account.service";
import * as DTO from "./dto"; import * as DTO from "./dto";
import { JwtAccessAuthGuard } from "src/middleware/auth/guard/access-token.guard";
import { Public } from "src/common/decorators/public.decorator"; import { Public } from "src/common/decorators/public.decorator";
import { JwtRefreshAuthGuard } from "src/middleware/auth/guard/refresh-token.guard"; import type { FastifyReply, FastifyRequest } from "fastify";
import { AuthGuard } from "@nestjs/passport";
import { JwtAccessAuthGuard } from "src/middleware/auth/guard/access-token.guard";
@UseGuards(JwtAccessAuthGuard) @UseGuards(JwtAccessAuthGuard)
@Controller('account') @Controller('account')
@@ -65,18 +66,44 @@ export class AccountController {
@Public() @Public()
@Post('login') @Post('login')
async login(@Body() body: DTO.LoginRequest): Promise<DTO.LoginResponse> { async login(@Body() body: DTO.LoginRequest, @Res({ passthrough: true }) res: FastifyReply): Promise<DTO.LoginResponse> {
console.log('a');
const result = await this.accountService.login(body); const result = await this.accountService.login(body);
return result; if (result.success) {
res.setCookie('refresh_token', result.refreshToken!, {
httpOnly: true,
path: '/',
secure: true,
maxAge: 7 * 24 * 60 * 60 * 1000
});
}
return {
success: result.success,
message: result.message,
error: result.error,
accessToken: result.accessToken
};
} }
@Public() @Public()
@UseGuards(JwtRefreshAuthGuard) @UseGuards(AuthGuard('refresh-token'))
@Get('refresh-access-token') @Get('refresh-access-token')
async refreshAccessToken(@Req() req): Promise<DTO.RefreshAccessTokenResponse> { async refreshAccessToken(@Req() req, @Res({ passthrough: true }) res: FastifyReply): Promise<DTO.RefreshAccessTokenResponse> {
const id = req.user.id; const result = await this.accountService.refreshAccessToken(req.user.id);
const newAccessToken = this.accountService.refreshAccessToken(id); if (result.success) {
return newAccessToken; res.setCookie('refresh_token', result.refreshToken!, {
httpOnly: true,
path: '/',
secure: true,
maxAge: 7 * 24 * 60 * 60 * 1000
});
return {
success: result.success,
message: result.message,
error: result.error,
accessToken: result.accessToken
}
}
return result;
} }
} }

View File

@@ -95,13 +95,9 @@ export class AccountService {
} }
{ {
const { id, accountId, status, isDeleted, birthday } = queryResult[0]; const { id } = queryResult[0];
const payload = { const { accessToken, refreshToken } = this.authService.generateTokens(id);
id, accountId, status, isDeleted, birthday
};
const { accessToken, refreshToken } = this.authService.generateTokens(payload);
return { return {
success: true, success: true,

View File

@@ -2,5 +2,5 @@ import { BaseResponseDto } from "../base-response.dto";
export class RefreshAccessTokenResponseDto extends BaseResponseDto{ export class RefreshAccessTokenResponseDto extends BaseResponseDto{
accessToken: string; accessToken: string;
refreshToken: string; refreshToken?: string;
} }

View File

@@ -1526,6 +1526,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@fastify/cookie@npm:^11.0.2":
version: 11.0.2
resolution: "@fastify/cookie@npm:11.0.2"
dependencies:
cookie: "npm:^1.0.0"
fastify-plugin: "npm:^5.0.0"
checksum: 10c0/055aef3260e2a95115e976d820faeacf2513790e99c589c6bfeef06ead181b501a5bd18fb6fa8c507fd2a284cb2e0808e180ab2e64138269f717383e82a75384
languageName: node
linkType: hard
"@fastify/cors@npm:11.1.0": "@fastify/cors@npm:11.1.0":
version: 11.1.0 version: 11.1.0
resolution: "@fastify/cors@npm:11.1.0" resolution: "@fastify/cors@npm:11.1.0"
@@ -4395,6 +4405,7 @@ __metadata:
dependencies: dependencies:
"@eslint/eslintrc": "npm:^3.2.0" "@eslint/eslintrc": "npm:^3.2.0"
"@eslint/js": "npm:^9.18.0" "@eslint/js": "npm:^9.18.0"
"@fastify/cookie": "npm:^11.0.2"
"@nestjs/class-transformer": "npm:^0.4.0" "@nestjs/class-transformer": "npm:^0.4.0"
"@nestjs/class-validator": "npm:^0.13.4" "@nestjs/class-validator": "npm:^0.13.4"
"@nestjs/cli": "npm:^11.0.0" "@nestjs/cli": "npm:^11.0.0"
@@ -4420,6 +4431,7 @@ __metadata:
bcrypt: "npm:^6.0.0" bcrypt: "npm:^6.0.0"
cross-env: "npm:^10.1.0" cross-env: "npm:^10.1.0"
dotenv: "npm:^17.2.3" dotenv: "npm:^17.2.3"
dotenv-cli: "npm:^11.0.0"
drizzle-kit: "npm:^0.31.7" drizzle-kit: "npm:^0.31.7"
drizzle-orm: "npm:^0.44.7" drizzle-orm: "npm:^0.44.7"
eslint: "npm:^9.18.0" eslint: "npm:^9.18.0"
@@ -4947,7 +4959,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cookie@npm:^1.0.1": "cookie@npm:^1.0.0, cookie@npm:^1.0.1":
version: 1.1.1 version: 1.1.1
resolution: "cookie@npm:1.1.1" resolution: "cookie@npm:1.1.1"
checksum: 10c0/79c4ddc0fcad9c4f045f826f42edf54bcc921a29586a4558b0898277fa89fb47be95bc384c2253f493af7b29500c830da28341274527328f18eba9f58afa112c checksum: 10c0/79c4ddc0fcad9c4f045f826f42edf54bcc921a29586a4558b0898277fa89fb47be95bc384c2253f493af7b29500c830da28341274527328f18eba9f58afa112c
@@ -5132,6 +5144,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"dotenv-cli@npm:^11.0.0":
version: 11.0.0
resolution: "dotenv-cli@npm:11.0.0"
dependencies:
cross-spawn: "npm:^7.0.6"
dotenv: "npm:^17.1.0"
dotenv-expand: "npm:^12.0.0"
minimist: "npm:^1.2.6"
bin:
dotenv: cli.js
checksum: 10c0/c3e6e58a484b3204eeb167a8f78c9cb04c4bae951069e7860eccbbbf96964e3bd808b3632dfe841e5d882b2c5d77c447f20abd06edd4db623ce719d94a7fb44e
languageName: node
linkType: hard
"dotenv-expand@npm:12.0.1": "dotenv-expand@npm:12.0.1":
version: 12.0.1 version: 12.0.1
resolution: "dotenv-expand@npm:12.0.1" resolution: "dotenv-expand@npm:12.0.1"
@@ -5141,6 +5167,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"dotenv-expand@npm:^12.0.0":
version: 12.0.3
resolution: "dotenv-expand@npm:12.0.3"
dependencies:
dotenv: "npm:^16.4.5"
checksum: 10c0/0824bdc74fc816a28b0744b7853a23e046602e9616c82121dfdae21ebc13c6e89afeb773e794e97c35d48be2be0a990fbca721ee3869a49c04210a58a3cf296f
languageName: node
linkType: hard
"dotenv@npm:16.4.7": "dotenv@npm:16.4.7":
version: 16.4.7 version: 16.4.7
resolution: "dotenv@npm:16.4.7" resolution: "dotenv@npm:16.4.7"
@@ -5155,7 +5190,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"dotenv@npm:^17.2.3": "dotenv@npm:^17.1.0, dotenv@npm:^17.2.3":
version: 17.2.3 version: 17.2.3
resolution: "dotenv@npm:17.2.3" resolution: "dotenv@npm:17.2.3"
checksum: 10c0/c884403209f713214a1b64d4d1defa4934c2aa5b0002f5a670ae298a51e3c3ad3ba79dfee2f8df49f01ae74290fcd9acdb1ab1d09c7bfb42b539036108bb2ba0 checksum: 10c0/c884403209f713214a1b64d4d1defa4934c2aa5b0002f5a670ae298a51e3c3ad3ba79dfee2f8df49f01ae74290fcd9acdb1ab1d09c7bfb42b539036108bb2ba0