ERC-8288 Draft: SIWE-Gated NFT Media URI
Hi everyone,
I would like feedback on a new ERC draft: SIWE-Gated NFT Media URI.
The core use case is simple: a wallet sees private_media_uri in public NFT metadata, asks an
authorized account to sign a SIWE challenge, then displays the returned private image instead of the public preview. Existing clients that do not implement the proposal can keep rendering the
public metadata.
Summary
The proposed metadata addition is one new public metadata field:
{
"name": "Example NFT",
"description": "Public description safe for unauthenticated clients.",
"image": "https://example.com/public-preview.png",
"private_media_uri": "https://media.example.com/eip-private-nft-media/8453/0xabc0000000000000000000000000000000000000/42"
}
ERC-721 tokens would expose this JSON through tokenURI(tokenId). ERC-1155 tokens would expose it through the existing metadata URI returned by uri(id).
private_media_uri is only a discovery pointer. It must be an absolute HTTPS URI, must not include a fragment or embedded userinfo, and must not contain secrets, bearer tokens, account-specific secrets, or private media payloads. Existing clients can ignore it and continue rendering the public image.
EIP
Demo site: https://eip-private-nft-media.vercel.app/
Source / reference implementation: GitHub - Fairmint/eip-private-nft-media · GitHub
Wallet Flow
The primary wallet use case is:
- The wallet reads normal ERC-721/ERC-1155 metadata and renders the public
image. - The wallet sees
private_media_uriand requests it. - The resource server returns
401 UnauthorizedwithWWW-Authenticate: SIWEand a
challenge_uri. - The wallet asks the owner, holder, or another authorized account to sign the SIWE message.
- The wallet retries the protected URI with the signed SIWE proof.
- The resource server verifies the signature and token authorization, then returns private JSON
metadata containingimage. - The wallet renders that private
imageas the unlocked NFT media.
The intent is that wallet providers can implement this once: detect private_media_uri, run the
SIWE unlock flow, and replace the public preview with the private image.
SIWE Binding
The SIWE challenge binds the signature to the exact resource being requested. The challenge message must include the resource server domain, requested private media URI, token chain id, nonce, expiration, and a token-scoped SIWE resources entry:
eip155:{chainId}/{standard}:{contractAddress}/{tokenId}?account={account}&resource={privateMediaUri}
For example:
eip155:8453/erc721:0xabc0000000000000000000000000000000000000/42?account=0x1230000000000000000000000000000000000000&resource=https%3A%2F%2Fmedia.example.com%2Feip-private-nft-media%2F8453%2F0xabc0000000000000000000000000000000000000%2F42
The resource server verifies the SIWE message, signature, nonce, expiration, domain, URI, chain, contract, token id, account, and exact private media URI before serving content. EIP-1271 is used for contract-account signatures.
For ERC-721, the bound account must be the current ownerOf(tokenId). For ERC-1155, the bound account must have balanceOf(account, id) > 0. Resource servers may also support approved operators or delegates if their own content policy treats those relationships as authorization.
Additional Resources
The same exact-resource binding can protect more than the unlocked NFT image. For example, a holder could authorize a third-party site to fetch third-party-view.json without granting access to
private-image.png or every protected resource for the token. The proposal does not define a global delegation registry; it only defines how a protected URI is discovered and how SIWE authorization is scoped to that exact URI.
The draft builds on existing ERC-721/ERC-1155 metadata, SIWE, and EIP-1271 contract-account
signatures. It does not add a new on-chain interface.
I would especially appreciate feedback on whether private_media_uri is clear, whether the SIWE
challenge flow fits wallet and media-client expectations, and whether this is small enough for
wallets, indexers, NFT projects, and media servers to implement consistently.
Thanks for reading.