Basic auth
This commit is contained in:
@@ -44,4 +44,3 @@ h1 {
|
||||
background-color: var(--hover-bg-color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: '.env' });
|
||||
|
||||
export const handle = async ({ event, resolve }) => {
|
||||
const theme = event.cookies.get('theme');
|
||||
const JWT = event.cookies.get('jwt');
|
||||
|
||||
if (process.env.JWT_SECRET === undefined) {
|
||||
throw new Error('JWT_SECRET not defined');
|
||||
}
|
||||
|
||||
if (JWT) {
|
||||
try {
|
||||
const decoded = jwt.verify(JWT, process.env.JWT_SECRET);
|
||||
} catch (err) {
|
||||
event.cookies.delete('jwt', { path: '/' });
|
||||
}
|
||||
}
|
||||
|
||||
if (!theme) {
|
||||
return await resolve(event);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import postgres from 'postgres';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: '.env' });
|
||||
|
||||
const sql = postgres({
|
||||
host: process.env.POSTGRES_HOST,
|
||||
port: parseInt(process.env.POSTGRES_PORT!),
|
||||
database: process.env.POSTGRES_DB,
|
||||
username: process.env.POSTGRES_USER,
|
||||
password: process.env.POSTGRES_PASSWORD
|
||||
});
|
||||
|
||||
export default sql;
|
||||
@@ -0,0 +1,28 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import sql from '$lib/db/db.server';
|
||||
|
||||
export async function createUser(username: string, password: string): Promise<void> {
|
||||
const password_hash: string = await bcrypt.hash(password, 12);
|
||||
|
||||
const response = await sql`
|
||||
INSERT INTO users (username, password_hash, perms)
|
||||
VALUES (${username}, ${password_hash}, 2);
|
||||
`;
|
||||
console.log(response);
|
||||
}
|
||||
|
||||
export async function checkUserCreds(username: string, password: string): Promise<number> {
|
||||
const [user] = await sql`
|
||||
SELECT password_hash, perms
|
||||
FROM users
|
||||
WHERE username = ${username}
|
||||
`;
|
||||
|
||||
if (!user) {
|
||||
return -1;
|
||||
}
|
||||
if (await bcrypt.compare(password, user.password_hash)) {
|
||||
return user['perms'];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
export async function createUser(username: string, password: string): Promise<void> {
|
||||
const sql = `INSERT INTO users (username, password, perms)
|
||||
VALUES ($username, $password, 0)`;
|
||||
|
||||
const hash = await bcrypt.hash(password, 12);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export let userState = $state({ permissions: 0b00000001 });
|
||||
+28
-11
@@ -1,34 +1,46 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { onMount } from 'svelte';
|
||||
import { userState } from '$lib/shared.svelte';
|
||||
// import { userState } from '$lib/shared.svelte';
|
||||
|
||||
let currentTheme: string = $state('');
|
||||
|
||||
function toggleTheme(): void {
|
||||
const theme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
set_theme(theme);
|
||||
setTheme(theme);
|
||||
}
|
||||
|
||||
function set_theme(theme: string) {
|
||||
function setTheme(theme: string) {
|
||||
const one_year = 60 * 60 * 24 * 365;
|
||||
document.cookie = `theme=${theme}; max-age=${one_year}; path=/`;
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
currentTheme = theme;
|
||||
}
|
||||
|
||||
const getCookieValue = (name: String) =>
|
||||
document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || '';
|
||||
|
||||
onMount(() => {
|
||||
const savedTheme = document.documentElement.getAttribute('data-theme');
|
||||
if (savedTheme) {
|
||||
currentTheme = savedTheme;
|
||||
return;
|
||||
} else {
|
||||
const darkPref: boolean = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
currentTheme = darkPref ? 'dark' : 'light';
|
||||
setTheme(currentTheme);
|
||||
}
|
||||
|
||||
const darkPref = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const theme = darkPref ? 'dark' : 'light';
|
||||
set_theme(theme);
|
||||
const JWT = getCookieValue('jwt');
|
||||
if (JWT !== '') {
|
||||
userState.permissions = JSON.parse(JWT.split('.')[1]).permissions;
|
||||
}
|
||||
console.log(userState.permissions);
|
||||
});
|
||||
|
||||
// userState.permissions = 0b00000011;
|
||||
// console.log(userState.permissions);
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
@@ -54,12 +66,17 @@
|
||||
</nav>
|
||||
<div>
|
||||
<button onclick={toggleTheme} class="pr-2">
|
||||
<span class="material-symbols-outlined nav-trailing">
|
||||
{currentTheme === 'dark' ? 'dark_mode' : 'light_mode'}
|
||||
<span class="material-symbols-outlined nav-trailing dark:invisible">
|
||||
{'light_mode'}
|
||||
</span>
|
||||
</button>
|
||||
<button>
|
||||
<span class="material-symbols-outlined nav-trailing">account_circle</span>
|
||||
<button onclick={toggleTheme} class="pr-2">
|
||||
<span class="material-symbols-outlined nav-trailing invisible dark:visible">
|
||||
{'dark_mode'}
|
||||
</span>
|
||||
</button>
|
||||
<button onclick={() => (window.location.href = '/signin')}>
|
||||
<span class="material-symbols-outlined nav-trailing">{'account_circle'}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,2 @@
|
||||
<h1>Hello world</h1>
|
||||
|
||||
<div class="text-center text-green-500">
|
||||
<p class="text-red-800">Test Text</p>
|
||||
<p>hello</p>
|
||||
</div>
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { checkUserCreds, createUser } from '$lib/db/index.server';
|
||||
import { fail, redirect, type Actions, type Cookies } from '@sveltejs/kit';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: '.env' });
|
||||
|
||||
function setJWT(cookies: Cookies, username: string, perms: number) {
|
||||
const payload = {
|
||||
username: username,
|
||||
perms: perms
|
||||
};
|
||||
|
||||
if (process.env.JWT_SECRET === undefined) {
|
||||
throw new Error('JWT_SECRET not defined');
|
||||
}
|
||||
|
||||
const maxAge = 60 * 60 * 24 * 30; // 30 days
|
||||
const JWT = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '30m' });
|
||||
cookies.set('jwt', JWT, { maxAge, path: '/' });
|
||||
}
|
||||
|
||||
export const actions: Actions = {
|
||||
register: async ({ request, cookies }) => {
|
||||
const data = await request.formData();
|
||||
const username = data.get('username')?.toString();
|
||||
const password = data.get('password')?.toString();
|
||||
|
||||
if (username && password) {
|
||||
try {
|
||||
await createUser(username, password);
|
||||
} catch (err) {
|
||||
return fail(400, { errorMessage: `Internal Server Error: ${err}` });
|
||||
}
|
||||
} else {
|
||||
return fail(400, { errorMessage: 'Missing username or password' });
|
||||
}
|
||||
},
|
||||
|
||||
login: async ({ request, cookies }) => {
|
||||
const data = await request.formData();
|
||||
const username = data.get('username')?.toString();
|
||||
const password = data.get('password')?.toString();
|
||||
|
||||
if (username && password) {
|
||||
const perms = await checkUserCreds(username, password);
|
||||
|
||||
if (perms === -1) {
|
||||
return fail(401, { errorMessage: 'Invalid username or password' });
|
||||
}
|
||||
|
||||
setJWT(cookies, username, perms);
|
||||
|
||||
// redirect to home page
|
||||
// return { perms: perms };
|
||||
throw redirect(303, '/');
|
||||
} else {
|
||||
return fail(400, { errorMessage: 'Missing username or password' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import type { ActionData } from './$types';
|
||||
|
||||
// receive form data from server
|
||||
let form: ActionData;
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<h1 class="is-size-3 has-text-weight-semibold my-4">Sign In or Register</h1>
|
||||
<form method="POST">
|
||||
<input class="input my-2" type="text" placeholder="Username" name="username" required />
|
||||
<input class="input my-2" type="password" placeholder="Password" name="password" required />
|
||||
|
||||
<!-- display error message -->
|
||||
{#if form?.errorMessage}
|
||||
<div class="has-text-danger my-2">{form.errorMessage}</div>
|
||||
{/if}
|
||||
|
||||
<button class="button mr-3 mt-4" type="submit" formaction="?/register">Register</button>
|
||||
<button class="button is-primary mt-4" type="submit" formaction="?/login">Sign In</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user