Skip to content

Authenticate with SIWE and JWT

This guide shows how to sign in to a StorageHub Main Storage Provider (MSP) using Sign-In with Ethereum (SIWE, EIP-4361) and maintain a session with short-lived JSON Web Tokens (JWTs). The MSP verifies a wallet-signed challenge, issues a JWT for subsequent requests, and resolves an ENS profile where available. The StorageHub SDK wraps this flow so you can check auth status, complete login, and fetch the authenticated profile with a few calls.

Prerequisites

  • Node.js v22+ installed
  • A TypeScript project

    Need a starter project?

    If you don't have an existing project, follow these steps to create a TypeScript project you can use to follow the guides in this section:

    1. Create a new project folder by executing the following command in the terminal:

      mkdir datahaven-project && cd datahaven-project
      
    2. Initialize a package.json file using the correct command for your package manager:

      pnpm init
      
      yarn init
      
      npm init --y
      
    3. Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:

      pnpm add -D typescript ts-node @types/node
      
      yarn add -D typescript ts-node @types/node
      
      npm install -D typescript ts-node @types/node
      
    4. Create a tsconfig.json file in the root of your project and paste the following configuration:

      tsconfig.json
      {
          "compilerOptions": {
              "target": "ES2022",
              "module": "nodenext",
              "moduleResolution": "NodeNext",
              "esModuleInterop": true,
              "strict": true,
              "skipLibCheck": true,
              "outDir": "dist",
              "declaration": true,
              "sourceMap": true
          },
          "include": ["src/**/*.ts"]
      }
      
    5. Initialize the src directory:

      mkdir src && touch src/index.ts
      

Dependencies

pnpm add @storagehub-sdk/core @storagehub-sdk/msp-client @storagehub/types-bundle @polkadot/api @storagehub/api-augment viem
yarn add @storagehub-sdk/core @storagehub-sdk/msp-client @storagehub/types-bundle @polkadot/api @storagehub/api-augment viem
npm install @storagehub-sdk/core @storagehub-sdk/msp-client @storagehub/types-bundle @polkadot/api @storagehub/api-augment viem

Initialize Clients

First, you'll need to set up the necessary clients to connect to the DataHaven network, which runs on a dual-protocol architecture (Substrate for core logic and EVM for compatibility).

If you've already followed the Create a Bucket guide, your clients may already be initialized. Review the placeholders at the bottom of the following snippet to see where you'll add logic in this guide, then skip ahead to Authenticate Your Address via MSP.

Create an index.ts and add the following code:

Note

The code below uses DataHaven Testnet configuration values, which include the Chain ID, RPC URL, WSS URL, MSP URL, and token metadata. If you’re running a local devnet, make sure to replace these with your local configuration parameters. You can find all the relevant local devnet values in the Starter Kit.

index.ts
import '@storagehub/api-augment';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { types } from '@storagehub/types-bundle';
import { HttpClientConfig, initWasm } from '@storagehub-sdk/core';
import {
  AuthStatus,
  HealthStatus,
  MspClient,
  UserInfo,
} from '@storagehub-sdk/msp-client';
import {
  Chain,
  WalletClient,
  createWalletClient,
  defineChain,
  http,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

async function run() {
  // For anything from @storagehub-sdk/core to work, initWasm() is required
  // on top of the file
  await initWasm();

  // --- viem setup ---
  // Define DataHaven chain, as expected by viem
  const chain: Chain = defineChain({
    id: 55931,
    name: 'DataHaven Testnet',
    nativeCurrency: { name: 'Mock', symbol: 'MOCK', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://services.datahaven-testnet.network/testnet'] },
    },
  });

  // Define account from a private key
  const account = privateKeyToAccount('INSERT_PRIVATE_KEY' as `0x${string}`);
  const address = account.address;

  // Create a wallet client using the defined chain, account, and RPC URL
  const walletClient: WalletClient = createWalletClient({
    chain,
    account,
    transport: http('https://services.datahaven-testnet.network/testnet'),
  });

  // --- Polkadot.js API setup ---
  const provider = new WsProvider(
    'wss://services.datahaven-testnet.network/testnet'
  );
  const polkadotApi: ApiPromise = await ApiPromise.create({
    provider,
    typesBundle: types,
    noInitWarn: true,
  });

  // --- Connect to MSP Client ---
  // Base URL of the MSP backend you want to interact with.
  const baseUrl = 'https://deo-dh-backend.testnet.datahaven-infra.network/';

  // Configuration for the HTTP client used by the SDK internally.
  const httpConfig: HttpClientConfig = { baseUrl: baseUrl };

  // A temporary authentication token obtained after Sign-In with Ethereum (SIWE).
  // If not yet authenticated, this will remain undefined and the client will operate in read-only mode.
  // Authentication is not always required, but is needed for other operations like file uploads and bucket management.
  let sessionToken: string | undefined = undefined;

  // Provides the SDK with session data when available.
  // This callback is automatically invoked by the MSP Client whenever it needs to authenticate a request.
  const sessionProvider = async () =>
    sessionToken
      ? ({ token: sessionToken, user: { address: address } } as const)
      : undefined;

  // Create an instance of the MSP Client and establish connection with the backend.
  const mspClient = await MspClient.connect(httpConfig, sessionProvider);

  // Check MSP Health Status
  const mspHealth: HealthStatus = await mspClient.info.getHealth();
  console.log('MSP service health:', mspHealth);

  // --- Issue storage request logic ---
  // **PLACEHOLDER FOR STEP 1: AUTHENTICATE YOUR ADDRESS VIA MSP**
  // **PLACEHOLDER FOR STEP 2: GET PROFILE INFO FROM MSP**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Authenticate Your Address via MSP

To check your address's authentication status and to authenticate your address using the MSP Client, add the following code:

// **PLACEHOLDER FOR STEP 1: AUTHENTICATE YOUR ADDRESS VIA MSP**
// Trigger the SIWE (Sign-In with Ethereum) flow to get authenticated.
// The SIWE method prompts the connected wallet to sign an EIP-4361 message,
// which the MSP backend verifies to issue a JWT session token
const siweSession = await mspClient.auth.SIWE(walletClient);
console.log('SIWE Session:', siweSession);
// Store the obtained session token for future authenticated requests
sessionToken = (siweSession as { token: string }).token;

Run the script:

ts-node index.ts

The SIWE session response should return a response like this:

Note

The ENS name is hardcoded currently.

SIWE Session: {
  token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1MiJ9.eyJhZGRyZXNzIjoiMHgwMEZBMzVEODRhNDNkYjc1NDY3RDJCMmMxZWQ4OTc0YUNBNTcyMjNlIiwiZXhwIjoxNzYyMzY0NDI5LCJpYXQiOjE3NjIzNDY0Mjl9.P24QlbiCFT1RO55xA3uvyNu4B4cGiApFerZsYif20K0',
  user: {
    address: '0x00FA35D84a43db74467D2B2c1ed8974aCA57223e',
    ens: 'user.eth'
  }
}

Get Profile Info from MSP

After authentication, to get the authenticated profile info from an MSP, add the following code:

// **PLACEHOLDER FOR STEP 2: GET PROFILE INFO FROM MSP**
// Retrieve and log the authenticated user's profile.
// This includes wallet address and, if available, ENS name
const profile: UserInfo = await mspClient.auth.getProfile();
console.log('Authenticated user profile:', profile);

Run the script:

ts-node index.ts

The response should return a response like this:

Note

The ENS name is hardcoded currently.

Authenticated user profile: {
  address: '0x00DA35D84a73db75462D2B2c1ed8974aAA57223e',
  ens: 'user.eth'
}
View complete script
index.ts
import '@storagehub/api-augment';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { types } from '@storagehub/types-bundle';
import { HttpClientConfig, initWasm } from '@storagehub-sdk/core';
import {
  AuthStatus,
  HealthStatus,
  MspClient,
  UserInfo,
} from '@storagehub-sdk/msp-client';
import {
  Chain,
  WalletClient,
  createWalletClient,
  defineChain,
  http,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

async function run() {
  // For anything from @storagehub-sdk/core to work, initWasm() is required
  // on top of the file
  await initWasm();

  // --- viem setup ---
  // Define DataHaven chain, as expected by viem
  const chain: Chain = defineChain({
    id: 55931,
    name: 'DataHaven Testnet',
    nativeCurrency: { name: 'Mock', symbol: 'MOCK', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://services.datahaven-testnet.network/testnet'] },
    },
  });

  // Define account from a private key
  const account = privateKeyToAccount('INSERT_PRIVATE_KEY' as `0x${string}`);
  const address = account.address;

  // Create a wallet client using the defined chain, account, and RPC URL
  const walletClient: WalletClient = createWalletClient({
    chain,
    account,
    transport: http('https://services.datahaven-testnet.network/testnet'),
  });

  // --- Polkadot.js API setup ---
  const provider = new WsProvider(
    'wss://services.datahaven-testnet.network/testnet'
  );
  const polkadotApi: ApiPromise = await ApiPromise.create({
    provider,
    typesBundle: types,
    noInitWarn: true,
  });

  // --- Connect to MSP Client ---
  // Base URL of the MSP backend you want to interact with.
  const baseUrl = 'https://deo-dh-backend.testnet.datahaven-infra.network/';

  // Configuration for the HTTP client used by the SDK internally.
  const httpConfig: HttpClientConfig = { baseUrl: baseUrl };

  // A temporary authentication token obtained after Sign-In with Ethereum (SIWE).
  // If not yet authenticated, this will remain undefined and the client will operate in read-only mode.
  // Authentication is not always required, but is needed for other operations like file uploads and bucket management.
  let sessionToken: string | undefined = undefined;

  // Provides the SDK with session data when available.
  // This callback is automatically invoked by the MSP Client whenever it needs to authenticate a request.
  const sessionProvider = async () =>
    sessionToken
      ? ({ token: sessionToken, user: { address: address } } as const)
      : undefined;

  // Create an instance of the MSP Client and establish connection with the backend.
  const mspClient = await MspClient.connect(httpConfig, sessionProvider);

  // Check MSP Health Status
  const mspHealth: HealthStatus = await mspClient.info.getHealth();
  console.log('MSP service health:', mspHealth);

  // --- Authenticate via SIWE and JWT logic ---

  // Trigger the SIWE (Sign-In with Ethereum) flow to get authenticated.
  // The SIWE method prompts the connected wallet to sign an EIP-4361 message,
  // which the MSP backend verifies to issue a JWT session token
  const siweSession = await mspClient.auth.SIWE(walletClient);
  console.log('SIWE Session:', siweSession);
  // Store the obtained session token for future authenticated requests
  sessionToken = (siweSession as { token: string }).token;


  // Retrieve and log the authenticated user's profile.
  // This includes wallet address and, if available, ENS name
  const profile: UserInfo = await mspClient.auth.getProfile();
  console.log('Authenticated user profile:', profile);

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Next Steps

Last update: November 11, 2025
| Created: October 17, 2025