-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add pw protection from Charca/cloudflare-pages-auth
- Loading branch information
Showing
5 changed files
with
159 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { CFP_ALLOWED_PATHS } from './constants'; | ||
import { getCookieKeyValue } from './utils'; | ||
import { getTemplate } from './template'; | ||
|
||
export async function onRequest(context: { | ||
request: Request; | ||
next: () => Promise<Response>; | ||
env: { CFP_PASSWORD?: string }; | ||
}): Promise<Response> { | ||
const { request, next, env } = context; | ||
const { pathname, searchParams } = new URL(request.url); | ||
const { error } = Object.fromEntries(searchParams); | ||
const cookie = request.headers.get('cookie') || ''; | ||
const cookieKeyValue = await getCookieKeyValue(env.CFP_PASSWORD); | ||
|
||
if ( | ||
cookie.includes(cookieKeyValue) || | ||
CFP_ALLOWED_PATHS.includes(pathname) || | ||
!env.CFP_PASSWORD | ||
) { | ||
// Correct hash in cookie, allowed path, or no password set. | ||
// Continue to next middleware. | ||
return await next(); | ||
} else { | ||
Check failure on line 24 in tinlake-ui/functions/_middleware.ts GitHub Actions / build-and-deploy-to-goerli
|
||
// No cookie or incorrect hash in cookie. Redirect to login. | ||
return new Response(getTemplate({ redirectPath: pathname, withError: error === '1' }), { | ||
headers: { | ||
'content-type': 'text/html' | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { CFP_COOKIE_MAX_AGE } from './constants'; | ||
import { sha256, getCookieKeyValue } from './utils'; | ||
|
||
export async function onRequestPost(context: { | ||
request: Request; | ||
env: { CFP_PASSWORD?: string }; | ||
}): Promise<Response> { | ||
const { request, env } = context; | ||
const body = await request.formData(); | ||
const { password, redirect } = Object.fromEntries(body); | ||
const hashedPassword = await sha256(password.toString()); | ||
const hashedCfpPassword = await sha256(env.CFP_PASSWORD); | ||
const redirectPath = redirect.toString() || '/'; | ||
|
||
if (hashedPassword === hashedCfpPassword) { | ||
// Valid password. Redirect to home page and set cookie with auth hash. | ||
const cookieKeyValue = await getCookieKeyValue(env.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: redirectPath | ||
} | ||
}); | ||
} else { | ||
Check failure on line 27 in tinlake-ui/functions/cfp_login.ts GitHub Actions / build-and-deploy-to-goerli
|
||
// Invalid password. Redirect to login page with error. | ||
return new Response('', { | ||
status: 302, | ||
headers: { | ||
'Cache-Control': 'no-cache', | ||
Location: `${redirectPath}?error=1` | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* Key for the auth cookie. | ||
*/ | ||
export const CFP_COOKIE_KEY = 'CFP-Auth-Key'; | ||
|
||
/** | ||
* Max age of the auth cookie in seconds. | ||
* Default: 1 week. | ||
*/ | ||
export const CFP_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; | ||
|
||
/** | ||
* Paths that don't require authentication. | ||
* The /cfp_login path must be included. | ||
*/ | ||
export const CFP_ALLOWED_PATHS = ['/cfp_login']; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
export function getTemplate({ | ||
redirectPath, | ||
withError | ||
}: { | ||
redirectPath: string; | ||
withError: boolean; | ||
}): string { | ||
return ` | ||
<!doctype html> | ||
<html lang="en" data-theme="dark"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<title>Password Protected Site</title> | ||
<meta name="description" content="This site is password protected."> | ||
<link rel="shortcut icon" href="https://picocss.com/favicon.ico"> | ||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css"> | ||
<style> | ||
body > main { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
min-height: calc(100vh - 7rem); | ||
padding: 1rem 0; | ||
max-width: 600px; | ||
} | ||
.error { | ||
background: white; | ||
border-radius: 10px; | ||
color: var(--del-color); | ||
padding: 0.5em 1em; | ||
} | ||
h2 { color: var(--color-h2); } | ||
</style> | ||
</head> | ||
<body> | ||
<main> | ||
<article> | ||
<hgroup> | ||
<h1>Password</h1> | ||
<h2>Please enter your password for this site.</h2> | ||
</hgroup> | ||
${withError ? `<p class="error">Incorrect password, please try again.</p>` : ''} | ||
<form method="post" action="/cfp_login"> | ||
<input type="hidden" name="redirect" value="${redirectPath}" /> | ||
<input type="password" name="password" placeholder="Password" aria-label="Password" autocomplete="current-password" required autofocus> | ||
<button type="submit" class="contrast">Login</button> | ||
</form> | ||
</article> | ||
</main> | ||
</body> | ||
</html> | ||
`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { CFP_COOKIE_KEY } from './constants'; | ||
|
||
export async function sha256(str: string): Promise<string> { | ||
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); | ||
return Array.prototype.map | ||
.call(new Uint8Array(buf), (x) => ('00' + x.toString(16)).slice(-2)) | ||
Check failure on line 6 in tinlake-ui/functions/utils.ts GitHub Actions / build-and-deploy-to-goerli
|
||
.join(''); | ||
} | ||
|
||
export async function getCookieKeyValue(password?: string): Promise<string> { | ||
const hash = await sha256(password); | ||
return `${CFP_COOKIE_KEY}=${hash}`; | ||
} |