-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathverify-token.ts
More file actions
171 lines (143 loc) · 5.5 KB
/
Copy pathverify-token.ts
File metadata and controls
171 lines (143 loc) · 5.5 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import { NextApiRequest, NextApiResponse } from 'next';
import { config } from '../../lib/config';
import prisma from '../../lib/prisma';
import { validateTraits } from '../../lib/trait-validator';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { signature, message, code, codeVerifier } = req.body;
// Validate required parameters
if (!signature || !message) {
return res.status(400).json({
error: 'Missing required parameters: signature and message'
});
}
// Define expected trait requirements for X verification
// This MUST match what the frontend generates
const expectedTraits = {
'verified': 'true'
};
// Validate that the message contains the expected trait requirements
const validation = validateTraits(message, 'x', expectedTraits);
if (!validation.valid) {
console.error('Trait validation failed:', validation.error);
console.error('Expected traits:', validation.expectedTraits);
console.error('Found traits:', validation.foundTraits);
return res.status(400).json({
error: 'Invalid trait requirements in message',
details: validation.error
});
}
const requestBody: any = {
signature,
message
};
if (code && codeVerifier) {
requestBody.code = code;
requestBody.code_verifier = codeVerifier;
}
// Call the base-verify-api to verify the signature
const uri = `${config.baseVerifyApiUrl}/base_verify_token`
const verifyResponse = await fetch(uri, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.baseVerifySecretKey}`
},
body: JSON.stringify(requestBody)
});
const responseBody = await verifyResponse.clone().text();
console.log('Request URI:', uri);
console.log('Base verify API response body:', responseBody);
if (!verifyResponse.ok) {
console.error('Base verify API error:', verifyResponse.status, verifyResponse.statusText);
// Handle specific case where Twitter account verification traits are not satisfied
if (verifyResponse.status === 400) {
try {
const errorData = JSON.parse(responseBody);
if (errorData.message === 'verification_traits_not_satisfied') {
return res.status(412).json({
error: 'X account does not satisfy verification requirements.'
});
}
} catch (parseError) {
console.error('Error parsing response body:', parseError);
}
}
return res.status(verifyResponse.status).json({
error: 'Failed to verify signature with base-verify-api'
});
}
// Get the verification data from the response
const verificationData = await verifyResponse.json();
// Extract the base verify token from response (support a few possible shapes)
const baseVerifyToken: string | undefined =
verificationData?.token ||
verificationData?.data?.token;
if (!baseVerifyToken || typeof baseVerifyToken !== 'string') {
return res.status(500).json({
error: 'Verification token missing from Base Verify response'
});
}
// Store the verification data in the database
try {
// Extract user data from verification response
const addressMatch = message.match(/0x[a-fA-F0-9]{40}/);
const walletAddress = addressMatch ? addressMatch[0].toLowerCase() : '';
console.log('Extracted wallet address:', walletAddress);
if (!walletAddress) {
console.error('Failed to extract wallet address from message:', message);
return res.status(400).json({
error: 'Could not extract wallet address from message'
});
}
console.log('Attempting to store user in database:', walletAddress);
// Enforce uniqueness of the base verify token before writing
const existingByToken = await prisma.verifiedUser.findUnique({
where: { baseVerifyToken }
});
if (existingByToken) {
return res.status(409).json({
error: 'This verification token has already been used.'
});
}
const result = await prisma.verifiedUser.upsert({
where: { address: walletAddress },
update: {
updatedAt: new Date(),
baseVerifyToken
},
create: {
address: walletAddress,
updatedAt: new Date(),
baseVerifyToken
}
});
console.log('Database operation successful:', result);
} catch (dbError) {
console.error('Error storing verification in database:', dbError);
console.error('Database error details:', {
message: (dbError as Error).message,
stack: (dbError as Error).stack
});
// Fail the request if database storage fails - this is important for verification integrity
return res.status(500).json({
error: 'Failed to store verification in database',
details: process.env.NODE_ENV === 'development' ? (dbError as Error).message : undefined
});
}
// Return 200 OK with the verification data
return res.status(200).json({
success: true,
verification: verificationData,
message: 'Airdrop claimed successfully'
});
} catch (error) {
console.error('Error in verify-token route:', error);
return res.status(500).json({
error: 'Internal server error'
});
}
}