Verify Signature
Endpoint: POST /api/v1/auth/verify-signature
Description
This endpoint verifies a wallet's signature to authenticate users. It uses Solana's signature verification system and issues a JWT token upon successful verification. The endpoint also manages user records in the database, creating new users or updating login timestamps for existing users.
Authentication Required: No (This is the authentication endpoint)
Type: None
Request Headers
Content-Type: application/json
Body Parameters
walletAddress
string
Yes
The Solana wallet address of the user
signature
string
Yes
The base58-encoded signature of the message
message
string
Yes
The original message that was signed
Example Request Body:
{
"walletAddress": "AaBbCc...",
"signature": "5tBpQ...",
"message": "Sign this message to authenticate with Pump Fun: 1234567890"
}
Response
Success Response
Status Code: 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIs..." // JWT token valid for 24 hours
}
Error Responses
Invalid Signature
Status Code: 400 Bad Request
{
"error": "Verification failed. Invalid wallet address or signature."
}
Server Error
Status Code: 500 Internal Server Error
{
"error": "An internal error occurred. Please try again later."
}
Example Usage
Here's a complete example using JavaScript/TypeScript showing how to sign a message with a Solana wallet and verify it:
import { Connection, PublicKey, Keypair } from "@solana/web3.js";
import bs58 from "bs58";
import nacl from "tweetnacl";
import { encodeUTF8, decodeUTF8 } from "tweetnacl-util";
async function signAndVerify(wallet: any) { // wallet could be Phantom, Solflare, etc.
// 1. Generate a message
const message = `Sign this message to authenticate with Pump Fun: ${Date.now()}`;
try {
// 2. Request signature from wallet
const encodedMessage = decodeUTF8(message);
const signature = await wallet.signMessage(encodedMessage, "utf8");
// 3. Send to verification endpoint
const response = await fetch('https://api.cybers.app/api/v1/auth/verify-signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
walletAddress: wallet.publicKey.toString(),
signature: bs58.encode(signature),
message: message
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { token } = await response.json();
// Store the JWT token for future authenticated requests
localStorage.setItem('auth_token', token);
return token;
} catch (error) {
console.error('Authentication error:', error);
throw error;
}
}
// Example with Phantom Wallet
async function authenticateWithPhantom() {
try {
// Check if Phantom is installed
const { solana } = window as any;
if (!solana?.isPhantom) {
throw new Error('Phantom wallet is not installed!');
}
// Connect to wallet
const response = await solana.connect();
const wallet = response.publicKey;
// Sign and verify
const token = await signAndVerify(solana);
console.log('Authentication successful!', token);
return token;
} catch (error) {
console.error('Failed to authenticate:', error);
throw error;
}
}
Implementation Notes
Signature Verification
The signature is verified using tweetnacl's sign.detached.verify
The signature must be base58 decoded before verification
The wallet address is converted to bytes for verification
JWT Token
The issued JWT token is valid for 24 hours (expiresIn: "1d"
)
The token payload contains the wallet address
Use this token in the Authorization
header for subsequent requests
Database Operations
New users are automatically created in the database
Existing users have their lastLogin
timestamp updated
Initial users are created with an empty username and "user" role
Last updated