A Python implementation of Sign in with Ethereum (EIP-4361).
Project description
Sign in with Ethereum
This package provides a Python implementation of EIP-4361: Sign in with Ethereum.
Installation
SIWE can be easily installed in any Python project with pip:
pip install signinwithethereum
The distribution is published as signinwithethereum; the import name is siwe:
from siwe import SiweMessage
Usage
SIWE provides a SiweMessage class which implements EIP-4361.
Parsing a SIWE Message
Parsing is done by initializing a SiweMessage object with an EIP-4361 formatted string:
from siwe import SiweMessage
message = SiweMessage.from_message(message=eip_4361_string)
Or to initialize a SiweMessage as a pydantic.BaseModel right away:
message = SiweMessage(domain="login.xyz", address="0x1234...", ...)
Verifying and Authenticating a SIWE Message
Verification and authentication is performed via EIP-191, using the address field of the SiweMessage as the expected signer. The verify method checks message structural integrity, signature address validity, and time-based validity attributes.
Replay protection relies on a single-use nonce that your server issues, stores alongside the pending session, passes to verify, and consumes on success. Always pass the nonce you issued — a signature verified without a nonce check can be replayed.
try:
message.verify(
signature="0x...",
domain="example.com",
nonce=expected_nonce, # the nonce your server issued for this session
uri="https://example.com/login",
chain_id=1,
strict=True,
)
# Consume the nonce now so it cannot be replayed.
except siwe.VerificationError:
# Invalid
Passing strict=True enforces that domain, uri, chain_id, and nonce are all supplied. Prefer it for authentication flows.
Smart-contract wallet signatures (EIP-1271 / EIP-6492)
For signatures produced by contract wallets (Safe, Argent, etc.) rather than
externally owned accounts, pass a web3 provider to verify. The
authentication arguments (strict, domain, nonce, uri, chain_id) still
apply — the provider only changes how the signature itself is verified:
from web3 import HTTPProvider
message.verify(
signature="0x...",
domain="example.com",
nonce=expected_nonce,
uri="https://example.com/login",
chain_id=1,
strict=True,
provider=HTTPProvider("https://mainnet.infura.io/v3/..."),
)
EOA recovery is tried first; if it fails, the signature is checked on-chain via
isValidSignature(bytes32,bytes) per EIP-1271. Signatures carrying the EIP-6492
magic suffix are handed to the universal off-chain validator bytecode via
eth_call, which covers counterfactual (undeployed) wallets as well as
already-deployed ones. The provider's chain id is checked against the message's
chain_id before any on-chain call.
Note: this is verification only. EIP-6492 allows a verifier to optionally submit the factory transaction after a successful check to finalize on-chain deployment ("side-effectful" verification). This library does not do that — if you need the wallet actually deployed, submit the factory call yourself.
Serialization of a SIWE Message
SiweMessage instances can also be serialized as their EIP-4361 string representations via the prepare_message method:
print(message.prepare_message())
Example
Parsing and verifying a SiweMessage is easy:
try:
message = SiweMessage.from_message(eip_4361_string)
message.verify(
signature,
domain="example.com",
nonce=expected_nonce,
uri="https://example.com/login",
chain_id=1,
strict=True,
)
except ValueError:
# Invalid message format
print("Authentication attempt rejected.")
except siwe.ExpiredMessage:
print("Authentication attempt rejected.")
except siwe.DomainMismatch:
print("Authentication attempt rejected.")
except siwe.NonceMismatch:
print("Authentication attempt rejected.")
except siwe.InvalidSignature:
print("Authentication attempt rejected.")
# Message has been verified. Invalidate the stored nonce, then continue with authorization/other.
Testing
git submodule update --init
uv sync
uv run pytest
See Also
- Sign in with Ethereum: TypeScript
- SIWE website: siwe.xyz
- EIP-4361 Specification Draft
- EIP-191 Specification
Disclaimer
Our Python library for Sign in with Ethereum has not yet undergone a formal security audit. We welcome continued feedback on the usability, architecture, and security of this implementation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file signinwithethereum-5.0.1.tar.gz.
File metadata
- Download URL: signinwithethereum-5.0.1.tar.gz
- Upload date:
- Size: 166.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9661ac2020cb659cc7c3fc1f9b8a9ff59f2018e259f7a4e1ae641f126a475fa3
|
|
| MD5 |
128af5bf6d2fec9f6a46cb85fef4d936
|
|
| BLAKE2b-256 |
c69c5bca1085418726e08c333b4b97891c2d03f12b99973418acf7357dd76a54
|
Provenance
The following attestation bundles were made for signinwithethereum-5.0.1.tar.gz:
Publisher:
publish.yml on signinwithethereum/siwe-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
signinwithethereum-5.0.1.tar.gz -
Subject digest:
9661ac2020cb659cc7c3fc1f9b8a9ff59f2018e259f7a4e1ae641f126a475fa3 - Sigstore transparency entry: 1369314957
- Sigstore integration time:
-
Permalink:
signinwithethereum/siwe-py@dbfc910b51e26cfa8b351f7fb389b5fa454a5d59 -
Branch / Tag:
refs/tags/v5.0.1 - Owner: https://github.com/signinwithethereum
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dbfc910b51e26cfa8b351f7fb389b5fa454a5d59 -
Trigger Event:
release
-
Statement type:
File details
Details for the file signinwithethereum-5.0.1-py3-none-any.whl.
File metadata
- Download URL: signinwithethereum-5.0.1-py3-none-any.whl
- Upload date:
- Size: 19.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53551a1073209f4f011589a957efb8603650af7be24433f966bf897405c0ebd3
|
|
| MD5 |
1c76e69b2b76542a06050ee93d15a599
|
|
| BLAKE2b-256 |
8322ac218e503a9634757c09ab66e50a6dc3c9bfaaa877163a744516d33c85ce
|
Provenance
The following attestation bundles were made for signinwithethereum-5.0.1-py3-none-any.whl:
Publisher:
publish.yml on signinwithethereum/siwe-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
signinwithethereum-5.0.1-py3-none-any.whl -
Subject digest:
53551a1073209f4f011589a957efb8603650af7be24433f966bf897405c0ebd3 - Sigstore transparency entry: 1369315036
- Sigstore integration time:
-
Permalink:
signinwithethereum/siwe-py@dbfc910b51e26cfa8b351f7fb389b5fa454a5d59 -
Branch / Tag:
refs/tags/v5.0.1 - Owner: https://github.com/signinwithethereum
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dbfc910b51e26cfa8b351f7fb389b5fa454a5d59 -
Trigger Event:
release
-
Statement type: