Hello,
I’m trying to password protect (just password, no username) a simple page using Cloudflare pages. This seems near impossible and I’m going insane.
My page is
- A SvelteKit
https://kit.svelte.dev
app using typescript - Using the Cloudflare Adapter
https://www.npmjs.com/package/@sveltejs/adapter-cloudflare
Authentication mechanism
- Is based on this coudflare-pages-auth
https://dev.to/charca/password-protection-for-cloudflare-pages-8ma
article that adds a middlewarehttps://github.com/Charca/cloudflare-pages-auth/blob/main/functions/_middleware.ts
- But rewritten to use sveltekit hooks
https://kit.svelte.dev/docs/hooks#server-hooks
since the Cloudflare adapter doesnt seem to support middlewares and svelte running simultaneouslyhttps://github.com/sveltejs/kit/tree/master/packages/adapter-cloudflare#notes
Behavior
When running locally using npm run dev
everything is working flawless. Pages are protected, entering the simple password sets the cookie and I’m allowed to enter the page.
When deployed as a Cloudflare page however
-
I enter my password and gets redirected to
/cfp_login
-
Which then redirects me back to
/
if I use the correct password, or/?error=1
if I’m using the wrong password. This behavior is so far correct. -
The weird thing is
- If I enter wrong password, I’m expected to render an error message due to the error query param, this is not happening and I’m stuck at the enter password dialog
- If I enter the correct password, nothing is happening, I’m stuck at the enter password dialog anyway
I have tried the following
-
I know about Access
https://www.cloudflare.com/products/zero-trust/access/
and I tried it but it does not suit my use case at all as they don´t support simple password protection. I just wan´t to send out an invitation with a password for all guests to the page (not any per-account password…instead one for all) -
I have tried hardcoding the password in the code so I know it´s not an issue with getting env vars
-
I have tried running a local production build
npm run build
thennpm run preview
and it also worked -
I have tried removing
Secure
andhttpOnly
attributes from the cookie -
I have tried settging
SameSite=Lax
on the cookie -
I have tried adding a
_headers
file with and disabling CORS using the following content/* Access-Control-Allow-Origin: *
Questions
- Is there some weird super caching going on which makes the login page not re-rendering?
- Can I get logs somehow?!
- Why am I not allowed to use markdown links in posts?
Simply put…what am I missing!?
svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare';
import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),
kit: {
adapter: adapter({
platform: 'node'
}),
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
}
};
export default config;
src/hooks.ts
import { CFP_ALLOWED_PATHS } from '$lib/login/constants';
import { getCookieKeyValue } from '$lib/login/utils';
import { getTemplate } from '$lib/login/template';
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
const password =
event.platform?.env?.CFP_PASSWORD?.toLowerCase()
?? process?.env['CFP_PASSWORD']?.toLowerCase();
const { pathname, searchParams } = new URL(event?.url);
const { error } = Object.fromEntries(searchParams);
const cookie = event.request.headers.get('cookie') || '';
const cookieKeyValue = await getCookieKeyValue(password);
if (cookie.includes(cookieKeyValue) || CFP_ALLOWED_PATHS.includes(pathname) || !password) {
// Correct hash in cookie, allowed path, or no password set.
// Continue to next middleware.
return resolve(event);
} else {
// No cookie or incorrect hash in cookie. Redirect to login.
return new Response(getTemplate({ withError: error === '1' }), {
headers: {
'content-type': 'text/html'
}
});
}
}
src/routes/cfp_login/index.ts
import { CFP_COOKIE_MAX_AGE } from '$lib/login/constants';
import { sha256, getCookieKeyValue } from '$lib/login/utils';
/** @type {import('./$types').RequestHandler} */
export async function post({ request, platform }) {
const cfp_password =
platform?.env?.CFP_PASSWORD?.toLowerCase()
?? process?.env['CFP_PASSWORD']?.toLowerCase();
const body = await request.formData();
const { password } = Object.fromEntries(body);
const hashedPassword = await sha256(password.toString());
const hashedCfpPassword = await sha256(cfp_password);
if (hashedPassword === hashedCfpPassword) {
// Valid password. Redirect to home page and set cookie with auth hash.
const cookieKeyValue = await getCookieKeyValue(cfp_password);
return new Response('', {
status: 302,
headers: {
'Set-Cookie': `${cookieKeyValue}; Max-Age=${CFP_COOKIE_MAX_AGE}; Path=/; HttpOnly; Secure`,
'Cache-Control': 'no-cache',
Location: '/'
}
});
} else {
// Invalid password. Redirect to login page with error.
return new Response('', {
status: 302,
headers: {
'Cache-Control': 'no-cache',
Location: '/?error=1'
}
});
}
}