Skip to content

Commit

Permalink
feat: implement test token injection & validation for captcha bypass,…
Browse files Browse the repository at this point in the history
… gut auth0
  • Loading branch information
dallen4 committed Jan 4, 2025
1 parent 3770ece commit e93adda
Show file tree
Hide file tree
Showing 23 changed files with 812 additions and 822 deletions.
12 changes: 8 additions & 4 deletions .github/workflows/web_ci_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@ jobs:
- name: Run Playwright Tests
shell: bash
env:
STAGE: ${{ github.ref_name }}
STAGE: ${{ github.event.deployment.environment }}
BASE_URL: ${{ github.event.deployment_status.target_url }}
DEADROP_API_URL: ${{ secrets.DEADROP_API_URL }}
REDIS_URL: ${{ secrets.REDIS_URL }}
run: |
echo $DEADROP_API_URL
export STAGE=$STAGE
echo $STAGE
export DEADROP_API_URL=$DEADROP_API_URL
echo $TEST_URI
echo $DEADROP_API_URL
export TEST_URI=$BASE_URL
CI=true yarn web playwright test --trace on
echo $TEST_URI
export REDIS_URL=$REDIS_URL
CI=true yarn test:e2e --trace on
- uses: actions/upload-artifact@v4
if: always()
with:
Expand Down
20 changes: 19 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"cli:build": "yarn cli build",
"cli:release": "cd cli && yarn install --frozen-lockfile && yarn release",
"worker:deploy": "yarn worker deploy",
"analyze:dup": "npx jscpd shared cli web --format \"typescript\"",
"analyze:dup": "jscpd",
"analyze:unused": "ts-prune",
"postversion": "git push --tags"
},
Expand All @@ -53,9 +53,11 @@
"@types/node": "^20",
"@vitest/coverage-istanbul": "^2.1.8",
"eslint": "^8.31.0",
"jscpd": "^4.0.5",
"prettier": "^2.8.2",
"ts-node": "^10.7.0",
"ts-prune": "^0.10.3",
"tsx": "^4.19.2",
"typescript": "^4.0.3",
"vitest": "^2.1.8"
},
Expand All @@ -68,5 +70,21 @@
"engines": {
"node": ">=20",
"yarn": "1"
},
"jscpd": {
"format": [
"typescript"
],
"ignore": [
"**/node_modules/**"
],
"absolute": true,
"gitignore": true,
"path": [
"shared",
"cli",
"web",
"worker"
]
}
}
4 changes: 3 additions & 1 deletion shared/config/http.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const DISABLE_CAPTCHA_COOKIE = 'disable-captcha';
export const TEST_FLAG_COOKIE = 'test-mode';

export const TEST_TOKEN_COOKIE = 'test-tkn';
2 changes: 1 addition & 1 deletion shared/config/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export const OVERVIEW_DOCS_PATH = '/docs/overview';

export const CLI_DOCS_PATH = '/docs/cli';

export const CAPTCHA_API_PATCH = '/api/captcha';
export const CAPTCHA_API_PATH = '/api/captcha';

export const DROP_API_PATH = '/drop';
15 changes: 0 additions & 15 deletions web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@ REDIS_URL=rediss://
NEXT_PUBLIC_HCAPTCHA_SITEKEY="<uuid>"
HCAPTCHA_SECRET=1234567890

# A long, secret value used to encrypt the session cookie
AUTH0_SECRET="LONG_RANDOM_VALUE"
# The base url of your application
AUTH0_BASE_URL="http://localhost:3000"
# The url of your Auth0 tenant domain
AUTH0_ISSUER_BASE_URL="https://AUTH0_DOMAIN.auth0.com"
# Your Auth0 application"s Client ID
AUTH0_CLIENT_ID="AUTH0_CLIENT_ID"
# Your Auth0 application"s Client Secret
AUTH0_CLIENT_SECRET="AUTH0_CLIENT_SECRET"

# machine to machine application creds
MGMT_AUTH0_CLIENT_ID="<client_id>"
MGMT_AUTH0_CLIENT_SECRET="<client_secret>"

IPINFO_IO_TOKEN=1234567890

STRIPE_SECRET_KEY="<secret_key>"
Expand Down
49 changes: 0 additions & 49 deletions web/api/auth0.ts

This file was deleted.

35 changes: 26 additions & 9 deletions web/atoms/Captcha.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react';
import { useEffect, useState } from 'react';
import { useUser } from '@clerk/nextjs';
import { getCookie } from 'cookies-next';
import HCaptcha from '@hcaptcha/react-hcaptcha';
import { post } from '@shared/lib/fetch';
import { CAPTCHA_API_PATCH } from '@shared/config/paths';
import { useMantineTheme } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import { TEST_FLAG_COOKIE } from '@shared/config/http';
import { CAPTCHA_API_PATH } from '@shared/config/paths';
import { post } from '@shared/lib/fetch';
import { HCAPTCHA_EMBED_ID } from 'lib/constants';

export const Captcha = ({ onSuccess, onExpire }: CaptchaProps) => {
Expand All @@ -12,10 +15,24 @@ export const Captcha = ({ onSuccess, onExpire }: CaptchaProps) => {
`(max-width: ${theme.breakpoints.sm}px)`,
);

const onVerify = async (token: string, _ekey: string) => {
console.log('testing');
const resp = await post<{ success: boolean }, { token: string }>(
CAPTCHA_API_PATCH,
const [show, setShow] = useState(true);

const { isLoaded, isSignedIn } = useUser();

useEffect(() => {
if (isLoaded) {
if (isSignedIn) {
onSuccess();
setShow(false);
} else if (getCookie(TEST_FLAG_COOKIE)) {
onVerify();
}
}
}, [isLoaded, isSignedIn]);

const onVerify = async (token?: string) => {
const resp = await post<{ success: boolean }, { token?: string }>(
CAPTCHA_API_PATH,
{ token },
);

Expand All @@ -26,7 +43,7 @@ export const Captcha = ({ onSuccess, onExpire }: CaptchaProps) => {
console.warn(event);
};

return (
return show ? (
<HCaptcha
id={HCAPTCHA_EMBED_ID}
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITEKEY!}
Expand All @@ -35,7 +52,7 @@ export const Captcha = ({ onSuccess, onExpire }: CaptchaProps) => {
onError={onError}
onExpire={onExpire}
/>
);
) : <></>;
};

type CaptchaProps = {
Expand Down
2 changes: 0 additions & 2 deletions web/config/cookies.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export const NONCE_COOKIE = 'drop-nonce';

export const DAILY_DROP_LIMIT_COOKIE = 'daily-drop-limit';

export const DISABLE_CAPTCHA_COOKIE = 'disable-captcha';
21 changes: 10 additions & 11 deletions web/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { nanoid } from 'nanoid';
import {
DISABLE_CAPTCHA_COOKIE,
DAILY_DROP_LIMIT_COOKIE,
NONCE_COOKIE,
} from 'config/cookies';
import { get } from '@vercel/edge-config';
import {
clerkMiddleware,
createRouteMatcher,
} from '@clerk/nextjs/server';
import { TEST_TOKEN_COOKIE } from '@shared/config/http';
import { get } from '@vercel/edge-config';
import {
DAILY_DROP_LIMIT_COOKIE,
NONCE_COOKIE,
} from 'config/cookies';
import { nanoid } from 'nanoid';
import { NextResponse } from 'next/server';

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);

Expand Down Expand Up @@ -54,10 +53,10 @@ export default clerkMiddleware(async (auth, req) => {
// disable captcha if in development mode
if (
process.env.NODE_ENV === 'development' &&
!req.cookies.get(DISABLE_CAPTCHA_COOKIE)
!req.cookies.get(TEST_TOKEN_COOKIE)
) {
console.log('Disabling captcha');
response.cookies.set(DISABLE_CAPTCHA_COOKIE, 'true', {
response.cookies.set(TEST_TOKEN_COOKIE, 'true', {
sameSite: true,
});
}
Expand Down
34 changes: 8 additions & 26 deletions web/molecules/steps/SecretInputCard.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
import React, { useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import {
Text,
PasswordInput,
Box,
Button,
FileButton,
JsonInput,
PasswordInput,
SegmentedControl,
Text,
useMantineTheme,
Box,
} from '@mantine/core';
import StepCard from './StepCard';
import { useDropContext } from 'contexts/DropContext';
import type { PayloadInputMode } from '@shared/types/common';
import { Captcha } from 'atoms/Captcha';
import {
ACCEPTED_FILE_TYPES,
MAX_PAYLOAD_SIZE,
} from '@shared/config/files';
import type { PayloadInputMode } from '@shared/types/common';
import { Captcha } from 'atoms/Captcha';
import { useDropContext } from 'contexts/DropContext';
import { CONFIRM_PAYLOAD_BTN_ID } from 'lib/constants';
import Cookies from 'js-cookie';
import { DISABLE_CAPTCHA_COOKIE } from 'config/cookies';
import { useUser } from '@clerk/nextjs';
import StepCard from './StepCard';

export const SecretInputCard = () => {
const [mode, setMode] = useState<PayloadInputMode>('text');
Expand All @@ -29,7 +26,6 @@ export const SecretInputCard = () => {
const [canConfirm, setCanConfirm] = useState(
process.env.NODE_ENV === 'development',
);
const { user } = useUser();

const textRef = useRef<HTMLInputElement>(null);
const jsonRef = useRef<HTMLTextAreaElement>(null);
Expand All @@ -38,20 +34,6 @@ export const SecretInputCard = () => {

const { setPayload } = useDropContext();

useEffect(() => {
if (user) setCanConfirm(true);
else setCanConfirm(false);
}, [user]);

useEffect(() => {
const disableCaptcha = Cookies.get(DISABLE_CAPTCHA_COOKIE);

if (disableCaptcha) {
setCanConfirm(true);
setIsValid(true);
}
}, []);

const isValidJson = (input: string) => {
try {
JSON.parse(input);
Expand Down
4 changes: 1 addition & 3 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"build": "next build",
"start:prod": "next build && next start",
"test:e2e": "playwright test",
"posttest:e2e": "tsx ./tests/scripts/clearTestToken.ts",
"postversion": "git add ./package.json && git push --follow-tags"
},
"dependencies": {
"@auth0/nextjs-auth0": "^2.6.3",
"@clerk/nextjs": "^5.6.0",
"@clerk/themes": "^2.1.36",
"@emotion/react": "^11.10.0",
Expand All @@ -34,7 +34,6 @@
"@tabler/icons": "^1.119.0",
"@vercel/analytics": "^1.3.1",
"@vercel/edge-config": "^1.3.0",
"auth0": "^3.5.0",
"cookies-next": "^2.0.4",
"cors": "^2.8.5",
"eslint-config-next": "^14.2.13",
Expand All @@ -58,7 +57,6 @@
"@next/eslint-plugin-next": "^13.1.1",
"@playwright/test": "^1.36.2",
"@testing-library/react-hooks": "^8.0.1",
"@types/auth0": "^3.3.3",
"@types/cors": "^2.8.13",
"@types/js-cookie": "^3.0.2",
"@types/mdx": "^2.0.13",
Expand Down
6 changes: 0 additions & 6 deletions web/pages/api/auth/[auth0].ts

This file was deleted.

19 changes: 15 additions & 4 deletions web/pages/api/captcha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { runMiddleware } from 'api/middleware';
import { cors } from 'api/middleware/cors';
import { verifyCaptcha } from 'api/captcha';
import { TEST_TOKEN_COOKIE } from '@shared/config/http';
import { verifyTestToken } from 'tests/e2e/util';

export default async function verifyCatpcha(
req: NextApiRequest,
Expand All @@ -17,9 +19,18 @@ export default async function verifyCatpcha(

const { token } = req.body;

const isValid = await verifyCaptcha(token);
let status = 400,
success = false;

return isValid
? res.status(200).send({ success: true })
: res.status(400).send({ success: false });
if (token && req.cookies[TEST_TOKEN_COOKIE]) {
const testToken = req.cookies[TEST_TOKEN_COOKIE];

success = await verifyTestToken(testToken);
} else {
success = await verifyCaptcha(token);
}

if (success) status = 200;

return res.status(status).send({ success });
}
Loading

0 comments on commit e93adda

Please sign in to comment.