New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API Harmonization #15
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1ac5636
Update README with latest API
theosirian fad60b2
API Harmonization Work
theosirian 41cb528
Add error message to error string
theosirian e58b4a9
Adding review comments changes
theosirian 386f5b9
Fix CreateEmpty test; Update submodule
theosirian b3ee35b
Update submodule
theosirian File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule siwe-js
updated
38 files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,28 +13,75 @@ import ( | |
"github.com/ethereum/go-ethereum/crypto" | ||
) | ||
|
||
func InitMessage(domain, address, uri, version string, options map[string]interface{}) (*Message, error) { | ||
validateURI, err := url.Parse(uri) | ||
func buildAuthority(uri *url.URL) string { | ||
authority := uri.Host | ||
if uri.User != nil { | ||
authority = fmt.Sprintf("%s@%s", uri.User.String(), authority) | ||
} | ||
return authority | ||
} | ||
|
||
func validateDomain(domain *string) (bool, error) { | ||
if isEmpty(domain) { | ||
return false, &InvalidMessage{"`domain` must not be empty"} | ||
} | ||
|
||
validateDomain, err := url.Parse(fmt.Sprintf("https://%s", *domain)) | ||
if err != nil { | ||
return false, &InvalidMessage{"Invalid format for field `domain`"} | ||
} | ||
|
||
authority := buildAuthority(validateDomain) | ||
if authority != *domain { | ||
return false, &InvalidMessage{"Invalid format for field `domain`"} | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
func validateURI(uri *string) (*url.URL, error) { | ||
if isEmpty(uri) { | ||
return nil, &InvalidMessage{"`uri` must not be empty"} | ||
} | ||
|
||
validateURI, err := url.Parse(*uri) | ||
if err != nil { | ||
return nil, &InvalidMessage{"Invalid format for field `uri`"} | ||
} | ||
|
||
return validateURI, nil | ||
} | ||
|
||
// InitMessage creates a Message object with the provided parameters | ||
func InitMessage(domain, address, uri, nonce string, options map[string]interface{}) (*Message, error) { | ||
if ok, err := validateDomain(&domain); !ok { | ||
return nil, err | ||
} | ||
|
||
if isEmpty(&address) { | ||
return nil, &InvalidMessage{"`address` must not be empty"} | ||
} | ||
|
||
validateURI, err := validateURI(&uri) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if isEmpty(&nonce) { | ||
return nil, &InvalidMessage{"`nonce` must not be empty"} | ||
} | ||
|
||
var statement *string | ||
if val, ok := options["statement"]; ok { | ||
value := val.(string) | ||
statement = &value | ||
} | ||
|
||
var nonce string | ||
if val, ok := isStringAndNotEmpty(options, "nonce"); ok { | ||
nonce = *val | ||
} else { | ||
return nil, &InvalidMessage{"Missing or empty `nonce` property"} | ||
} | ||
|
||
var chainId int | ||
if val, ok := options["chainId"]; ok { | ||
switch val.(type) { | ||
case float64: | ||
chainId = int(val.(float64)) | ||
case int: | ||
chainId = val.(int) | ||
case string: | ||
|
@@ -87,21 +134,21 @@ func InitMessage(domain, address, uri, version string, options map[string]interf | |
requestID = val | ||
} | ||
|
||
var resources []string | ||
var resources []url.URL | ||
if val, ok := options["resources"]; ok { | ||
switch val.(type) { | ||
case []string: | ||
resources = val.([]string) | ||
case []url.URL: | ||
resources = val.([]url.URL) | ||
default: | ||
return nil, &InvalidMessage{"`resources` must be a []string"} | ||
return nil, &InvalidMessage{"`resources` must be a []url.URL"} | ||
} | ||
} | ||
|
||
return &Message{ | ||
domain: domain, | ||
address: common.HexToAddress(address), | ||
uri: *validateURI, | ||
version: version, | ||
version: "1", | ||
|
||
statement: statement, | ||
nonce: nonce, | ||
|
@@ -130,19 +177,45 @@ func parseMessage(message string) (map[string]interface{}, error) { | |
} | ||
} | ||
|
||
if _, ok := result["domain"]; !ok { | ||
return nil, &InvalidMessage{"`domain` must not be empty"} | ||
} | ||
domain := result["domain"].(string) | ||
if ok, err := validateDomain(&domain); !ok { | ||
return nil, err | ||
} | ||
|
||
if _, ok := result["uri"]; !ok { | ||
return nil, &InvalidMessage{"`domain` must not be empty"} | ||
} | ||
uri := result["uri"].(string) | ||
if _, err := validateURI(&uri); err != nil { | ||
return nil, err | ||
} | ||
|
||
originalAddress := result["address"].(string) | ||
parsedAddress := common.HexToAddress(originalAddress) | ||
if originalAddress != parsedAddress.String() { | ||
return nil, &InvalidMessage{"Address must be in EIP-55 format"} | ||
} | ||
|
||
if val, ok := result["resources"]; ok { | ||
result["resources"] = strings.Split(val.(string), "\n- ")[1:] | ||
resources := strings.Split(val.(string), "\n- ")[1:] | ||
validateResources := make([]url.URL, len(resources)) | ||
for i, resource := range resources { | ||
validateResource, err := url.Parse(resource) | ||
if err != nil { | ||
return nil, &InvalidMessage{fmt.Sprintf("Invalid format for field `resources` at position %d", i)} | ||
} | ||
validateResources[i] = *validateResource | ||
} | ||
result["resources"] = validateResources | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
// ParseMessage returns a Message object by parsing an EIP-4361 formatted string | ||
func ParseMessage(message string) (*Message, error) { | ||
result, err := parseMessage(message) | ||
if err != nil { | ||
|
@@ -153,7 +226,7 @@ func ParseMessage(message string) (*Message, error) { | |
result["domain"].(string), | ||
result["address"].(string), | ||
result["uri"].(string), | ||
result["version"].(string), | ||
result["nonce"].(string), | ||
result, | ||
) | ||
|
||
|
@@ -171,26 +244,29 @@ func (m *Message) eip191Hash() common.Hash { | |
return crypto.Keccak256Hash([]byte(msg)) | ||
} | ||
|
||
// ValidNow validates the time constraints of the message at current time. | ||
func (m *Message) ValidNow() (bool, error) { | ||
return m.ValidAt(time.Now().UTC()) | ||
} | ||
|
||
// ValidAt validates the time constraints of the message at a specific point in time. | ||
func (m *Message) ValidAt(when time.Time) (bool, error) { | ||
if m.expirationTime != nil { | ||
if time.Now().UTC().After(*m.getExpirationTime()) { | ||
if when.After(*m.getExpirationTime()) { | ||
return false, &ExpiredMessage{"Message expired"} | ||
} | ||
} | ||
|
||
if m.notBefore != nil { | ||
if time.Now().UTC().Before(*m.getNotBefore()) { | ||
if when.Before(*m.getNotBefore()) { | ||
return false, &InvalidMessage{"Message not yet valid"} | ||
} | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
// VerifyEIP191 validates the integrity of the object by matching it's signature. | ||
func (m *Message) VerifyEIP191(signature string) (*ecdsa.PublicKey, error) { | ||
if isEmpty(&signature) { | ||
return nil, &InvalidSignature{"Signature cannot be empty"} | ||
|
@@ -221,7 +297,8 @@ func (m *Message) VerifyEIP191(signature string) (*ecdsa.PublicKey, error) { | |
return pkey, nil | ||
} | ||
|
||
func (m *Message) Verify(signature string, nonce *string, timestamp *time.Time) (*ecdsa.PublicKey, error) { | ||
// Verify validates time constraints and integrity of the object by matching it's signature. | ||
func (m *Message) Verify(signature string, domain *string, nonce *string, timestamp *time.Time) (*ecdsa.PublicKey, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we provide inline doc for public / exported functions? |
||
var err error | ||
|
||
if timestamp != nil { | ||
|
@@ -234,8 +311,14 @@ func (m *Message) Verify(signature string, nonce *string, timestamp *time.Time) | |
return nil, err | ||
} | ||
|
||
if domain != nil { | ||
if m.GetDomain() != *domain { | ||
return nil, &InvalidSignature{"Message domain doesn't match"} | ||
} | ||
} | ||
|
||
if nonce != nil { | ||
if m.GetNonce() == *nonce { | ||
if m.GetNonce() != *nonce { | ||
return nil, &InvalidSignature{"Message nonce doesn't match"} | ||
} | ||
} | ||
|
@@ -281,7 +364,7 @@ func (m *Message) prepareMessage() string { | |
if len(m.resources) > 0 { | ||
resourcesArr := make([]string, len(m.resources)) | ||
for i, v := range m.resources { | ||
resourcesArr[i] = fmt.Sprintf("- %s", v) | ||
resourcesArr[i] = fmt.Sprintf("- %s", v.String()) | ||
} | ||
|
||
resources := strings.Join(resourcesArr, "\n") | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we provide inline doc for public / exported functions -> ParseMessage(...): Message?