jwt 작동되도록 수정..
This commit is contained in:
parent
e0ed7c598b
commit
bdf43e1906
3235
package-lock.json
generated
3235
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@
|
|||||||
"@sveltejs/adapter-auto": "^6.0.0",
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
"@sveltejs/kit": "^2.16.0",
|
"@sveltejs/kit": "^2.16.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"@types/bcryptjs": "^2.4.6",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
"drizzle-kit": "^0.30.2",
|
"drizzle-kit": "^0.30.2",
|
||||||
@ -28,9 +29,9 @@
|
|||||||
"vite-plugin-devtools-json": "^0.2.0"
|
"vite-plugin-devtools-json": "^0.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-rs/argon2": "^2.0.2",
|
|
||||||
"@oslojs/crypto": "^1.0.1",
|
"@oslojs/crypto": "^1.0.1",
|
||||||
"@oslojs/encoding": "^1.1.0",
|
"@oslojs/encoding": "^1.1.0",
|
||||||
|
"bcryptjs": "^3.0.2",
|
||||||
"drizzle-orm": "^0.40.0",
|
"drizzle-orm": "^0.40.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"postgres": "^3.4.5"
|
"postgres": "^3.4.5"
|
||||||
|
|||||||
@ -8,10 +8,10 @@ const handleAuth: Handle = async ({ event, resolve }) => {
|
|||||||
const accessToken = event.cookies.get(auth.jwtCookieName);
|
const accessToken = event.cookies.get(auth.jwtCookieName);
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
const { user, isValid } = auth.validateJwtToken(accessToken);
|
const { userId, isValid } = auth.validateJwtToken(accessToken);
|
||||||
|
|
||||||
if (isValid && user) {
|
if (isValid && userId) {
|
||||||
event.locals.user = user;
|
event.locals.user = {id: userId};
|
||||||
return resolve(event);
|
return resolve(event);
|
||||||
}
|
}
|
||||||
// 액세스 토큰이 유효하지 않으면 refresh 시도
|
// 액세스 토큰이 유효하지 않으면 refresh 시도
|
||||||
@ -37,8 +37,8 @@ const handleAuth: Handle = async ({ event, resolve }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 유효한 리프레시 토큰이 있으면 사용자 정보 조회
|
// 유효한 리프레시 토큰이 있으면 사용자 정보 조회
|
||||||
const user = await db.query.users.findFirst({
|
const user = await db.query.refreshTokens.findFirst({
|
||||||
where: (users, { eq }) => eq(users.id, userId)
|
where: (refreshTokens, { eq }) => eq(refreshTokens.id, userId)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@ -48,13 +48,12 @@ const handleAuth: Handle = async ({ event, resolve }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 새 액세스 토큰 발급
|
// 새 액세스 토큰 발급
|
||||||
const newAccessToken = auth.generateAccessToken(user.id, user.email);
|
const newAccessToken = auth.generateAccessToken(user.id);
|
||||||
auth.setAccessTokenCookie(event, newAccessToken);
|
auth.setAccessTokenCookie(event, newAccessToken);
|
||||||
|
|
||||||
// 사용자 정보 설정
|
// 사용자 정보 설정
|
||||||
event.locals.user = {
|
event.locals.user = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
email: user.email
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return resolve(event);
|
return resolve(event);
|
||||||
|
|||||||
@ -27,6 +27,16 @@ export function generateRefreshToken(userId: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 일반 토큰 검증
|
||||||
|
export function validateJwtToken(token: string) {
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(token, JWT_SECRET) as { userId: string };
|
||||||
|
return { userId: decoded.userId, isValid: true };
|
||||||
|
} catch (error) {
|
||||||
|
return { userId: null, isValid: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 리프레시 토큰 검증
|
// 리프레시 토큰 검증
|
||||||
export function validateRefreshToken(token: string) {
|
export function validateRefreshToken(token: string) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -13,5 +13,5 @@ export const refreshTokens = pgTable('refresh_tokens', {
|
|||||||
createdAt: timestamp('created_at').defaultNow().notNull()
|
createdAt: timestamp('created_at').defaultNow().notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type RefreshTokens = typeof refreshTokens.$inferSelect;
|
||||||
export type User = typeof user.$inferSelect;
|
export type User = typeof user.$inferSelect;
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { hash, verify } from '@node-rs/argon2';
|
// D:/gitea/Jwt/src/routes/demo/lucia/login/+page.server.ts
|
||||||
|
|
||||||
|
// 1. @node-rs/argon2 대신 bcryptjs를 임포트합니다.
|
||||||
|
import { hash, compare } from 'bcryptjs';
|
||||||
import { encodeBase32LowerCase } from '@oslojs/encoding';
|
import { encodeBase32LowerCase } from '@oslojs/encoding';
|
||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
@ -37,12 +40,8 @@ export const actions: Actions = {
|
|||||||
return fail(400, { message: 'Incorrect username or password' });
|
return fail(400, { message: 'Incorrect username or password' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const validPassword = await verify(existingUser.passwordHash, password, {
|
// 2. verify를 compare 함수로 변경하고, 인자 순서를 (평문, 해시)로 맞춥니다.
|
||||||
memoryCost: 19456,
|
const validPassword = await compare(password, existingUser.passwordHash);
|
||||||
timeCost: 2,
|
|
||||||
outputLen: 32,
|
|
||||||
parallelism: 1,
|
|
||||||
});
|
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
return fail(400, { message: 'Incorrect username or password' });
|
return fail(400, { message: 'Incorrect username or password' });
|
||||||
}
|
}
|
||||||
@ -62,7 +61,6 @@ export const actions: Actions = {
|
|||||||
const email = formData.get('email');
|
const email = formData.get('email');
|
||||||
const password = formData.get('password');
|
const password = formData.get('password');
|
||||||
|
|
||||||
|
|
||||||
const userId = generateUserId();
|
const userId = generateUserId();
|
||||||
// 타입 체크 및 검증
|
// 타입 체크 및 검증
|
||||||
if (!email || typeof email !== 'string') {
|
if (!email || typeof email !== 'string') {
|
||||||
@ -72,13 +70,10 @@ export const actions: Actions = {
|
|||||||
if (!password || typeof password !== 'string') {
|
if (!password || typeof password !== 'string') {
|
||||||
return fail(400, { message: '비밀번호가 필요합니다' });
|
return fail(400, { message: '비밀번호가 필요합니다' });
|
||||||
}
|
}
|
||||||
const passwordHash = await hash(password, {
|
|
||||||
// recommended minimum parameters
|
// 3. hash 함수에 salt rounds 값을 인자로 전달합니다. (보통 10~12를 사용)
|
||||||
memoryCost: 19456,
|
const saltRounds = 10;
|
||||||
timeCost: 2,
|
const passwordHash = await hash(password, saltRounds);
|
||||||
outputLen: 32,
|
|
||||||
parallelism: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.insert(table.user).values({ id: userId, email, passwordHash });
|
await db.insert(table.user).values({ id: userId, email, passwordHash });
|
||||||
@ -103,4 +98,4 @@ function generateUserId() {
|
|||||||
const bytes = crypto.getRandomValues(new Uint8Array(15));
|
const bytes = crypto.getRandomValues(new Uint8Array(15));
|
||||||
const id = encodeBase32LowerCase(bytes);
|
const id = encodeBase32LowerCase(bytes);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user