-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from movie-web/dev
Simple proxy v2.1.0
Showing
6 changed files
with
143 additions
and
21 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
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,10 @@ | ||
import { EventHandlerRequest, H3Event } from 'h3'; | ||
|
||
export function getIp(event: H3Event<EventHandlerRequest>) { | ||
const value = getHeader(event, 'CF-Connecting-IP'); | ||
if (!value) | ||
throw new Error( | ||
'Ip header not found, turnstile only works on cloudflare workers', | ||
); | ||
return value; | ||
} |
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,87 @@ | ||
import { H3Event, EventHandlerRequest } from 'h3'; | ||
import jsonwebtoken from '@tsndr/cloudflare-worker-jwt'; | ||
import { getIp } from '@/utils/ip'; | ||
|
||
const turnstileSecret = process.env.TURNSTILE_SECRET ?? null; | ||
const jwtSecret = process.env.JWT_SECRET ?? null; | ||
|
||
const tokenHeader = 'X-Token'; | ||
const jwtPrefix = 'jwt|'; | ||
const turnstilePrefix = 'turnstile|'; | ||
|
||
export function isTurnstileEnabled() { | ||
return !!turnstileSecret && !!jwtSecret; | ||
} | ||
|
||
export async function makeToken(ip: string) { | ||
if (!jwtSecret) throw new Error('Cannot make token without a secret'); | ||
return await jsonwebtoken.sign( | ||
{ | ||
ip, | ||
exp: Math.floor(Date.now() / 1000) + 60 * 10, // 10 Minutes | ||
}, | ||
jwtSecret, | ||
); | ||
} | ||
|
||
export function setTokenHeader( | ||
event: H3Event<EventHandlerRequest>, | ||
token: string, | ||
) { | ||
setHeader(event, tokenHeader, token); | ||
} | ||
|
||
export async function createTokenIfNeeded( | ||
event: H3Event<EventHandlerRequest>, | ||
): Promise<null | string> { | ||
if (!isTurnstileEnabled()) return null; | ||
if (!jwtSecret) return null; | ||
const token = event.headers.get(tokenHeader); | ||
if (!token) return null; | ||
if (!token.startsWith(turnstilePrefix)) return null; | ||
|
||
return await makeToken(getIp(event)); | ||
} | ||
|
||
export async function isAllowedToMakeRequest( | ||
event: H3Event<EventHandlerRequest>, | ||
) { | ||
if (!isTurnstileEnabled()) return true; | ||
|
||
const token = event.headers.get(tokenHeader); | ||
if (!token) return false; | ||
if (!jwtSecret || !turnstileSecret) return false; | ||
|
||
if (token.startsWith(jwtPrefix)) { | ||
const jwtToken = token.slice(jwtPrefix.length); | ||
const isValid = await jsonwebtoken.verify(jwtToken, jwtSecret, { | ||
algorithm: 'HS256', | ||
}); | ||
if (!isValid) return false; | ||
const jwtBody = jsonwebtoken.decode<{ ip: string }>(jwtToken); | ||
if (!jwtBody.payload) return false; | ||
if (getIp(event) !== jwtBody.payload.ip) return false; | ||
return true; | ||
} | ||
|
||
if (token.startsWith(turnstilePrefix)) { | ||
const turnstileToken = token.slice(turnstilePrefix.length); | ||
const formData = new FormData(); | ||
formData.append('secret', turnstileSecret); | ||
formData.append('response', turnstileToken); | ||
formData.append('remoteip', getIp(event)); | ||
|
||
const result = await fetch( | ||
'https://challenges.cloudflare.com/turnstile/v0/siteverify', | ||
{ | ||
body: formData, | ||
method: 'POST', | ||
}, | ||
); | ||
|
||
const outcome: { success: boolean } = await result.json(); | ||
return outcome.success; | ||
} | ||
|
||
return false; | ||
} |