Skip to content

Commit 30045e8

Browse files
authored
Merge pull request #4 from Sparths/Dev
Security Fixes
2 parents b666244 + ae94a42 commit 30045e8

File tree

29 files changed

+3486
-819
lines changed

29 files changed

+3486
-819
lines changed

.env.local.backup

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
POSTGRES_URL="postgres://postgres.hxijjvnsrxynegthasmh:pcE680j67rhFfDRr@aws-0-eu-central-1.pooler.supabase.com:6543/postgres?sslmode=require&supa=base-pooler.x"
2+
POSTGRES_USER="postgres"
3+
POSTGRES_HOST="db.hxijjvnsrxynegthasmh.supabase.co"
4+
SUPABASE_JWT_SECRET="998Vo+CP+u2rNnH6fv9w4KWGb+8I1P9bXjrM2tKiFE5YcIlQ1Tr+NBNPNGw1de2drhvoXX0dSZJZ84toPkxBtA=="
5+
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh4aWpqdm5zcnh5bmVndGhhc21oIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDc3Mzk5NDcsImV4cCI6MjA2MzMxNTk0N30.zWILyiJA1K5L0w2rynsWqhMVU6jySaal5lC9IBO05bc"
6+
POSTGRES_PRISMA_URL="postgres://postgres.hxijjvnsrxynegthasmh:pcE680j67rhFfDRr@aws-0-eu-central-1.pooler.supabase.com:6543/postgres?sslmode=require&supa=base-pooler.x"
7+
POSTGRES_PASSWORD="pcE680j67rhFfDRr"
8+
POSTGRES_DATABASE="postgres"
9+
SUPABASE_URL="https://hxijjvnsrxynegthasmh.supabase.co"
10+
NEXT_PUBLIC_SUPABASE_URL="https://hxijjvnsrxynegthasmh.supabase.co"
11+
SUPABASE_SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh4aWpqdm5zcnh5bmVndGhhc21oIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0NzczOTk0NywiZXhwIjoyMDYzMzE1OTQ3fQ.poV3TJW7eJULdQ_yYHhygUPbEMYW-SSrIVWSsnRXRAk"
12+
POSTGRES_URL_NON_POOLING="postgres://postgres.hxijjvnsrxynegthasmh:pcE680j67rhFfDRr@aws-0-eu-central-1.pooler.supabase.com:5432/postgres?sslmode=require"
13+
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/1326548524657016842/rJrqRnK7lZQKKh_kzT2bz_rjBx6T0BDz8vym4SVeMt-sLXl4PaMycrYuM8pbbr1JNpSm"
14+
NEXT_PUBLIC_SITE_URL = "https://orange-engine-q6gpjxwwqg9h9pw5-3001.app.github.dev"
15+
16+
AUTHORIZED_ADMINS=Sparths,sparths,f8adc96a-496f-412b-af15-20bd3cd66b3c
17+
NEXT_PUBLIC_AUTHORIZED_ADMINS=Sparths,sparths

app/api/admin/verify/route.tsx

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { NextResponse } from "next/server";
22
import { createClient } from '@supabase/supabase-js';
3+
import { createSecureAdminToken, verifySecureAdminToken } from '@/lib/security/secure-token';
4+
import { sanitizeInput } from '@/lib/security/sanitization';
35

46
// Admin verification with service role
57
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
@@ -19,11 +21,6 @@ const getAuthorizedAdmins = (): string[] => {
1921
return adminList.split(',').map(admin => admin.trim());
2022
};
2123

22-
// Input sanitization
23-
const sanitizeInput = (input: string): string => {
24-
return input.replace(/[<>]/g, '').trim();
25-
};
26-
2724
export async function POST(request: Request) {
2825
try {
2926
const body = await request.json();
@@ -52,15 +49,8 @@ export async function POST(request: Request) {
5249
if (authorizedAdmins.includes(sanitizedUserId)) {
5350
console.log("User found in direct admin list:", sanitizedUserId);
5451

55-
// Create admin session token for subsequent requests
56-
const adminSession = {
57-
userId: sanitizedUserId,
58-
timestamp: Date.now(),
59-
verified: true,
60-
isAdmin: true
61-
};
62-
63-
const adminToken = Buffer.from(JSON.stringify(adminSession)).toString('base64');
52+
// Create secure admin session token
53+
const adminToken = createSecureAdminToken(sanitizedUserId);
6454

6555
return NextResponse.json({
6656
success: true,
@@ -108,15 +98,8 @@ export async function POST(request: Request) {
10898
if (isAdmin) {
10999
console.log("User verified as admin");
110100

111-
// Create admin session token for subsequent requests
112-
const adminSession = {
113-
userId: sanitizedUserId,
114-
timestamp: Date.now(),
115-
verified: true,
116-
isAdmin: true
117-
};
118-
119-
const adminToken = Buffer.from(JSON.stringify(adminSession)).toString('base64');
101+
// Create secure admin session token
102+
const adminToken = createSecureAdminToken(sanitizedUserId);
120103

121104
return NextResponse.json({
122105
success: true,
@@ -160,12 +143,26 @@ export async function POST(request: Request) {
160143
}
161144
}
162145

163-
// Optional: GET method to check current admin status
146+
// GET method to check current admin status
164147
export async function GET(request: Request) {
165148
try {
166149
const { searchParams } = new URL(request.url);
167150
const userId = searchParams.get("userId");
168151

152+
// Check for admin token in Authorization header
153+
const authHeader = request.headers.get('authorization');
154+
if (authHeader && authHeader.startsWith('Bearer ')) {
155+
const token = authHeader.substring(7);
156+
const verification = verifySecureAdminToken(token);
157+
158+
if (verification.isValid) {
159+
return NextResponse.json({
160+
isAdmin: true,
161+
userId: verification.userId
162+
});
163+
}
164+
}
165+
169166
if (!userId) {
170167
return NextResponse.json(
171168
{ error: "User ID is required" },

app/api/auth/csrf/route.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// =================================
2+
// app/api/auth/csrf/route.tsx
3+
// =================================
4+
5+
import { NextResponse } from 'next/server';
6+
import { generateCSRFToken } from '@/lib/security/csrf-protection';
7+
import { rateLimit, createRateLimitHeaders } from "@/lib/security/rate-limiter-config";
8+
9+
// GET: Get CSRF token
10+
export async function GET(request: Request) {
11+
try {
12+
// Apply rate limiting
13+
const rateLimitResult = await rateLimit(request, 'default');
14+
if (!rateLimitResult.success) {
15+
return new NextResponse(
16+
JSON.stringify({ error: 'Too many requests. Please try again later.' }),
17+
{
18+
status: 429,
19+
headers: {
20+
'Content-Type': 'application/json',
21+
...createRateLimitHeaders(rateLimitResult),
22+
}
23+
}
24+
);
25+
}
26+
27+
const token = generateCSRFToken();
28+
29+
return NextResponse.json(
30+
{ token },
31+
{ headers: createRateLimitHeaders(rateLimitResult) }
32+
);
33+
} catch (error) {
34+
console.error("Error generating CSRF token:", error);
35+
return NextResponse.json(
36+
{ error: "Failed to generate CSRF token" },
37+
{ status: 500 }
38+
);
39+
}
40+
}

app/api/auth/session/route.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// =================================
2+
// app/api/auth/session/route.tsx
3+
// =================================
4+
5+
import { NextResponse } from 'next/server';
6+
import { rateLimit, createRateLimitHeaders } from "@/lib/security/rate-limiter-config";
7+
import supabase from '@/lib/supabase';
8+
9+
// GET: Get current session information
10+
export async function GET(request: Request) {
11+
try {
12+
// Apply rate limiting
13+
const rateLimitResult = await rateLimit(request, 'default');
14+
if (!rateLimitResult.success) {
15+
return new NextResponse(
16+
JSON.stringify({ error: 'Too many requests. Please try again later.' }),
17+
{
18+
status: 429,
19+
headers: {
20+
'Content-Type': 'application/json',
21+
...createRateLimitHeaders(rateLimitResult),
22+
}
23+
}
24+
);
25+
}
26+
27+
// Get session from Supabase Auth
28+
const authHeader = request.headers.get('authorization');
29+
if (!authHeader) {
30+
return NextResponse.json(
31+
{ session: null },
32+
{ headers: createRateLimitHeaders(rateLimitResult) }
33+
);
34+
}
35+
36+
// For now, return a basic response
37+
// In a full implementation, you would verify the session token
38+
return NextResponse.json(
39+
{ session: null },
40+
{ headers: createRateLimitHeaders(rateLimitResult) }
41+
);
42+
} catch (error) {
43+
console.error("Error getting session:", error);
44+
return NextResponse.json(
45+
{ error: "Failed to get session" },
46+
{ status: 500 }
47+
);
48+
}
49+
}
50+
51+
// POST: Create a new session (login)
52+
export async function POST(request: Request) {
53+
try {
54+
// Apply rate limiting
55+
const rateLimitResult = await rateLimit(request, 'users_login');
56+
if (!rateLimitResult.success) {
57+
return new NextResponse(
58+
JSON.stringify({ error: 'Too many login attempts. Please try again later.' }),
59+
{
60+
status: 429,
61+
headers: {
62+
'Content-Type': 'application/json',
63+
...createRateLimitHeaders(rateLimitResult),
64+
}
65+
}
66+
);
67+
}
68+
69+
// This would handle session creation
70+
// For now, redirect to the main users login endpoint
71+
return NextResponse.json(
72+
{ message: "Use /api/users?action=login for authentication" },
73+
{
74+
status: 400,
75+
headers: createRateLimitHeaders(rateLimitResult)
76+
}
77+
);
78+
} catch (error) {
79+
console.error("Error creating session:", error);
80+
return NextResponse.json(
81+
{ error: "Failed to create session" },
82+
{ status: 500 }
83+
);
84+
}
85+
}
86+
87+
// DELETE: Logout/destroy session
88+
export async function DELETE(request: Request) {
89+
try {
90+
// Apply rate limiting
91+
const rateLimitResult = await rateLimit(request, 'default');
92+
if (!rateLimitResult.success) {
93+
return new NextResponse(
94+
JSON.stringify({ error: 'Too many requests. Please try again later.' }),
95+
{
96+
status: 429,
97+
headers: {
98+
'Content-Type': 'application/json',
99+
...createRateLimitHeaders(rateLimitResult),
100+
}
101+
}
102+
);
103+
}
104+
105+
// Handle logout logic here
106+
return NextResponse.json(
107+
{ success: true },
108+
{ headers: createRateLimitHeaders(rateLimitResult) }
109+
);
110+
} catch (error) {
111+
console.error("Error destroying session:", error);
112+
return NextResponse.json(
113+
{ error: "Failed to destroy session" },
114+
{ status: 500 }
115+
);
116+
}
117+
}

0 commit comments

Comments
 (0)