weiter die Sache mit Auth
This commit is contained in:
50
lib/auth.ts
Normal file
50
lib/auth.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Reusable authentication library
|
||||
* Configure users via environment variables in .env:
|
||||
* AUTH_USERS=user1:$2a$10$hash1,user2:$2a$10$hash2
|
||||
*
|
||||
* Use scripts/generate-password.js to generate password hashes
|
||||
*/
|
||||
|
||||
export interface User {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse users from environment variable
|
||||
* Format: username:password,username2:password2
|
||||
*/
|
||||
export function getUsers(): User[] {
|
||||
const usersString = process.env.AUTH_USERS || '';
|
||||
if (!usersString) {
|
||||
console.warn('AUTH_USERS not configured in .env');
|
||||
return [];
|
||||
}
|
||||
return usersString
|
||||
.split(',')
|
||||
.map((userPair) => {
|
||||
const [username, password] = userPair.trim().split(':');
|
||||
return { username: username?.trim(), password: password?.trim() };
|
||||
})
|
||||
.filter((user) => user.username && user.password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify user credentials
|
||||
*/
|
||||
export function verifyCredentials(username: string, password: string): boolean {
|
||||
const users = getUsers();
|
||||
const user = users.find(u => u.username === username);
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
return user.password === password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if authentication is enabled
|
||||
*/
|
||||
export function isAuthEnabled(): boolean {
|
||||
return !!process.env.AUTH_USERS;
|
||||
}
|
||||
102
lib/session.ts
Normal file
102
lib/session.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { cookies } from 'next/headers';
|
||||
import { SignJWT, jwtVerify } from 'jose';
|
||||
|
||||
const SESSION_COOKIE_NAME = 'auth_session';
|
||||
const SESSION_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
const secretKey = process.env.AUTH_SECRET || 'default-secret-change-in-production';
|
||||
const key = new TextEncoder().encode(secretKey);
|
||||
|
||||
export interface SessionData {
|
||||
username: string;
|
||||
isAuthenticated: boolean;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt session data to JWT
|
||||
*/
|
||||
async function encrypt(payload: SessionData): Promise<string> {
|
||||
return await new SignJWT(payload as any)
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime(new Date(payload.expiresAt))
|
||||
.sign(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt JWT to session data
|
||||
*/
|
||||
async function decrypt(token: string): Promise<SessionData | null> {
|
||||
try {
|
||||
const { payload } = await jwtVerify(token, key, {
|
||||
algorithms: ['HS256'],
|
||||
});
|
||||
return {
|
||||
username: payload.username as string,
|
||||
isAuthenticated: payload.isAuthenticated as boolean,
|
||||
expiresAt: payload.expiresAt as number,
|
||||
};
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new session
|
||||
*/
|
||||
export async function createSession(username: string): Promise<void> {
|
||||
const expiresAt = Date.now() + SESSION_DURATION;
|
||||
const session: SessionData = {
|
||||
username,
|
||||
isAuthenticated: true,
|
||||
expiresAt,
|
||||
};
|
||||
|
||||
const encryptedSession = await encrypt(session);
|
||||
const cookieStore = await cookies();
|
||||
|
||||
cookieStore.set(SESSION_COOKIE_NAME, encryptedSession, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
expires: expiresAt,
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current session
|
||||
*/
|
||||
export async function getSession(): Promise<SessionData | null> {
|
||||
const cookieStore = await cookies();
|
||||
const cookie = cookieStore.get(SESSION_COOKIE_NAME);
|
||||
|
||||
if (!cookie?.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const session = await decrypt(cookie.value);
|
||||
|
||||
if (!session || session.expiresAt < Date.now()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete session (logout)
|
||||
*/
|
||||
export async function deleteSession(): Promise<void> {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.delete(SESSION_COOKIE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if user is authenticated
|
||||
*/
|
||||
export async function isAuthenticated(): Promise<boolean> {
|
||||
const session = await getSession();
|
||||
return session?.isAuthenticated ?? false;
|
||||
}
|
||||
Reference in New Issue
Block a user