Skip to content

Commit

Permalink
Add support for authority scheme and update tests (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbihel authored Apr 29, 2024
1 parent ace09dc commit eacc18e
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 9 deletions.
3 changes: 2 additions & 1 deletion siwe/defs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Regexes for the various fields."""

SCHEME = "((?P<scheme>([a-zA-Z][a-zA-Z0-9\\+\\-\\.]*))://)?"
DOMAIN = "(?P<domain>([^/?#]+)) wants you to sign in with your Ethereum account:\\n"
ADDRESS = "(?P<address>0x[a-zA-Z0-9]{40})\\n\\n"
STATEMENT = "((?P<statement>[^\\n]+)\\n)?\\n"
Expand All @@ -19,6 +20,6 @@
REQUEST_ID = "(\\nRequest ID: (?P<requestId>[-._~!$&'()*+,;=:@%a-zA-Z0-9]*))?"
RESOURCES = f"(\\nResources:(?P<resources>(\\n- {URI})+))?"
REGEX_MESSAGE = (
f"^{DOMAIN}{ADDRESS}{STATEMENT}{URI_LINE}{VERSION}{CHAIN_ID}{NONCE}"
f"^{SCHEME}{DOMAIN}{ADDRESS}{STATEMENT}{URI_LINE}{VERSION}{CHAIN_ID}{NONCE}"
f"{ISSUED_AT}{EXPIRATION_TIME}{NOT_BEFORE}{REQUEST_ID}{RESOURCES}$"
)
13 changes: 7 additions & 6 deletions siwe/grammars/eip4361.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# RFC 3986
("URI", rfc3986.Rule("URI")),
("authority", rfc3986.Rule("authority")),
("scheme", rfc3986.Rule("scheme")),
("reserved", rfc3986.Rule("reserved")),
("unreserved", rfc3986.Rule("unreserved")),
("reserved", rfc3986.Rule("reserved")),
Expand All @@ -31,12 +32,12 @@ class Rule(_Rule):
"""Rules from EIP-4361."""

grammar: ClassVar[List] = [
'sign-in-with-ethereum = domain %s" wants you to sign in with your Ethereum '
'account:" LF address LF LF [ statement LF ] LF %s"URI: " uri LF %s"Version: "'
' version LF %s"Chain ID: " chain-id LF %s"Nonce: " nonce LF %s"Issued At: " '
'issued-at [ LF %s"Expiration Time: " expiration-time ] [ LF %s"Not Before: " '
'not-before ] [ LF %s"Request ID: " request-id ] [ LF %s"Resources:" resources '
"]",
'sign-in-with-ethereum = [ scheme "://" ] domain %s" wants you to sign in with '
'your Ethereum account:" LF address LF LF [ statement LF ] LF %s"URI: " uri LF '
'%s"Version: " version LF %s"Chain ID: " chain-id LF %s"Nonce: " nonce LF %s"'
'Issued At: " issued-at [ LF %s"Expiration Time: " expiration-time ] [ LF %s"'
'Not Before: " not-before ] [ LF %s"Request ID: " request-id ] [ LF %s"'
'Resources:" resources ]',
"domain = authority",
'address = "0x" 40HEXDIG',
'statement = 1*( reserved / unreserved / " " )',
Expand Down
2 changes: 2 additions & 0 deletions siwe/parsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __init__(self, message: str):
raise ValueError("Message did not match the regular expression.")

self.match = match
self.scheme = match.group(expr.groupindex["scheme"])
self.domain = match.group(expr.groupindex["domain"])
self.address = match.group(expr.groupindex["address"])
self.statement = match.group(expr.groupindex["statement"])
Expand Down Expand Up @@ -49,6 +50,7 @@ def __init__(self, message: str):

for child in node.children:
if child.name in [
"scheme",
"domain",
"address",
"statement",
Expand Down
13 changes: 13 additions & 0 deletions siwe/siwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ class NotYetValidMessage(VerificationError):
pass


class SchemeMismatch(VerificationError):
"""The message does not contain the expected scheme."""

pass


class DomainMismatch(VerificationError):
"""The message does not contain the expected domain."""

Expand Down Expand Up @@ -157,6 +163,8 @@ def utc_now() -> datetime:
class SiweMessage(BaseModel):
"""A Sign-in with Ethereum (EIP-4361) message."""

scheme: Optional[str] = None
"""RFC 3986 URI scheme for the authority that is requesting the signing."""
domain: str = Field(pattern="^[^/?#]+$")
"""RFC 4501 dns authority that is requesting the signing."""
address: ChecksumAddress
Expand Down Expand Up @@ -234,6 +242,8 @@ def prepare_message(self) -> str:
:return: EIP-4361 formatted message, ready for EIP-191 signing.
"""
header = f"{self.domain} wants you to sign in with your Ethereum account:"
if self.scheme:
header = f"{self.scheme}://{header}"

uri_field = f"URI: {self.uri}"

Expand Down Expand Up @@ -281,6 +291,7 @@ def verify(
self,
signature: str,
*,
scheme: Optional[str] = None,
domain: Optional[str] = None,
nonce: Optional[str] = None,
timestamp: Optional[datetime] = None,
Expand All @@ -302,6 +313,8 @@ def verify(
message = encode_defunct(text=self.prepare_message())
w3 = Web3(provider=provider)

if scheme is not None and self.scheme != scheme:
raise SchemeMismatch()
if domain is not None and self.domain != domain:
raise DomainMismatch()
if nonce is not None and self.nonce != nonce:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_siwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_valid_message(self, abnf, test_name, test):
siwe_message = SiweMessage(message=test["message"], abnf=abnf)
for key, value in test["fields"].items():
v = getattr(siwe_message, key)
if not isinstance(v, int) and not isinstance(v, list):
if not (isinstance(v, int) or isinstance(v, list) or v is None):
v = str(v)
assert v == value

Expand Down

0 comments on commit eacc18e

Please sign in to comment.