- Register by signing service key: GET /v1/service-key, POST /v1/auth/register-by-signature - Login auto-attempts register first for new users - Web: default API URL momswap.produktor.duckdns.org, /libs/ static handler - Docker: webbuild stage for geo-api-client, copy web+libs to runtime - Bin scripts: test.sh, run.sh, up.sh, down.sh - docs/ed25519-security-use-cases.md: use cases, message formats, examples - SERVICE_PUBLIC_KEY env (defaults to ADMIN_PUBLIC_KEY) Made-with: Cursor
This commit is contained in:
@@ -21,7 +21,7 @@ func newTestServer(adminPublicKey string) *httptest.Server {
|
||||
svc := app.NewService(memory, app.Config{
|
||||
ChallengeTTL: 5 * time.Minute,
|
||||
SessionTTL: 24 * time.Hour,
|
||||
})
|
||||
}, adminPublicKey)
|
||||
svc.BootstrapAdmin(adminPublicKey)
|
||||
api := httpapi.NewAPI(svc)
|
||||
return httptest.NewServer(api.Routes())
|
||||
@@ -36,6 +36,22 @@ func mustJSON(t *testing.T, value interface{}) []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
func getJSON(t *testing.T, client *http.Client, url string, token string) (*http.Response, map[string]interface{}) {
|
||||
t.Helper()
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("do request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
out := map[string]interface{}{}
|
||||
_ = json.NewDecoder(resp.Body).Decode(&out)
|
||||
return resp, out
|
||||
}
|
||||
|
||||
func postJSON(t *testing.T, client *http.Client, url string, body interface{}, token string) (*http.Response, map[string]interface{}) {
|
||||
t.Helper()
|
||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(mustJSON(t, body)))
|
||||
@@ -107,6 +123,46 @@ func registerUserViaAdmin(t *testing.T, client *http.Client, baseURL, adminPub s
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterBySignature(t *testing.T) {
|
||||
adminPub, _, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("generate admin key: %v", err)
|
||||
}
|
||||
adminPubB64 := base64.RawURLEncoding.EncodeToString(adminPub)
|
||||
|
||||
server := newTestServer(adminPubB64)
|
||||
defer server.Close()
|
||||
client := server.Client()
|
||||
|
||||
svcKeyResp, svcKeyData := getJSON(t, client, server.URL+"/v1/service-key", "")
|
||||
if svcKeyResp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("get service key status=%d body=%v", svcKeyResp.StatusCode, svcKeyData)
|
||||
}
|
||||
if svcKeyData["publicKey"] != adminPubB64 {
|
||||
t.Fatalf("service key mismatch: got %v", svcKeyData["publicKey"])
|
||||
}
|
||||
|
||||
userPub, userPriv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("generate user key: %v", err)
|
||||
}
|
||||
userPubB64 := base64.RawURLEncoding.EncodeToString(userPub)
|
||||
sig := base64.RawURLEncoding.EncodeToString(ed25519.Sign(userPriv, []byte(adminPubB64)))
|
||||
|
||||
regResp, regData := postJSON(t, client, server.URL+"/v1/auth/register-by-signature", map[string]string{
|
||||
"publicKey": userPubB64,
|
||||
"signature": sig,
|
||||
}, "")
|
||||
if regResp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("register-by-signature status=%d body=%v", regResp.StatusCode, regData)
|
||||
}
|
||||
|
||||
userToken := loginUser(t, client, server.URL, userPubB64, userPriv)
|
||||
if userToken == "" {
|
||||
t.Fatal("login after register-by-signature failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterLoginAndProfile(t *testing.T) {
|
||||
adminPub, adminPriv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user