> For the complete documentation index, see [llms.txt](https://docs.heyhal.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.heyhal.xyz/api-documentation/verify-signature.md).

# Verify Signature

**Endpoint:** <mark style="color:yellow;">`POST /api/v1/auth/verify-signature`</mark>

### **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**

| Parameter     | Type   | Required | Description                                 |
| ------------- | ------ | -------- | ------------------------------------------- |
| 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

`{`

&#x20; `"token": "eyJhbGciOiJIUzI1NiIs..."  // JWT token valid for 24 hours`

`}`

### Error Responses

Invalid Signature

Status Code: 400 Bad Request

`{`

&#x20; `"error": "Verification failed. Invalid wallet address or signature."`

`}`

Server Error

Status Code: 500 Internal Server Error

`{`

&#x20; `"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.`

&#x20; **`// 1. Generate a message`**

&#x20; ``const message = `Sign this message to authenticate with Pump Fun: ${Date.now()}`;``

&#x20;&#x20;

&#x20; `try {`

**`2. Request signature from wallet`**

&#x20;   `const encodedMessage = decodeUTF8(message);`

&#x20;   `const signature = await wallet.signMessage(encodedMessage, "utf8");`

&#x20;  &#x20;

&#x20; **3. Send to verification endpoint**

&#x20;`const response = await fetch('https://api.heyhal.xyz/api/v1/auth/verify-signature', {`

&#x20;     `method: 'POST',`

&#x20;     `headers: {`

&#x20;       `'Content-Type': 'application/json',`

&#x20;    `},`

&#x20;     `body: JSON.stringify({`

&#x20;       `walletAddress: wallet.publicKey.toString(),`

&#x20;       `signature: bs58.encode(signature),`

&#x20;       `message: message`

&#x20;     `})`

&#x20;   `});`

&#x20;   `if (!response.ok) {`

&#x20;     ``throw new Error(`HTTP error! status: ${response.status}`);``

&#x20;   `}`

&#x20;   `const { token } = await response.json();`

&#x20;  &#x20;

&#x20;   // Store the JWT token for future authenticated requests

&#x20;   `localStorage.setItem('auth_token', token);`

&#x20;  &#x20;

&#x20;   `return token;`

&#x20; `} catch (error) {`

&#x20;   `console.error('Authentication error:', error);`

&#x20;   `throw error;`

&#x20; `}`

`}`

**// Example with Phantom Wallet**

`async function authenticateWithPhantom() {`

&#x20; `try {`

&#x20;   `// Check if Phantom is installed`

&#x20;   `const { solana } = window as any;`

&#x20;  &#x20;

&#x20;   `if (!solana?.isPhantom) {`

&#x20;     `throw new Error('Phantom wallet is not installed!');`

&#x20;   `}`

&#x20;  &#x20;

&#x20; **Connect to wallet**

&#x20; `const response = await solana.connect();`

&#x20;   `const wallet = response.publicKey;`

&#x20;  &#x20;

&#x20; **Sign and verify**

&#x20;  `const token = await signAndVerify(solana);`

&#x20;   `console.log('Authentication successful!', token);`

&#x20;  &#x20;

&#x20;   `return token;`

&#x20; `} catch (error) {`

&#x20;   `console.error('Failed to authenticate:', error);`

&#x20;   `throw error;`

&#x20; `}`

`}`

### 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

<br>

**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

<br>

**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

<br>
