Skip to content

Commit 3ee02f0

Browse files
authored
fix: allow HTTP with localhost in solana (#2027)
`http://locahost:1234` URIs in SIWS messages were not allowed.
1 parent 066c53a commit 3ee02f0

File tree

3 files changed

+45
-30
lines changed

3 files changed

+45
-30
lines changed

hack/test.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ API_EXTERNAL_URL="http://localhost:9999"
1313
GOTRUE_LOG_SQL=none
1414
GOTRUE_LOG_LEVEL=warn
1515
GOTRUE_SITE_URL=https://example.netlify.com
16-
GOTRUE_URI_ALLOW_LIST="http://localhost:3000,https://supabase.com/"
16+
GOTRUE_URI_ALLOW_LIST="http://localhost:3000,https://supabase.com/,http://localhost:5173/"
1717
GOTRUE_OPERATOR_TOKEN=foobar
1818
GOTRUE_EXTERNAL_APPLE_ENABLED=true
1919
GOTRUE_EXTERNAL_APPLE_CLIENT_ID=testclientid

internal/api/web3.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,15 @@ func (a *API) web3GrantSolana(ctx context.Context, w http.ResponseWriter, r *htt
6868
return apierrors.NewOAuthError("invalid_grant", "Signature does not match address in message")
6969
}
7070

71-
if parsedMessage.URI.Scheme != "https" {
72-
if parsedMessage.URI.Scheme == "http" && parsedMessage.URI.Hostname() != "localhost" {
73-
return apierrors.NewOAuthError("invalid_grant", "Signed Solana message is using URI which uses HTTP and hostname is not localhost, only HTTPS is allowed")
74-
} else {
75-
return apierrors.NewOAuthError("invalid_grant", "Signed Solana message is using URI which does not use HTTPS")
76-
}
71+
if parsedMessage.URI.Scheme != "https" && parsedMessage.URI.Hostname() != "localhost" {
72+
return apierrors.NewOAuthError("invalid_grant", "Signed Solana message is using URI which does not use HTTPS")
7773
}
7874

7975
if !utilities.IsRedirectURLValid(config, parsedMessage.URI.String()) {
8076
return apierrors.NewOAuthError("invalid_grant", "Signed Solana message is using URI which is not allowed on this server, message was signed for another app")
8177
}
8278

83-
if parsedMessage.URI.Host != parsedMessage.Domain || !utilities.IsRedirectURLValid(config, "https://"+parsedMessage.Domain+"/") {
79+
if parsedMessage.URI.Hostname() != "localhost" && (parsedMessage.URI.Host != parsedMessage.Domain || !utilities.IsRedirectURLValid(config, "https://"+parsedMessage.Domain+"/")) {
8480
return apierrors.NewOAuthError("invalid_grant", "Signed Solana message is using a Domain that does not match the one in URI which is not allowed on this server")
8581
}
8682

internal/api/web3_test.go

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,35 +90,54 @@ func (ts *Web3TestSuite) TestHappyPath_FullMessage() {
9090
ts.API.overrideTime = nil
9191
}()
9292

93-
ts.API.overrideTime = func() time.Time {
94-
t, _ := time.Parse(time.RFC3339, "2025-03-29T00:09:59Z")
95-
return t
93+
examples := []struct {
94+
now string
95+
message string
96+
signature string
97+
}{
98+
{
99+
now: "2025-03-29T00:09:59Z",
100+
message: "supabase.com wants you to sign in with your Solana account:\n2EZEiBdw47VHT6SpZSW9VnuSvBe7DxuYHBTxj19gxvv8\n\nStatement\n\nURI: https://supabase.com/\nVersion: 1\nIssued At: 2025-03-29T00:00:00Z\nExpiration Time: 2025-03-29T00:10:00Z\nNot Before: 2025-03-29T00:00:00Z",
101+
signature: "aiKn+PAoB1OoXxS8H34HrB456YD4sKAVjeTjsxgkaQy3bkdV51WBTmUUE9lBU9kuXr0hTLI+1aTn5TFRbIF8CA==",
102+
},
103+
{
104+
now: "2025-05-16T15:01:59Z",
105+
message: "localhost:5173 wants you to sign in with your Solana account:\n4UPcfLX6rHuunkDiCnrVdN2BxnaKUAT1m2KCrzaAAct6\n\nSign in on localhost\n\nURI: http://localhost:5173/\nVersion: 1\nIssued At: 2025-05-16T14:52:03.613Z",
106+
signature: "RT2JCFpZQtPwGONApGZn1dZnxOBB3zJZHAQPr+cOaI+eQ4ecw/N6zJ6TNw8a+g8n6Xm/Ky1TVZRuWHSxMU1jDg==",
107+
},
96108
}
97109

98-
var buffer bytes.Buffer
99-
require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(map[string]interface{}{
100-
"chain": "solana",
101-
"message": "supabase.com wants you to sign in with your Solana account:\n2EZEiBdw47VHT6SpZSW9VnuSvBe7DxuYHBTxj19gxvv8\n\nStatement\n\nURI: https://supabase.com/\nVersion: 1\nIssued At: 2025-03-29T00:00:00Z\nExpiration Time: 2025-03-29T00:10:00Z\nNot Before: 2025-03-29T00:00:00Z",
102-
"signature": "aiKn+PAoB1OoXxS8H34HrB456YD4sKAVjeTjsxgkaQy3bkdV51WBTmUUE9lBU9kuXr0hTLI+1aTn5TFRbIF8CA==",
103-
}))
110+
for _, example := range examples {
111+
ts.API.overrideTime = func() time.Time {
112+
t, _ := time.Parse(time.RFC3339, example.now)
113+
return t
114+
}
104115

105-
req := httptest.NewRequest(http.MethodPost, "http://localhost/token?grant_type=web3", &buffer)
106-
req.Header.Set("Content-Type", "application/json")
116+
var buffer bytes.Buffer
117+
require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(map[string]interface{}{
118+
"chain": "solana",
119+
"message": example.message,
120+
"signature": example.signature,
121+
}))
107122

108-
w := httptest.NewRecorder()
109-
ts.API.handler.ServeHTTP(w, req)
123+
req := httptest.NewRequest(http.MethodPost, "http://localhost/token?grant_type=web3", &buffer)
124+
req.Header.Set("Content-Type", "application/json")
110125

111-
assert.Equal(ts.T(), http.StatusOK, w.Code)
126+
w := httptest.NewRecorder()
127+
ts.API.handler.ServeHTTP(w, req)
112128

113-
var firstResult struct {
114-
AccessToken string `json:"access_token"`
115-
RefreshToken string `json:"refresh_token"`
116-
}
129+
assert.Equal(ts.T(), http.StatusOK, w.Code)
117130

118-
assert.NoError(ts.T(), json.NewDecoder(w.Result().Body).Decode(&firstResult))
131+
var firstResult struct {
132+
AccessToken string `json:"access_token"`
133+
RefreshToken string `json:"refresh_token"`
134+
}
119135

120-
assert.NotEmpty(ts.T(), firstResult.AccessToken)
121-
assert.NotEmpty(ts.T(), firstResult.RefreshToken)
136+
assert.NoError(ts.T(), json.NewDecoder(w.Result().Body).Decode(&firstResult))
137+
138+
assert.NotEmpty(ts.T(), firstResult.AccessToken)
139+
assert.NotEmpty(ts.T(), firstResult.RefreshToken)
140+
}
122141
}
123142

124143
func (ts *Web3TestSuite) TestHappyPath_MinimalMessage() {
@@ -190,7 +209,7 @@ func (ts *Web3TestSuite) TestValidationRules_URINotHTTPSButIsHTTP() {
190209
assert.NoError(ts.T(), json.NewDecoder(w.Result().Body).Decode(&firstResult))
191210

192211
assert.Equal(ts.T(), firstResult.Error, "invalid_grant")
193-
assert.Equal(ts.T(), firstResult.ErrorDescription, "Signed Solana message is using URI which uses HTTP and hostname is not localhost, only HTTPS is allowed")
212+
assert.Equal(ts.T(), firstResult.ErrorDescription, "Signed Solana message is using URI which does not use HTTPS")
194213
}
195214

196215
func (ts *Web3TestSuite) TestValidationRules_URINotAllowed() {

0 commit comments

Comments
 (0)