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:
-
Create a new project folder by executing the following command in the terminal:
-
Initialize a
package.jsonfile using the correct command for your package manager: -
Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:
-
Create a
tsconfig.jsonfile in the root of your project and paste the following configuration: -
Initialize the
srcdirectory:
-
Dependencies¶
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.
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:
// 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:
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:
// 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:
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
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¶
| Created: October 17, 2025