SIWE Authentication
Sign-In with Ethereum (SIWE, EIP-4361) lets users authenticate by signing a message with their wallet instead of using passwords. Nethereum integrates SIWE directly with Blazor's AuthenticationStateProvider so you can use standard <AuthorizeView> and [Authorize] attributes.
Authentication Flow
- User connects wallet (see Wallet Connection)
- Server generates a SIWE message with a nonce
- User signs the message in their wallet
- Server verifies the signature and creates a session (JWT or server-side)
- User gains
EthereumConnectedandSiweAuthenticatedroles
Two Roles, Two Levels
| Role | Meaning |
|---|---|
EthereumConnected | Wallet is connected (address known, no signature proof) |
SiweAuthenticated | User signed a SIWE message (cryptographic proof of ownership) |
Blazor Server Setup
The server-side approach validates signatures directly without a REST API.
dotnet add package Nethereum.Blazor
dotnet add package Nethereum.Siwe
Register services in Program.cs:
using Nethereum.Blazor;
using Nethereum.Blazor.Siwe;
using Nethereum.Siwe;
using Nethereum.Siwe.Core;
using Nethereum.Siwe.Authentication;
using Nethereum.UI;
using Microsoft.AspNetCore.Components.Authorization;
builder.Services.AddSingleton<ISessionStorage, InMemorySessionNonceStorage>();
builder.Services.AddScoped<SelectedEthereumHostProviderService>();
builder.Services.AddScoped<NethereumSiweAuthenticatorService>();
builder.Services.AddScoped<IAccessTokenService, LocalStorageAccessTokenService>();
builder.Services.AddScoped<IUserService<User>, YourUserService>();
builder.Services.AddScoped<AuthenticationStateProvider,
SiweAuthenticationServerStateProvider<User, SiweMessage>>();
builder.Services.AddAuthorizationCore();
Use in a component:
@inject AuthenticationStateProvider AuthStateProvider
@inject IEthereumHostProvider EthereumProvider
<AuthorizeView Roles="SiweAuthenticated">
<Authorized>
<p>Signed in as @context.User.Identity?.Name</p>
<button @onclick="LogOut">Log Out</button>
</Authorized>
<NotAuthorized>
<button @onclick="SignIn">Sign In with Ethereum</button>
</NotAuthorized>
</AuthorizeView>
@code {
private async Task SignIn()
{
if (AuthStateProvider is SiweAuthenticationServerStateProvider<User, SiweMessage> siweAuth)
{
if (!EthereumProvider.Enabled)
await EthereumProvider.EnableProviderAsync();
await siweAuth.AuthenticateAsync();
}
}
private async Task LogOut()
{
if (AuthStateProvider is SiweAuthenticationServerStateProvider<User, SiweMessage> siweAuth)
{
await siweAuth.LogOutUserAsync();
}
}
}
Blazor WASM + REST API Setup
For WebAssembly apps, the SIWE signature is sent to a REST API that returns a JWT token. The token is stored in localStorage.
REST API (Server)
The API generates nonces and validates signatures. Token issuance (JWT) is application code; GenerateJwt below stands for your own implementation:
using Nethereum.Siwe;
using Nethereum.Siwe.Core;
app.MapGet("/api/siwe/nonce", () =>
{
var nonce = RandomNonceBuilder.GenerateNewNonce();
return Results.Ok(new { nonce });
});
app.MapPost("/api/siwe/verify", async (SiweVerifyRequest request,
ISessionStorage sessionStorage) =>
{
var siweMessage = SiweMessageParser.Parse(request.Message);
var validUser = await new SiweMessageService(sessionStorage)
.IsMessageSignatureValid(siweMessage, request.Signature);
if (!validUser) return Results.Unauthorized();
var token = GenerateJwt(siweMessage.Address);
return Results.Ok(new { token });
});
WASM Client
using Nethereum.Blazor.Siwe;
using Nethereum.Siwe.Authentication;
builder.Services.AddSingleton<IAccessTokenService, LocalStorageAccessTokenService>();
builder.Services.AddAuthorizationCore();
LocalStorageAccessTokenService stores the access token in localStorage. The WASM AuthenticationStateProvider that calls the REST API and restores the state on page reload (SiweAuthenticationWasmStateProvider) is part of the external Nethereum SIWE template, not a Nethereum package.
Protecting Routes
Use standard Blazor authorization attributes:
@page "/dashboard"
@attribute [Authorize(Roles = "SiweAuthenticated")]
<h3>Dashboard</h3>
<p>Only visible to SIWE-authenticated users.</p>
Or inline with <AuthorizeView>:
<AuthorizeView Roles="EthereumConnected">
<Authorized>
<p>Wallet connected: @context.User.FindFirst(
System.Security.Claims.ClaimTypes.NameIdentifier)?.Value</p>
</Authorized>
</AuthorizeView>
<AuthorizeView Roles="SiweAuthenticated">
<Authorized>
<p>Fully authenticated via SIWE.</p>
</Authorized>
</AuthorizeView>
NFT-Gated Access
Implement IEthereumUserService to restrict access based on token holdings:
using Nethereum.Siwe.UserServices;
using Nethereum.UI;
public class NFTGateService : IEthereumUserService
{
private readonly IEthereumHostProvider _provider;
private readonly string _requiredNftContract;
public NFTGateService(IEthereumHostProvider provider, string requiredNftContract)
{
_provider = provider;
_requiredNftContract = requiredNftContract;
}
public async Task<bool> IsUserAddressRegistered(string address)
{
var web3 = await _provider.GetWeb3Async();
var erc721 = web3.Eth.ERC721.GetContractService(_requiredNftContract);
var balance = await erc721.BalanceOfQueryAsync(address);
return balance > 0;
}
}
The built-in ERC721BalanceEthereumUserService does this out of the box -- register it with the contract address:
builder.Services.AddSingleton<IEthereumUserService>(sp =>
new ERC721BalanceEthereumUserService(
"0xYourNFTContractAddress",
"https://your-rpc-url"));
Smart Contract Wallet Support
SiweMessageService.IsMessageSignatureValid supports:
- EOA wallets -- standard
ecRecoversignature verification - ERC-1271 -- on-chain
isValidSignaturefor deployed smart contract wallets - ERC-6492 -- pre-deployment signature validation for counterfactual smart accounts
ERC-1271 and ERC-6492 validation only runs when SiweMessageService is constructed with a web3ForERC1271Validation instance; without it only EOA signatures are verified.
Full Starter Template
A complete SIWE template with REST API, Blazor Server, and Blazor WASM is available:
dotnet new install Nethereum.Templates.Pack
dotnet new nethereum-siwe -o MySiweDapp
The template includes JWT generation, nonce management, MetaMask integration, and role-based authorization out of the box.
Next Steps
- Wallet Connection -- connect wallets before authenticating
- Dynamic Contract Interaction -- interact with contracts as the authenticated user
- Nethereum.Siwe -- SIWE package reference (message building, parsing, verification)