signinwithethereum 5.0.0__tar.gz → 5.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/PKG-INFO +60 -9
  2. signinwithethereum-5.0.2/README.md +151 -0
  3. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/pyproject.toml +1 -1
  4. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/grammars/eip4361.py +1 -8
  5. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/grammars/rfc3339.py +1 -8
  6. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/siwe.py +8 -3
  7. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/tests/test_siwe.py +55 -1
  8. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/uv.lock +5 -5
  9. signinwithethereum-5.0.0/README.md +0 -100
  10. signinwithethereum-5.0.0/siwe/grammars/rfc5234.py +0 -20
  11. signinwithethereum-5.0.0/test-vectors/.git +0 -1
  12. signinwithethereum-5.0.0/test-vectors/LICENSE-APACHE +0 -202
  13. signinwithethereum-5.0.0/test-vectors/LICENSE-MIT +0 -26
  14. signinwithethereum-5.0.0/test-vectors/README.md +0 -33
  15. signinwithethereum-5.0.0/test-vectors/vectors/grammar/invalid_chars.json +0 -202
  16. signinwithethereum-5.0.0/test-vectors/vectors/grammar/invalid_resources.json +0 -18
  17. signinwithethereum-5.0.0/test-vectors/vectors/grammar/invalid_uris.json +0 -19
  18. signinwithethereum-5.0.0/test-vectors/vectors/grammar/valid_chars.json +0 -42
  19. signinwithethereum-5.0.0/test-vectors/vectors/grammar/valid_resources.json +0 -34
  20. signinwithethereum-5.0.0/test-vectors/vectors/grammar/valid_specification.json +0 -74
  21. signinwithethereum-5.0.0/test-vectors/vectors/grammar/valid_uris.json +0 -308
  22. signinwithethereum-5.0.0/test-vectors/vectors/objects/message_objects.json +0 -59
  23. signinwithethereum-5.0.0/test-vectors/vectors/objects/parsing_negative_objects.json +0 -349
  24. signinwithethereum-5.0.0/test-vectors/vectors/parsing/parsing_negative.json +0 -39
  25. signinwithethereum-5.0.0/test-vectors/vectors/parsing/parsing_positive.json +0 -269
  26. signinwithethereum-5.0.0/test-vectors/vectors/parsing/parsing_warnings.json +0 -38
  27. signinwithethereum-5.0.0/test-vectors/vectors/verification/eip1271.json +0 -10
  28. signinwithethereum-5.0.0/test-vectors/vectors/verification/verification_negative.json +0 -125
  29. signinwithethereum-5.0.0/test-vectors/vectors/verification/verification_positive.json +0 -51
  30. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/.codex +0 -0
  31. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/.github/workflows/publish.yml +0 -0
  32. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/.gitignore +0 -0
  33. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/.gitmodules +0 -0
  34. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/LICENSE-APACHE +0 -0
  35. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/LICENSE-MIT +0 -0
  36. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/__init__.py +0 -0
  37. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/defs.py +0 -0
  38. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/grammars/__init__.py +0 -0
  39. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/siwe/parsed.py +0 -0
  40. {signinwithethereum-5.0.0 → signinwithethereum-5.0.2}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: signinwithethereum
3
- Version: 5.0.0
3
+ Version: 5.0.2
4
4
  Summary: A Python implementation of Sign in with Ethereum (EIP-4361).
5
5
  Project-URL: Homepage, https://siwe.xyz
6
6
  Project-URL: Repository, https://github.com/signinwithethereum/siwe-py
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  # Sign in with Ethereum
24
24
 
25
- This package provides a Python implementation of EIP-4361: Sign in With Ethereum.
25
+ This package provides a Python implementation of EIP-4361: Sign in with Ethereum.
26
26
 
27
27
  ## Installation
28
28
 
@@ -32,7 +32,7 @@ SIWE can be easily installed in any Python project with pip:
32
32
  pip install signinwithethereum
33
33
  ```
34
34
 
35
- The distribution is published as `signinwithethereum`, but the import name remains `siwe`:
35
+ The distribution is published as `signinwithethereum`; the import name is `siwe`:
36
36
 
37
37
  ```python
38
38
  from siwe import SiweMessage
@@ -59,16 +59,60 @@ message = SiweMessage(domain="login.xyz", address="0x1234...", ...)
59
59
 
60
60
  ### Verifying and Authenticating a SIWE Message
61
61
 
62
- Verification and authentication is performed via EIP-191, using the `address` field of the `SiweMessage` as the expected signer. The validate method checks message structural integrity, signature address validity, and time-based validity attributes.
62
+ 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.
63
+
64
+ 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.
63
65
 
64
66
  ```python
65
67
  try:
66
- message.verify(signature="0x...")
67
- # You can also specify other checks (e.g. the nonce or domain expected).
68
+ message.verify(
69
+ signature="0x...",
70
+ domain="example.com",
71
+ nonce=expected_nonce, # the nonce your server issued for this session
72
+ uri="https://example.com/login",
73
+ chain_id=1,
74
+ strict=True,
75
+ )
76
+ # Consume the nonce now so it cannot be replayed.
68
77
  except siwe.VerificationError:
69
78
  # Invalid
70
79
  ```
71
80
 
81
+ Passing `strict=True` enforces that `domain`, `uri`, `chain_id`, and `nonce` are all supplied. Prefer it for authentication flows.
82
+
83
+ ### Smart-contract wallet signatures (EIP-1271 / EIP-6492)
84
+
85
+ For signatures produced by contract wallets (Safe, Argent, etc.) rather than
86
+ externally owned accounts, pass a `web3` provider to `verify`. The
87
+ authentication arguments (`strict`, `domain`, `nonce`, `uri`, `chain_id`) still
88
+ apply — the provider only changes how the signature itself is verified:
89
+
90
+ ```python
91
+ from web3 import HTTPProvider
92
+
93
+ message.verify(
94
+ signature="0x...",
95
+ domain="example.com",
96
+ nonce=expected_nonce,
97
+ uri="https://example.com/login",
98
+ chain_id=1,
99
+ strict=True,
100
+ provider=HTTPProvider("https://mainnet.infura.io/v3/..."),
101
+ )
102
+ ```
103
+
104
+ EOA recovery is tried first; if it fails, the signature is checked on-chain via
105
+ `isValidSignature(bytes32,bytes)` per EIP-1271. Signatures carrying the EIP-6492
106
+ magic suffix are handed to the universal off-chain validator bytecode via
107
+ `eth_call`, which covers counterfactual (undeployed) wallets as well as
108
+ already-deployed ones. The provider's chain id is checked against the message's
109
+ `chain_id` before any on-chain call.
110
+
111
+ Note: this is verification only. EIP-6492 allows a verifier to optionally
112
+ submit the factory transaction after a successful check to finalize on-chain
113
+ deployment ("side-effectful" verification). This library does not do that — if
114
+ you need the wallet actually deployed, submit the factory call yourself.
115
+
72
116
  ### Serialization of a SIWE Message
73
117
 
74
118
  `SiweMessage` instances can also be serialized as their EIP-4361 string representations via the `prepare_message` method:
@@ -84,7 +128,14 @@ Parsing and verifying a `SiweMessage` is easy:
84
128
  ```python
85
129
  try:
86
130
  message = SiweMessage.from_message(eip_4361_string)
87
- message.verify(signature, nonce="abcdef", domain="example.com")
131
+ message.verify(
132
+ signature,
133
+ domain="example.com",
134
+ nonce=expected_nonce,
135
+ uri="https://example.com/login",
136
+ chain_id=1,
137
+ strict=True,
138
+ )
88
139
  except ValueError:
89
140
  # Invalid message format
90
141
  print("Authentication attempt rejected.")
@@ -97,7 +148,7 @@ except siwe.NonceMismatch:
97
148
  except siwe.InvalidSignature:
98
149
  print("Authentication attempt rejected.")
99
150
 
100
- # Message has been verified. Authentication complete. Continue with authorization/other.
151
+ # Message has been verified. Invalidate the stored nonce, then continue with authorization/other.
101
152
  ```
102
153
 
103
154
  ## Testing
@@ -0,0 +1,151 @@
1
+ # Sign in with Ethereum
2
+
3
+ This package provides a Python implementation of EIP-4361: Sign in with Ethereum.
4
+
5
+ ## Installation
6
+
7
+ SIWE can be easily installed in any Python project with pip:
8
+
9
+ ```bash
10
+ pip install signinwithethereum
11
+ ```
12
+
13
+ The distribution is published as `signinwithethereum`; the import name is `siwe`:
14
+
15
+ ```python
16
+ from siwe import SiweMessage
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ SIWE provides a `SiweMessage` class which implements EIP-4361.
22
+
23
+ ### Parsing a SIWE Message
24
+
25
+ Parsing is done by initializing a `SiweMessage` object with an EIP-4361 formatted string:
26
+
27
+ ```python
28
+ from siwe import SiweMessage
29
+ message = SiweMessage.from_message(message=eip_4361_string)
30
+ ```
31
+
32
+ Or to initialize a `SiweMessage` as a `pydantic.BaseModel` right away:
33
+
34
+ ```python
35
+ message = SiweMessage(domain="login.xyz", address="0x1234...", ...)
36
+ ```
37
+
38
+ ### Verifying and Authenticating a SIWE Message
39
+
40
+ 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.
41
+
42
+ 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.
43
+
44
+ ```python
45
+ try:
46
+ message.verify(
47
+ signature="0x...",
48
+ domain="example.com",
49
+ nonce=expected_nonce, # the nonce your server issued for this session
50
+ uri="https://example.com/login",
51
+ chain_id=1,
52
+ strict=True,
53
+ )
54
+ # Consume the nonce now so it cannot be replayed.
55
+ except siwe.VerificationError:
56
+ # Invalid
57
+ ```
58
+
59
+ Passing `strict=True` enforces that `domain`, `uri`, `chain_id`, and `nonce` are all supplied. Prefer it for authentication flows.
60
+
61
+ ### Smart-contract wallet signatures (EIP-1271 / EIP-6492)
62
+
63
+ For signatures produced by contract wallets (Safe, Argent, etc.) rather than
64
+ externally owned accounts, pass a `web3` provider to `verify`. The
65
+ authentication arguments (`strict`, `domain`, `nonce`, `uri`, `chain_id`) still
66
+ apply — the provider only changes how the signature itself is verified:
67
+
68
+ ```python
69
+ from web3 import HTTPProvider
70
+
71
+ message.verify(
72
+ signature="0x...",
73
+ domain="example.com",
74
+ nonce=expected_nonce,
75
+ uri="https://example.com/login",
76
+ chain_id=1,
77
+ strict=True,
78
+ provider=HTTPProvider("https://mainnet.infura.io/v3/..."),
79
+ )
80
+ ```
81
+
82
+ EOA recovery is tried first; if it fails, the signature is checked on-chain via
83
+ `isValidSignature(bytes32,bytes)` per EIP-1271. Signatures carrying the EIP-6492
84
+ magic suffix are handed to the universal off-chain validator bytecode via
85
+ `eth_call`, which covers counterfactual (undeployed) wallets as well as
86
+ already-deployed ones. The provider's chain id is checked against the message's
87
+ `chain_id` before any on-chain call.
88
+
89
+ Note: this is verification only. EIP-6492 allows a verifier to optionally
90
+ submit the factory transaction after a successful check to finalize on-chain
91
+ deployment ("side-effectful" verification). This library does not do that — if
92
+ you need the wallet actually deployed, submit the factory call yourself.
93
+
94
+ ### Serialization of a SIWE Message
95
+
96
+ `SiweMessage` instances can also be serialized as their EIP-4361 string representations via the `prepare_message` method:
97
+
98
+ ```python
99
+ print(message.prepare_message())
100
+ ```
101
+
102
+ ## Example
103
+
104
+ Parsing and verifying a `SiweMessage` is easy:
105
+
106
+ ```python
107
+ try:
108
+ message = SiweMessage.from_message(eip_4361_string)
109
+ message.verify(
110
+ signature,
111
+ domain="example.com",
112
+ nonce=expected_nonce,
113
+ uri="https://example.com/login",
114
+ chain_id=1,
115
+ strict=True,
116
+ )
117
+ except ValueError:
118
+ # Invalid message format
119
+ print("Authentication attempt rejected.")
120
+ except siwe.ExpiredMessage:
121
+ print("Authentication attempt rejected.")
122
+ except siwe.DomainMismatch:
123
+ print("Authentication attempt rejected.")
124
+ except siwe.NonceMismatch:
125
+ print("Authentication attempt rejected.")
126
+ except siwe.InvalidSignature:
127
+ print("Authentication attempt rejected.")
128
+
129
+ # Message has been verified. Invalidate the stored nonce, then continue with authorization/other.
130
+ ```
131
+
132
+ ## Testing
133
+
134
+ ```bash
135
+ git submodule update --init
136
+ uv sync
137
+ uv run pytest
138
+ ```
139
+
140
+ ## See Also
141
+
142
+ - [Sign in with Ethereum: TypeScript](https://github.com/signinwithethereum/siwe)
143
+ - [SIWE website: siwe.xyz](https://siwe.xyz)
144
+ - [EIP-4361 Specification Draft](https://eips.ethereum.org/EIPS/eip-4361)
145
+ - [EIP-191 Specification](https://eips.ethereum.org/EIPS/eip-191)
146
+
147
+ ## Disclaimer
148
+
149
+ Our Python library for Sign in with Ethereum has not yet undergone a formal
150
+ security audit. We welcome continued feedback on the usability, architecture,
151
+ and security of this implementation.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "signinwithethereum"
3
- version = "5.0.0"
3
+ version = "5.0.2"
4
4
  description = "A Python implementation of Sign in with Ethereum (EIP-4361)."
5
5
  license = "MIT OR Apache-2.0"
6
6
  authors = [
@@ -6,7 +6,7 @@ from abnf.grammars import rfc3986
6
6
  from abnf.grammars.misc import load_grammar_rules
7
7
  from abnf.parser import Rule as _Rule
8
8
 
9
- from . import rfc3339, rfc5234
9
+ from . import rfc3339
10
10
 
11
11
 
12
12
  @load_grammar_rules(
@@ -17,13 +17,7 @@ from . import rfc3339, rfc5234
17
17
  ("scheme", rfc3986.Rule("scheme")),
18
18
  ("reserved", rfc3986.Rule("reserved")),
19
19
  ("unreserved", rfc3986.Rule("unreserved")),
20
- ("reserved", rfc3986.Rule("reserved")),
21
20
  ("pchar", rfc3986.Rule("pchar")),
22
- # RFC 5234
23
- ("LF", rfc5234.Rule("LF")),
24
- ("HEXDIG", rfc5234.Rule("HEXDIG")),
25
- ("ALPHA", rfc5234.Rule("ALPHA")),
26
- ("DIGIT", rfc5234.Rule("DIGIT")),
27
21
  # RFC 3339
28
22
  ("date-time", rfc3339.Rule("date-time")),
29
23
  ]
@@ -41,7 +35,6 @@ class Rule(_Rule):
41
35
  "domain = authority",
42
36
  'address = "0x" 40HEXDIG',
43
37
  'statement = *( reserved / unreserved / " " )',
44
- "uri = URI",
45
38
  'version = "1"',
46
39
  "nonce = 8*( ALPHA / DIGIT )",
47
40
  "issued-at = date-time",
@@ -5,15 +5,8 @@ from typing import ClassVar, List
5
5
  from abnf.grammars.misc import load_grammar_rules
6
6
  from abnf.parser import Rule as _Rule
7
7
 
8
- from . import rfc5234
9
8
 
10
-
11
- @load_grammar_rules(
12
- [
13
- # RFC 5234
14
- ("DIGIT", rfc5234.Rule("DIGIT")),
15
- ]
16
- )
9
+ @load_grammar_rules()
17
10
  class Rule(_Rule):
18
11
  """Rules from RFC 3339."""
19
12
 
@@ -402,16 +402,21 @@ class SiweMessage(BaseModel):
402
402
  :param provider: A Web3 provider able to perform a contract check. This is
403
403
  required if support for Smart Contract Wallets that implement EIP-1271 or
404
404
  EIP-6492 is needed.
405
- :param strict: When True, requires uri and chain_id parameters to be provided.
405
+ :param strict: When True, requires domain, uri, chain_id, and nonce
406
+ parameters to be provided. The nonce must be a single-use value issued by
407
+ the relying party and consumed on successful verification — reusing the
408
+ same nonce across sessions defeats replay protection.
406
409
  :return: None if the message is valid and raises an exception otherwise
407
410
  """
408
411
  if strict:
412
+ if domain is None:
413
+ raise VerificationError("Strict mode requires domain parameter")
409
414
  if uri is None:
410
415
  raise VerificationError("Strict mode requires uri parameter")
411
416
  if chain_id is None:
412
417
  raise VerificationError("Strict mode requires chain_id parameter")
413
- if domain is None:
414
- raise VerificationError("Strict mode requires domain parameter")
418
+ if nonce is None:
419
+ raise VerificationError("Strict mode requires nonce parameter")
415
420
 
416
421
  message = encode_defunct(text=self.prepare_message())
417
422
  w3 = Web3(provider=provider)
@@ -1,5 +1,7 @@
1
1
  import json
2
2
  import os
3
+ import subprocess
4
+ import sys
3
5
 
4
6
  import abnf
5
7
  import pytest
@@ -367,7 +369,46 @@ class TestFieldValidation:
367
369
  def test_strict_mode_requires_domain(self):
368
370
  m = SiweMessage(**self.base)
369
371
  with pytest.raises(VerificationError):
370
- m.verify("0x" + "00" * 65, uri="https://ex.com", chain_id=1, strict=True)
372
+ m.verify(
373
+ "0x" + "00" * 65,
374
+ uri="https://ex.com",
375
+ chain_id=1,
376
+ nonce="12345678",
377
+ strict=True,
378
+ )
379
+
380
+ def test_strict_mode_requires_uri(self):
381
+ m = SiweMessage(**self.base)
382
+ with pytest.raises(VerificationError):
383
+ m.verify(
384
+ "0x" + "00" * 65,
385
+ domain="ex.com",
386
+ chain_id=1,
387
+ nonce="12345678",
388
+ strict=True,
389
+ )
390
+
391
+ def test_strict_mode_requires_chain_id(self):
392
+ m = SiweMessage(**self.base)
393
+ with pytest.raises(VerificationError):
394
+ m.verify(
395
+ "0x" + "00" * 65,
396
+ domain="ex.com",
397
+ uri="https://ex.com",
398
+ nonce="12345678",
399
+ strict=True,
400
+ )
401
+
402
+ def test_strict_mode_requires_nonce(self):
403
+ m = SiweMessage(**self.base)
404
+ with pytest.raises(VerificationError):
405
+ m.verify(
406
+ "0x" + "00" * 65,
407
+ domain="ex.com",
408
+ uri="https://ex.com",
409
+ chain_id=1,
410
+ strict=True,
411
+ )
371
412
 
372
413
 
373
414
  def _get_abnf_rule(rule_name):
@@ -502,3 +543,16 @@ class TestValidSpecification:
502
543
  siwe_message = SiweMessage.from_message(message=test["msg"], abnf=abnf_mode)
503
544
  for key, value in test["items"].items():
504
545
  assert getattr(siwe_message, key) == value
546
+
547
+
548
+ def test_grammar_loads_without_grammar_warnings():
549
+ # Grammars load once per process at import, so check in a fresh interpreter.
550
+ code = (
551
+ "import warnings, abnf\n"
552
+ "warnings.simplefilter('error', getattr(abnf, 'GrammarWarning', UserWarning))\n"
553
+ "import siwe.grammars.eip4361\n"
554
+ )
555
+ result = subprocess.run(
556
+ [sys.executable, "-c", code], capture_output=True, text=True
557
+ )
558
+ assert result.returncode == 0, result.stderr
@@ -4,11 +4,11 @@ requires-python = ">=3.10, <4.0"
4
4
 
5
5
  [[package]]
6
6
  name = "abnf"
7
- version = "2.4.1"
7
+ version = "2.9.0"
8
8
  source = { registry = "https://pypi.org/simple" }
9
- sdist = { url = "https://files.pythonhosted.org/packages/e2/ad/65071d3abe27e29347e8e4cd9ba3f8c5df096ac486d4afb7ab17b29f36ad/abnf-2.4.1.tar.gz", hash = "sha256:cde0674eabdf782c0bd3a8abae625f42c80650d65be5bfd76939ba9efc3c8122", size = 322627, upload-time = "2025-12-01T00:35:36.292Z" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/78/55/5514e564539e23a4948795695d5f25c184f0e02ac18695db1c8d5e6f3d05/abnf-2.9.0.tar.gz", hash = "sha256:45af3643da2281e34b03fba467bdeb09797f5552d140907a578c2d8ec10fcdd5", size = 1088572, upload-time = "2026-08-27T11:12:45.963Z" }
10
10
  wheels = [
11
- { url = "https://files.pythonhosted.org/packages/e0/2e/815384e25cdade147af784ad5a0f71fe10602c5e0a1d35a9c9040a72248b/abnf-2.4.1-py3-none-any.whl", hash = "sha256:c9918b6aa9fe93c531ab245d99b47fc2ccf56407e005a598b279f384397d1df1", size = 50039, upload-time = "2025-12-01T00:35:35.118Z" },
11
+ { url = "https://files.pythonhosted.org/packages/43/4c/cb77419f4dd5e6086550a05f4bce9eb521c80a2b877215065331c2b476b7/abnf-2.9.0-py3-none-any.whl", hash = "sha256:8592e174c05cc6010f5250f2292b832993f92e2ae136d7618368ee89d649435e", size = 78100, upload-time = "2026-08-27T11:12:44.185Z" },
12
12
  ]
13
13
 
14
14
  [[package]]
@@ -774,7 +774,7 @@ name = "exceptiongroup"
774
774
  version = "1.3.1"
775
775
  source = { registry = "https://pypi.org/simple" }
776
776
  dependencies = [
777
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
777
+ { name = "typing-extensions" },
778
778
  ]
779
779
  sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
780
780
  wheels = [
@@ -1633,7 +1633,7 @@ wheels = [
1633
1633
 
1634
1634
  [[package]]
1635
1635
  name = "signinwithethereum"
1636
- version = "5.0.0"
1636
+ version = "5.0.1"
1637
1637
  source = { editable = "." }
1638
1638
  dependencies = [
1639
1639
  { name = "abnf" },
@@ -1,100 +0,0 @@
1
- # Sign in with Ethereum
2
-
3
- This package provides a Python implementation of EIP-4361: Sign in With Ethereum.
4
-
5
- ## Installation
6
-
7
- SIWE can be easily installed in any Python project with pip:
8
-
9
- ```bash
10
- pip install signinwithethereum
11
- ```
12
-
13
- The distribution is published as `signinwithethereum`, but the import name remains `siwe`:
14
-
15
- ```python
16
- from siwe import SiweMessage
17
- ```
18
-
19
- ## Usage
20
-
21
- SIWE provides a `SiweMessage` class which implements EIP-4361.
22
-
23
- ### Parsing a SIWE Message
24
-
25
- Parsing is done by initializing a `SiweMessage` object with an EIP-4361 formatted string:
26
-
27
- ```python
28
- from siwe import SiweMessage
29
- message = SiweMessage.from_message(message=eip_4361_string)
30
- ```
31
-
32
- Or to initialize a `SiweMessage` as a `pydantic.BaseModel` right away:
33
-
34
- ```python
35
- message = SiweMessage(domain="login.xyz", address="0x1234...", ...)
36
- ```
37
-
38
- ### Verifying and Authenticating a SIWE Message
39
-
40
- Verification and authentication is performed via EIP-191, using the `address` field of the `SiweMessage` as the expected signer. The validate method checks message structural integrity, signature address validity, and time-based validity attributes.
41
-
42
- ```python
43
- try:
44
- message.verify(signature="0x...")
45
- # You can also specify other checks (e.g. the nonce or domain expected).
46
- except siwe.VerificationError:
47
- # Invalid
48
- ```
49
-
50
- ### Serialization of a SIWE Message
51
-
52
- `SiweMessage` instances can also be serialized as their EIP-4361 string representations via the `prepare_message` method:
53
-
54
- ```python
55
- print(message.prepare_message())
56
- ```
57
-
58
- ## Example
59
-
60
- Parsing and verifying a `SiweMessage` is easy:
61
-
62
- ```python
63
- try:
64
- message = SiweMessage.from_message(eip_4361_string)
65
- message.verify(signature, nonce="abcdef", domain="example.com")
66
- except ValueError:
67
- # Invalid message format
68
- print("Authentication attempt rejected.")
69
- except siwe.ExpiredMessage:
70
- print("Authentication attempt rejected.")
71
- except siwe.DomainMismatch:
72
- print("Authentication attempt rejected.")
73
- except siwe.NonceMismatch:
74
- print("Authentication attempt rejected.")
75
- except siwe.InvalidSignature:
76
- print("Authentication attempt rejected.")
77
-
78
- # Message has been verified. Authentication complete. Continue with authorization/other.
79
- ```
80
-
81
- ## Testing
82
-
83
- ```bash
84
- git submodule update --init
85
- uv sync
86
- uv run pytest
87
- ```
88
-
89
- ## See Also
90
-
91
- - [Sign in with Ethereum: TypeScript](https://github.com/signinwithethereum/siwe)
92
- - [SIWE website: siwe.xyz](https://siwe.xyz)
93
- - [EIP-4361 Specification Draft](https://eips.ethereum.org/EIPS/eip-4361)
94
- - [EIP-191 Specification](https://eips.ethereum.org/EIPS/eip-191)
95
-
96
- ## Disclaimer
97
-
98
- Our Python library for Sign in with Ethereum has not yet undergone a formal
99
- security audit. We welcome continued feedback on the usability, architecture,
100
- and security of this implementation.
@@ -1,20 +0,0 @@
1
- """Primitive ABNF definition."""
2
-
3
- from typing import ClassVar, List
4
-
5
- from abnf.grammars.misc import load_grammar_rules
6
- from abnf.parser import Rule as _Rule
7
-
8
-
9
- @load_grammar_rules()
10
- class Rule(_Rule):
11
- """Rules from RFC 5234."""
12
-
13
- grammar: ClassVar[List] = [
14
- "ALPHA = %x41-5A / %x61-7A ; A-Z / a-z",
15
- "LF = %x0A \
16
- ; linefeed",
17
- "DIGIT = %x30-39 \
18
- ; 0-9",
19
- 'HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"',
20
- ]
@@ -1 +0,0 @@
1
- gitdir: ../.git/modules/test-vectors