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

# Server Verify

Two core functions. SIWE message generation + signature verification for MOSS wallet auth flows.

**install**

```bash
npm install @megaeth-labs/wallet-server-verify
```

## How It Works

1. **Server** generates challenge via `getMessageToSign()`.
2. **Client** signs the exact message with `mega.signMessage()`.
3. **Server** verifies with `verifySignature()` using the same config.

{% hint style="warning" %}
Always verify server-side before issuing app sessions. Never trust client-only signature checks.
{% endhint %}

## Complete Express.js Example

**server/auth.ts**

```typescript
import express from 'express';
import crypto from 'node:crypto';
import jwt from 'jsonwebtoken';
import {
  getMessageToSign,
  verifySignature,
  Errors,
  type MessageConfig,
  type MessageToConfirm,
  type MessageToSign,
} from '@megaeth-labs/wallet-server-verify';

const app = express();
app.use(express.json());

const config: MessageConfig = {
  scheme: 'https',
  domain: 'yourapp.com',
  uri: 'https://yourapp.com',
  chainId: 4326,
  statement: 'Sign in to YourApp',
};

const challenges = new Map<string, MessageToSign>();

// POST /auth/challenge
app.post('/auth/challenge', (req, res) => {
  const challenge = getMessageToSign(config, req.body.address);
  const challengeId = crypto.randomUUID();

  challenges.set(challengeId, challenge);

  res.json({
    challengeId,
    ...challenge,
  });
});

// POST /auth/verify
app.post('/auth/verify', async (req, res) => {
  const { challengeId, signature } = req.body;
  const stored = challenges.get(challengeId);

  if (!stored) {
    return res.status(400).json({ error: 'UNKNOWN_OR_EXPIRED_CHALLENGE' });
  }

  const signedMessage: MessageToConfirm = {
    ...stored,
    signature,
  };

  try {
    await verifySignature(config, signedMessage);
    challenges.delete(challengeId);

    const token = jwt.sign(
      { address: stored.address },
      process.env.JWT_SECRET as string,
      { expiresIn: '1h' },
    );

    res.json({ ok: true, token });
  } catch (error) {
    if (error instanceof Error) {
      if (error.message === Errors.DIFFERENT_MESSAGE) {
        return res.status(400).json({ error: Errors.DIFFERENT_MESSAGE });
      }
      if (error.message === Errors.INVALID_SIGNATURE) {
        return res.status(401).json({ error: Errors.INVALID_SIGNATURE });
      }
    }

    return res.status(500).json({ error: 'UNKNOWN_ERROR' });
  }
});
```

## Error Handling

| Error               | Meaning                                                              | Typical action                                            |
| ------------------- | -------------------------------------------------------------------- | --------------------------------------------------------- |
| `DIFFERENT_MESSAGE` | Submitted message payload does not match what your server generated. | Reject request and ask client to fetch a fresh challenge. |
| `INVALID_SIGNATURE` | The signature does not match the message/address pair.               | Reject auth attempt.                                      |

## Chain IDs Reference

Use the same chain ID in `MessageConfig` that your app uses for the target environment.

| Network | chainId |
| ------- | ------- |
| Mainnet | `4326`  |
| Testnet | `6343`  |

## Exports and Types

This package exports core methods, typed payloads, and error constants:

| Export                                                 | Purpose                                                                                          |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| `getMessageToSign(config, address)`                    | Create a SIWE message payload plus nonce/issuedAt.                                               |
| `verifySignature(config, messageToConfirm)`            | Validate message integrity and signature via relay-backed verification.                          |
| `Errors`                                               | Error constants: `DIFFERENT_MESSAGE`, `INVALID_SIGNATURE`, `ACCOUNT_NOT_FOUND`, `ACCESS_DENIED`. |
| `MessageConfig` / `MessageToSign` / `MessageToConfirm` | Typed contracts for challenge and verification payloads.                                         |

**types.ts**

```typescript
type MegaETHNetwork = 6343 | 4326;

interface MessageConfig {
  scheme: 'http' | 'https';
  domain: string;
  uri: string;
  chainId: MegaETHNetwork;
  statement: string;
}

interface MessageToSign {
  address: Hex;
  message: string;
  nonce: string;
  issuedAt: Date;
}

interface MessageToConfirm extends MessageToSign {
  signature: Hex;
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.megaeth.com/moss-docs/wallet/server-verify.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
