-refreshtoken용 테이블을 user 테이블과 합침
-db에서 refreshtoken_hash 를 확인하는걸로 변경
This commit is contained in:
parent
63e3831951
commit
7fc3434567
@ -2,6 +2,8 @@ import type { Handle } from '@sveltejs/kit';
|
||||
import * as auth from '$lib/server/auth';
|
||||
import { db } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import {compare, hash} from "bcryptjs";
|
||||
import {eq} from "drizzle-orm";
|
||||
|
||||
const handleAuth: Handle = async ({ event, resolve }) => {
|
||||
// 1. 먼저 액세스 토큰 확인
|
||||
@ -18,45 +20,61 @@ const handleAuth: Handle = async ({ event, resolve }) => {
|
||||
}
|
||||
|
||||
// 2. 액세스 토큰이 없거나 유효하지 않을 때 리프레시 토큰 확인
|
||||
const refreshToken = event.cookies.get(auth.refreshCookieName);
|
||||
|
||||
if (!refreshToken) {
|
||||
const refreshTokenCookie = event.cookies.get(auth.refreshCookieName);
|
||||
if (!refreshTokenCookie) {
|
||||
// 두 토큰 모두 없거나 유효하지 않으면 인증되지 않은 사용자로 처리
|
||||
event.locals.user = null;
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
// 리프레시 토큰 검증
|
||||
const { userId, isValid } = auth.validateRefreshToken(refreshToken);
|
||||
|
||||
const { userId, isValid } = auth.validateRefreshToken(refreshTokenCookie);
|
||||
if (!isValid || !userId) {
|
||||
// 리프레시 토큰이 유효하지 않으면 모든 쿠키 삭제
|
||||
auth.deleteAuthCookies(event);
|
||||
event.locals.user = null;
|
||||
return resolve(event);
|
||||
}
|
||||
// 리프레시 토큰이 유효하면 사용자 DB의 리프레시토큰해시와 비교
|
||||
const results = await db
|
||||
.select({refreshTokenHash: table.user.refreshTokenHash})
|
||||
.from(table.user)
|
||||
.where(eq(table.user.id, userId));
|
||||
|
||||
// 유효한 리프레시 토큰이 있으면 사용자 정보 조회
|
||||
const user = await db.query.refreshTokens.findFirst({
|
||||
where: (refreshTokens, { eq }) => eq(refreshTokens.id, userId)
|
||||
});
|
||||
const refreshToken_data = results.at(0);
|
||||
|
||||
if (!user) {
|
||||
|
||||
if (!refreshToken_data) {
|
||||
auth.deleteAuthCookies(event);
|
||||
event.locals.user = null;
|
||||
return resolve(event);
|
||||
}
|
||||
console.log("refreshToken_cookie: ",refreshTokenCookie);
|
||||
console.log("refreshToken_hash: ",refreshToken_data.refreshTokenHash);
|
||||
|
||||
// 새 액세스 토큰 발급
|
||||
const newAccessToken = auth.generateAccessToken(user.id);
|
||||
auth.setAccessTokenCookie(event, newAccessToken);
|
||||
const validRefreshToken = await compare(refreshTokenCookie, refreshToken_data.refreshTokenHash);
|
||||
console.log("validRefreshToken: ",validRefreshToken);
|
||||
if(!validRefreshToken){
|
||||
auth.deleteAuthCookies(event);
|
||||
event.locals.user = null;
|
||||
return resolve(event);
|
||||
|
||||
// 사용자 정보 설정
|
||||
event.locals.user = {
|
||||
id: user.id,
|
||||
};
|
||||
} else {
|
||||
// 새 액세스 토큰 발급
|
||||
const newAccessToken = auth.generateAccessToken(userId);
|
||||
const newRefreshToken = auth.generateRefreshToken(userId);
|
||||
auth.setAccessTokenCookie(event, newAccessToken);
|
||||
auth.setRefreshTokenCookie(event, newRefreshToken);
|
||||
const refreshTokenHash = await hash(refreshTokenCookie, 10) ;
|
||||
await db.update(table.user).set({ refreshTokenHash: refreshTokenHash }).where(eq(table.user.id,userId));
|
||||
// 사용자 정보 설정
|
||||
event.locals.user = {
|
||||
id: userId,
|
||||
};
|
||||
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
return resolve(event);
|
||||
};
|
||||
|
||||
export const handle: Handle = handleAuth;
|
||||
@ -75,3 +75,7 @@ export function deleteAuthCookies(event: RequestEvent) {
|
||||
event.cookies.delete(jwtCookieName, { path: '/' });
|
||||
event.cookies.delete(refreshCookieName, { path: '/' });
|
||||
}
|
||||
|
||||
export function deleteJwtCookie(event: RequestEvent) {
|
||||
event.cookies.delete(jwtCookieName, { path: '/' });
|
||||
}
|
||||
@ -3,15 +3,9 @@ import { pgTable, serial, integer, text, timestamp } from 'drizzle-orm/pg-core';
|
||||
export const user = pgTable('user', {
|
||||
id: text('id').primaryKey(),
|
||||
email: text('email').notNull().unique(),
|
||||
passwordHash: text('password_hash').notNull()
|
||||
passwordHash: text('password_hash').notNull(),
|
||||
refreshTokenHash: text('refresh_token_hash').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const refreshTokens = pgTable('refresh_tokens', {
|
||||
id: text('id').primaryKey(),
|
||||
userId: text('user_id').notNull(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull()
|
||||
});
|
||||
|
||||
export type RefreshTokens = typeof refreshTokens.$inferSelect;
|
||||
export type User = typeof user.$inferSelect;
|
||||
|
||||
@ -1,23 +1,17 @@
|
||||
// D:/gitea/Jwt/src/routes/demo/lucia/+page.server.ts
|
||||
|
||||
import * as auth from '$lib/server/auth';
|
||||
import { redirect } from '@sveltejs/kit'; // `fail`은 사용되지 않으므로 제거합니다.
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
import {db} from "$lib/server/db";
|
||||
import * as table from "$lib/server/db/schema";
|
||||
import {eq} from "drizzle-orm";
|
||||
|
||||
// `load` 함수는 event 객체에서 locals와 cookies를 직접 받아옵니다.
|
||||
export const load: PageServerLoad = async ({ locals, cookies }) => {
|
||||
// 1. `locals`에서 직접 사용자 정보를 확인합니다. 이것이 표준 방식입니다.
|
||||
if (!locals.user) {
|
||||
// 2. 사용자가 없으면 여기서 바로 리다이렉트합니다.
|
||||
throw redirect(302, '/demo/lucia/login');
|
||||
}
|
||||
|
||||
// 이 코드는 사용자가 로그인한 경우에만 실행됩니다.
|
||||
// 이전에 정의한 쿠키 이름으로 수정했습니다.
|
||||
const accessToken = cookies.get(auth.jwtCookieName);
|
||||
const refreshToken = cookies.get(auth.refreshCookieName);
|
||||
|
||||
// 3. `locals`의 사용자와 쿠키에서 읽은 토큰을 반환합니다.
|
||||
return {
|
||||
user: locals.user,
|
||||
accessToken: accessToken ?? 'N/A', // 토큰이 없을 경우를 대비해 기본값 설정
|
||||
@ -27,17 +21,13 @@ export const load: PageServerLoad = async ({ locals, cookies }) => {
|
||||
|
||||
export const actions: Actions = {
|
||||
logout: async (event) => {
|
||||
// 더 안전한 로그아웃을 위해 DB의 리프레시 토큰을 무효화합니다.
|
||||
const refreshTokenFromCookie = event.cookies.get(auth.refreshCookieName);
|
||||
// if (refreshTokenFromCookie) {
|
||||
// // auth.ts에 구현된 invalidateRefreshToken 함수를 호출합니다.
|
||||
// await auth.invalidateRefreshToken(refreshTokenFromCookie);
|
||||
// }
|
||||
|
||||
// 그 다음 쿠키를 삭제합니다.
|
||||
if (event.locals.user) {
|
||||
await db.update(table.user).set({refreshTokenHash: ''}).where(eq(table.user.id, event.locals.user.id));
|
||||
}
|
||||
auth.deleteAuthCookies(event);
|
||||
|
||||
// `actions`에서는 throw redirect(...)를 사용하는 것이 표준입니다.
|
||||
throw redirect(302, '/demo/lucia/login');
|
||||
},
|
||||
removeJwtCookie: async (event) => {
|
||||
auth.deleteJwtCookie(event);
|
||||
}
|
||||
};
|
||||
@ -13,3 +13,7 @@
|
||||
<form method='post' action='?/logout' use:enhance>
|
||||
<button>Sign out</button>
|
||||
</form>
|
||||
|
||||
<form method='post' action='?/removeJwtCookie' use:enhance>
|
||||
<button>Remove Jwt Cookie</button>
|
||||
</form>
|
||||
|
||||
@ -45,15 +45,25 @@ export const actions: Actions = {
|
||||
if (!validPassword) {
|
||||
return fail(400, { message: 'Incorrect username or password' });
|
||||
}
|
||||
|
||||
const saltRounds = 10;
|
||||
// 두 토큰 모두 생성
|
||||
const accessToken = auth.generateAccessToken(existingUser.id);
|
||||
const refreshToken = auth.generateRefreshToken(existingUser.id);
|
||||
|
||||
const refreshTokenHash = await hash(refreshToken, saltRounds) ;
|
||||
// 두 쿠키 모두 설정
|
||||
auth.setAccessTokenCookie(event, accessToken);
|
||||
auth.setRefreshTokenCookie(event, refreshToken);
|
||||
try {
|
||||
await db.update(table.user).set({ refreshTokenHash: refreshTokenHash }).where(eq(table.user.id, existingUser.id));
|
||||
// 두 토큰 모두 생성
|
||||
|
||||
// 두 쿠키 모두 설정
|
||||
auth.setAccessTokenCookie(event, accessToken);
|
||||
auth.setRefreshTokenCookie(event, refreshToken);
|
||||
|
||||
} catch {
|
||||
return fail(500, { message: 'An error has occurred' });
|
||||
}
|
||||
return redirect(302, '/demo/lucia');
|
||||
},
|
||||
register: async (event) => {
|
||||
@ -62,6 +72,7 @@ export const actions: Actions = {
|
||||
const password = formData.get('password');
|
||||
|
||||
const userId = generateUserId();
|
||||
const refreshId = generateUserId();
|
||||
// 타입 체크 및 검증
|
||||
if (!email || typeof email !== 'string') {
|
||||
return fail(400, { message: '이메일이 필요합니다' });
|
||||
@ -76,13 +87,12 @@ export const actions: Actions = {
|
||||
const passwordHash = await hash(password, saltRounds);
|
||||
|
||||
try {
|
||||
await db.insert(table.user).values({ id: userId, email, passwordHash });
|
||||
|
||||
// 두 토큰 모두 생성
|
||||
const accessToken = auth.generateAccessToken(userId);
|
||||
const refreshToken = auth.generateRefreshToken(userId);
|
||||
const refreshTokenHash = await hash(auth.generateRefreshToken(userId), saltRounds) ;
|
||||
|
||||
await db.insert(table.user).values({ id: userId, email, passwordHash, refreshTokenHash: refreshTokenHash });
|
||||
|
||||
// 두 쿠키 모두 설정
|
||||
auth.setAccessTokenCookie(event, accessToken);
|
||||
auth.setRefreshTokenCookie(event, refreshToken);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user