-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxy.ts
More file actions
127 lines (104 loc) · 3.28 KB
/
Copy pathproxy.ts
File metadata and controls
127 lines (104 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { markketplace } from './markket/config';
/**
* Routes that require authentication
* @type {string[]}
*/
const PROTECTED_ROUTES: string[] = [
'/api/stripe/connect',
];
/**
* Next.js middleware to protect certain routes,
* It verifies the JWT token from the request headers
* and checks if the user is authorized to access the requested store
* @param request
* @returns
*/
export async function proxy(request: NextRequest) {
if (request.headers.get('x-middleware-subrequest')) {
return NextResponse.json(
{ error: 'Subrequest not allowed' },
{ status: 400 }
);
}
const isProtectedRoute = PROTECTED_ROUTES.some(route =>
request.nextUrl.pathname.startsWith(route)
);
if (!isProtectedRoute) {
console.warn('middleware:bypass:', request.nextUrl.pathname);
return NextResponse.next();
}
/** read token from request to verify against Strapi */
const token = request.headers.get('authorization')?.replace('Bearer ', '');
if (!token) {
return NextResponse.json(
{ error: 'Authentication required' },
{ status: 401 }
);
}
try {
const verifyResponse = await fetch(new URL(`/api/users/me`, markketplace.api), {
headers: {
'Authorization': `Bearer ${token}`,
},
});
if (!verifyResponse.ok) {
console.warn('middleware:token:verification:', verifyResponse.status);
return NextResponse.json(
{ error: 'Invalid token' },
{ status: 401 }
);
}
const userData = await verifyResponse.json();
const body = await request.json();
const storeId = body.store;
// for stores, check that the user exists in the store
if (storeId || request.nextUrl.pathname.includes('/api/stripe/connect')) {
if (storeId) {
const url = new URL(`/api/stores/${storeId}?populate=users`, markketplace.api);
const storeResponse = await fetch(url,
{
headers: {
'Authorization': `Bearer ${request.headers.get('Authorization')}`,
},
}
);
const storeData = await storeResponse.json();
console.info('middleware:store:verification:', {
storeResponse: storeResponse.status,
store: storeData?.documentId,
user: userData?.documentId
});
const flattened_user_ids = storeData?.data?.users?.map((user: any) => user.documentId);
if (!flattened_user_ids?.includes(userData.documentId)) {
return NextResponse.json(
{ error: 'Not authorized for this store' },
{ status: 403 }
);
}
}
}
// Add user info to request headers for downstream use
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-user-id', userData.id);
requestHeaders.set('x-user-email', userData.email);
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
} catch (error) {
console.error('Auth middleware error:', error);
return NextResponse.json(
{ error: 'Authentication failed' },
{ status: 500 }
);
}
}
/** paths that use this middleware */
export const config = {
matcher: [
'/api/stripe/connect/:path*',
],
};