mirror of
https://github.com/taogaetz/chefbible.git
synced 2025-12-06 11:47:24 -05:00
Add access PIN authentication and manage access cookies
- Introduced ACCESS_PIN in env.example for site access control. - Updated Locals interface to include hasAccess boolean for access management. - Implemented access check in hooks.server.ts to redirect users without access to the /access page. - Cleared access cookie on logout to ensure proper session management.
This commit is contained in:
parent
ec24b39d1a
commit
fe3f532a25
4
cookies.txt
Normal file
4
cookies.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Netscape HTTP Cookie File
|
||||||
|
# https://curl.se/docs/http-cookies.html
|
||||||
|
# This file was generated by libcurl! Edit at your own risk.
|
||||||
|
|
||||||
@ -8,5 +8,8 @@ ORIGIN=https://your-domain.com
|
|||||||
# Authentication token (required - generate a secure random token)
|
# Authentication token (required - generate a secure random token)
|
||||||
MAGIC_LINK_TOKEN=your-secure-token-here
|
MAGIC_LINK_TOKEN=your-secure-token-here
|
||||||
|
|
||||||
|
# Access PIN code (required - set a PIN for site access)
|
||||||
|
ACCESS_PIN=1234
|
||||||
|
|
||||||
# Cloudinary URL for photo uploads (optional)
|
# Cloudinary URL for photo uploads (optional)
|
||||||
CLOUDINARY_URL=cloudinary://api_key:api_secret@cloud_name
|
CLOUDINARY_URL=cloudinary://api_key:api_secret@cloud_name
|
||||||
|
|||||||
1
src/app.d.ts
vendored
1
src/app.d.ts
vendored
@ -8,6 +8,7 @@ declare global {
|
|||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
interface Locals {
|
interface Locals {
|
||||||
authenticated: boolean;
|
authenticated: boolean;
|
||||||
|
hasAccess: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,24 @@
|
|||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
import { dev } from '$app/environment';
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
// Check if user has access (PIN code authentication)
|
||||||
|
const accessCookie = event.cookies.get('access_granted');
|
||||||
|
const hasAccess = !!accessCookie;
|
||||||
|
|
||||||
|
// If user doesn't have access and is not on the access page, redirect to /access
|
||||||
|
if (!hasAccess && event.url.pathname !== '/access') {
|
||||||
|
throw redirect(302, '/access');
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user is authenticated using the chef token cookie
|
// Check if user is authenticated using the chef token cookie
|
||||||
const chefToken = event.cookies.get('chef_token');
|
const chefToken = event.cookies.get('chef_token');
|
||||||
const authenticated = !!chefToken;
|
const authenticated = !!chefToken;
|
||||||
|
|
||||||
// Add authentication status to locals for use in load functions
|
// Add authentication status to locals for use in load functions
|
||||||
event.locals.authenticated = authenticated;
|
event.locals.authenticated = authenticated;
|
||||||
|
event.locals.hasAccess = hasAccess;
|
||||||
|
|
||||||
// Continue with the request
|
// Continue with the request
|
||||||
const response = await resolve(event);
|
const response = await resolve(event);
|
||||||
|
|||||||
38
src/routes/access/+page.server.ts
Normal file
38
src/routes/access/+page.server.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
|
import { dev } from '$app/environment';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ locals }) => {
|
||||||
|
// If user already has access, redirect to home
|
||||||
|
if (locals.hasAccess) {
|
||||||
|
throw redirect(302, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
default: async ({ request, cookies }) => {
|
||||||
|
const data = await request.formData();
|
||||||
|
const pin = data.get('pin') as string;
|
||||||
|
|
||||||
|
if (!pin) {
|
||||||
|
return fail(400, { error: 'PIN is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin !== process.env.ACCESS_PIN) {
|
||||||
|
return fail(400, { error: 'Invalid PIN' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set access cookie
|
||||||
|
cookies.set('access_granted', 'true', {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: !dev,
|
||||||
|
maxAge: 60 * 60 * 24 * 30 // 30 days
|
||||||
|
});
|
||||||
|
|
||||||
|
throw redirect(302, '/');
|
||||||
|
}
|
||||||
|
};
|
||||||
47
src/routes/access/+page.svelte
Normal file
47
src/routes/access/+page.svelte
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { enhance } from '$app/forms';
|
||||||
|
import type { ActionData } from './$types';
|
||||||
|
|
||||||
|
let { form, data }: { form: ActionData; data: any } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Access Bible</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="flex min-h-screen items-center justify-center bg-base-200">
|
||||||
|
<div class="card w-full max-w-md bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-6 text-center">
|
||||||
|
<h1 class="text-2xl font-bold text-base-content">Access</h1>
|
||||||
|
<p class="mt-2 text-base-content/70">Enter your access code to continue</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" use:enhance>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="pin">
|
||||||
|
<span class="label-text">Access Code</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="pin"
|
||||||
|
name="pin"
|
||||||
|
class="input-bordered input w-full"
|
||||||
|
placeholder="Enter access code"
|
||||||
|
required
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
{#if form?.error}
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text-alt text-error">{form.error}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control mt-6">
|
||||||
|
<button type="submit" class="btn btn-primary"> Access Site </button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -20,6 +20,15 @@ export const POST: RequestHandler = async ({ cookies }) => {
|
|||||||
maxAge: 0 // Expire immediately
|
maxAge: 0 // Expire immediately
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clear the access cookie
|
||||||
|
cookies.set('access_granted', '', {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: !dev,
|
||||||
|
maxAge: 0 // Expire immediately
|
||||||
|
});
|
||||||
|
|
||||||
// Redirect to home page
|
// Redirect to home page
|
||||||
throw redirect(302, '/');
|
throw redirect(302, '/');
|
||||||
};
|
};
|
||||||
@ -42,6 +51,15 @@ export const GET: RequestHandler = async ({ cookies }) => {
|
|||||||
maxAge: 0 // Expire immediately
|
maxAge: 0 // Expire immediately
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clear the access cookie
|
||||||
|
cookies.set('access_granted', '', {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: !dev,
|
||||||
|
maxAge: 0 // Expire immediately
|
||||||
|
});
|
||||||
|
|
||||||
// Redirect to home page
|
// Redirect to home page
|
||||||
throw redirect(302, '/');
|
throw redirect(302, '/');
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user