feat: add EIP712 verification, auth service implemention and jwt gen

This commit is contained in:
AmirMahdi Qiasvand 2025-09-07 14:35:40 +03:30
parent 6d16a6ea8c
commit 5c5df48644
10 changed files with 244 additions and 62 deletions

8
go.mod
View File

@ -12,6 +12,13 @@ require (
) )
require ( require (
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/consensys/gnark-crypto v0.18.0 // indirect
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/holiman/uint256 v1.3.2 // indirect github.com/holiman/uint256 v1.3.2 // indirect
@ -28,6 +35,7 @@ require (
github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.14 // indirect
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.36.0 // indirect golang.org/x/crypto v0.36.0 // indirect

58
go.sum
View File

@ -1,16 +1,48 @@
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI=
github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w=
github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
github.com/ethereum/go-ethereum v1.16.3 h1:nDoBSrmsrPbrDIVLTkDQCy1U9KdHN+F2PzvMbDoS42Q= github.com/ethereum/go-ethereum v1.16.3 h1:nDoBSrmsrPbrDIVLTkDQCy1U9KdHN+F2PzvMbDoS42Q=
github.com/ethereum/go-ethereum v1.16.3/go.mod h1:Lrsc6bt9Gm9RyvhfFK53vboCia8kpF9nv+2Ukntnl+8= github.com/ethereum/go-ethereum v1.16.3/go.mod h1:Lrsc6bt9Gm9RyvhfFK53vboCia8kpF9nv+2Ukntnl+8=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@ -29,18 +61,36 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
@ -58,6 +108,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo=
github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
@ -73,6 +129,8 @@ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -14,26 +14,23 @@ type SessionRepo interface {
GetUserSessions(ctx context.Context, userID uuid.UUID) ([]UserSession, error) GetUserSessions(ctx context.Context, userID uuid.UUID) ([]UserSession, error)
} }
// TODO: add IP and UserAgent
type UserSession struct { type UserSession struct {
ID uuid.UUID ID uuid.UUID
UserID uuid.UUID UserID uuid.UUID
User User User User
WalletID uuid.UUID WalletID string
IPaddress string
Agent string
ExpiresAt time.Time ExpiresAt time.Time
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
DeletedAt *time.Time DeletedAt *time.Time
} }
func NewSession(userID, walletID uuid.UUID, ipAddress, agent string, expiresAt time.Time) *UserSession { func NewSession(userID uuid.UUID, walletID string, expiresAt time.Time) *UserSession {
return &UserSession{ return &UserSession{
ID: uuid.New(), ID: uuid.New(),
UserID: userID, UserID: userID,
WalletID: walletID, WalletID: walletID,
IPaddress: ipAddress,
Agent: agent,
ExpiresAt: expiresAt, ExpiresAt: expiresAt,
CreatedAt: time.Now().UTC(), CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),

View File

@ -1,23 +1,25 @@
package domain package domain
import ( import (
"backend/pkg/validate/national"
"backend/pkg/validate/phone"
"context" "context"
"errors" "errors"
"fmt"
"strings"
"time" "time"
"net/mail" "net/mail"
validate "backend/pkg/validate/common"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/google/uuid" "github.com/google/uuid"
) )
var ( var (
ErrPhoneInvalid = errors.New("phone number is invalid")
ErrUserNotFound = errors.New("user not found") ErrUserNotFound = errors.New("user not found")
ErrNationalIDInvalid = errors.New("national ID is invalid")
ErrWalletInvalid = errors.New("wallet address is invalid")
ErrEmailInvalid = errors.New("email is invalid") ErrEmailInvalid = errors.New("email is invalid")
) )
@ -25,6 +27,7 @@ type UserRepo interface {
Create(ctx context.Context, user *User) error Create(ctx context.Context, user *User) error
GetByID(ctx context.Context, id uuid.UUID) (*User, error) GetByID(ctx context.Context, id uuid.UUID) (*User, error)
GetByPhone(ctx context.Context, phone string) (*User, error) GetByPhone(ctx context.Context, phone string) (*User, error)
GetByPubKey(ctx context.Context, pubKey string) (*User, error)
Update(ctx context.Context, user *User) error Update(ctx context.Context, user *User) error
Delete(ctx context.Context, id uuid.UUID) error Delete(ctx context.Context, id uuid.UUID) error
} }
@ -53,25 +56,65 @@ type Challenge struct {
ExpiresAt time.Time ExpiresAt time.Time
} }
// TODO: check EIP712 in here for challenge validation
func (c *Challenge) IsExpired() bool { func (c *Challenge) IsExpired() bool {
return time.Now().After(c.ExpiresAt) return time.Now().After(c.ExpiresAt)
} }
func (c *Challenge) Verify(address string, signedMsg string) (bool, error) {
if c.IsExpired() {
return false, errors.New("challenge has expired")
}
signature, err := hexutil.Decode(signedMsg)
if err != nil {
return false, fmt.Errorf("decode signature: %w", err)
}
challengeBytes := []byte(c.Message.String())
return c.VerifySignedBytes(address, challengeBytes, signature)
}
func (c *Challenge) VerifySignedBytes(expectedAddress string, msg []byte, sig []byte) (bool, error) {
msgHash := accounts.TextHash(msg)
if len(sig) != 65 {
return false, fmt.Errorf("invalid signature length: expected 65 bytes, got %d", len(sig))
}
if sig[crypto.RecoveryIDOffset] == 27 || sig[crypto.RecoveryIDOffset] == 28 {
sig[crypto.RecoveryIDOffset] -= 27
}
recovered, err := crypto.SigToPub(msgHash, sig)
if err != nil {
return false, fmt.Errorf("failed to recover public key: %w", err)
}
recoveredAddr := crypto.PubkeyToAddress(*recovered)
if !common.IsHexAddress(expectedAddress) {
return false, fmt.Errorf("invalid Ethereum address format: %s", expectedAddress)
}
expectedAddr := common.HexToAddress(expectedAddress)
return strings.EqualFold(expectedAddr.Hex(), recoveredAddr.Hex()), nil
}
func NewUser(pubKey, name, lastName, phoneNumber, email, nationalID string) (*User, error) { func NewUser(pubKey, name, lastName, phoneNumber, email, nationalID string) (*User, error) {
_, err := phone.IsValid(phoneNumber) _, err := validate.IsValidPhone(phoneNumber)
if err != nil { if err != nil {
return nil, ErrPhoneInvalid return nil, err
} }
_, err = national.IsValid(nationalID) _, err = validate.IsValidNationalID(nationalID)
if err != nil { if err != nil {
return nil, ErrNationalIDInvalid return nil, err
} }
if !common.IsHexAddress(pubKey) { _, err = validate.IsValidPublicKey(pubKey)
return nil, ErrWalletInvalid if err != nil {
return nil, err
} }
return &User{ return &User{

View File

@ -11,9 +11,7 @@ type UserSession struct {
Base Base
UserID uuid.UUID UserID uuid.UUID
User *User User *User
WalletID uuid.UUID WalletID string
IP string
Agent string
ExpireAt time.Time ExpireAt time.Time
} }
@ -28,8 +26,6 @@ func CastSessionToStorage(s domain.UserSession) *UserSession {
UserID: s.UserID, UserID: s.UserID,
User: CastUserToStorage(s.User), User: CastUserToStorage(s.User),
WalletID: s.WalletID, WalletID: s.WalletID,
IP: s.IPaddress,
Agent: s.Agent,
ExpireAt: s.ExpiresAt, ExpireAt: s.ExpiresAt,
} }
} }
@ -40,8 +36,6 @@ func CastSessionToDomain(s UserSession) *domain.UserSession {
UserID: s.UserID, UserID: s.UserID,
User: *CastUserToDomain(*s.User), User: *CastUserToDomain(*s.User),
WalletID: s.WalletID, WalletID: s.WalletID,
IPaddress: s.IP,
Agent: s.Agent,
ExpiresAt: s.ExpireAt, ExpiresAt: s.ExpireAt,
CreatedAt: s.CreatedAt, CreatedAt: s.CreatedAt,
UpdatedAt: s.UpdatedAt, UpdatedAt: s.UpdatedAt,

View File

@ -48,3 +48,11 @@ func (r *UserRepository) Update(ctx context.Context, user *domain.User) error {
func (r *UserRepository) Delete(ctx context.Context, id uuid.UUID) error { func (r *UserRepository) Delete(ctx context.Context, id uuid.UUID) error {
return r.db.WithContext(ctx).Delete(&types.User{}, "id = ?", id).Error return r.db.WithContext(ctx).Delete(&types.User{}, "id = ?", id).Error
} }
func (r *UserRepository) GetByPubKey(ctx context.Context, pubKey string) (*domain.User, error) {
var user types.User
if err := r.db.WithContext(ctx).First(&user, "pub_key = ?", pubKey).Error; err != nil {
return nil, err
}
return types.CastUserToDomain(user), nil
}

View File

@ -1,11 +1,17 @@
package usecase package usecase
import ( import (
"backend/config"
"backend/internal/domain" "backend/internal/domain"
"backend/pkg/jwt"
"backend/pkg/validate/common"
"context" "context"
"errors"
"fmt"
"time" "time"
"github.com/ethereum/go-ethereum/common" jwt2 "github.com/golang-jwt/jwt/v5"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -15,11 +21,10 @@ type AuthService interface {
} }
type authService struct { type authService struct {
userRepo *domain.UserRepo userRepo domain.UserRepo
sessionRepo *domain.SessionRepo sessionRepo domain.SessionRepo
challengeExp uint challengeExp uint
tokenExp uint cfg config.JWT
refreshTokenExp uint
} }
type UserToken struct { type UserToken struct {
@ -29,24 +34,25 @@ type UserToken struct {
} }
func NewAuthService( func NewAuthService(
userRepo *domain.UserRepo, userRepo domain.UserRepo,
sessionRepo *domain.SessionRepo, sessionRepo domain.SessionRepo,
challengeExp uint, challengeExp uint,
tokenExp uint, cfg config.JWT,
refreshTokenExp uint) AuthService { ) AuthService {
return &authService{ return &authService{
userRepo: userRepo, userRepo: userRepo,
sessionRepo: sessionRepo, sessionRepo: sessionRepo,
challengeExp: challengeExp, challengeExp: challengeExp,
tokenExp: tokenExp, cfg: cfg,
refreshTokenExp: refreshTokenExp,
} }
} }
func (s *authService) GenerateChallenge(ctx context.Context, pubKey string) (*domain.Challenge, error) { func (s *authService) GenerateChallenge(ctx context.Context, pubKey string) (*domain.Challenge, error) {
if !common.IsHexAddress(pubKey) { _, err := common.IsValidPublicKey(pubKey)
return nil, domain.ErrWalletInvalid if err != nil {
return nil, err
} }
challenge := &domain.Challenge{ challenge := &domain.Challenge{
Message: uuid.New(), Message: uuid.New(),
TimeStamp: time.Now().UTC(), TimeStamp: time.Now().UTC(),
@ -56,5 +62,68 @@ func (s *authService) GenerateChallenge(ctx context.Context, pubKey string) (*do
} }
func (s *authService) Authenticate(ctx context.Context, pubKey string, signature string, challenge *domain.Challenge, chainID uint) (*UserToken, error) { func (s *authService) Authenticate(ctx context.Context, pubKey string, signature string, challenge *domain.Challenge, chainID uint) (*UserToken, error) {
return nil, nil _, err := common.IsValidPublicKey(pubKey)
if err != nil {
return nil, fmt.Errorf("invalid public key: %w", err)
}
isValid, err := challenge.Verify(pubKey, signature)
if err != nil {
return nil, fmt.Errorf("signature verification failed: %w", err)
}
if !isValid {
return nil, errors.New("invalid signature")
}
user, err := s.userRepo.GetByPubKey(ctx, pubKey)
if err != nil {
return nil, err
}
if user == nil {
return nil, domain.ErrUserNotFound
}
user.UpdateLastLogin()
if err := s.userRepo.Update(ctx, user); err != nil {
return nil, err
}
expiresAt := time.Now().Add(time.Duration(s.cfg.TokenExpMinutes) * time.Minute)
session := domain.NewSession(user.ID, user.PubKey, expiresAt)
err = s.sessionRepo.Create(ctx, session)
if err != nil {
return nil, fmt.Errorf("failed to create session: %w", err)
}
claims := &jwt.UserClaims{
UserID: uint(user.ID.ID()),
Sections: []string{},
}
claims.ExpiresAt = jwt2.NewNumericDate(expiresAt)
claims.IssuedAt = jwt2.NewNumericDate(time.Now())
authToken, err := jwt.CreateToken([]byte(s.cfg.TokenSecret), claims)
if err != nil {
return nil, err
}
refreshExpiresAt := time.Now().Add(time.Duration(s.cfg.RefreshTokenExpMinutes) * time.Minute)
refreshClaims := &jwt.UserClaims{
UserID: uint(user.ID.ID()),
Sections: []string{"refresh"},
}
refreshClaims.ExpiresAt = jwt2.NewNumericDate(refreshExpiresAt)
refreshClaims.IssuedAt = jwt2.NewNumericDate(time.Now())
refreshToken, err := jwt.CreateToken([]byte(s.cfg.TokenSecret), refreshClaims)
if err != nil {
return nil, fmt.Errorf("failed to create refresh token: %w", err)
}
return &UserToken{
AuthorizationToken: authToken,
RefreshToken: refreshToken,
ExpiresAt: expiresAt.Unix(),
}, nil
} }

View File

@ -3,4 +3,5 @@ package errors
var ( var (
ErrPhoneInvalid = NewAppError(400, "Invalid phone number", ErrorTypeValidation, nil) ErrPhoneInvalid = NewAppError(400, "Invalid phone number", ErrorTypeValidation, nil)
ErrNationalIDInvalid = NewAppError(400, "Invalid national ID", ErrorTypeValidation, nil) ErrNationalIDInvalid = NewAppError(400, "Invalid national ID", ErrorTypeValidation, nil)
ErrWalletInvalid = NewAppError(400, "Invalid wallet address", ErrorTypeValidation, nil)
) )

View File

@ -1,12 +1,24 @@
package national package common
import ( import (
"backend/pkg/errors" "backend/pkg/errors"
"regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum/go-ethereum/common"
) )
func IsValid(value string) (bool, error) { func IsValidPhone(value string) (bool, error) {
re := regexp.MustCompile(`^(?:0|\+98|0098)(\d{10})$`)
result := re.FindStringSubmatch(value)
if len(result) > 0 {
return true, nil
}
return false, errors.ErrPhoneInvalid
}
func IsValidNationalID(value string) (bool, error) {
valueCount := len(value) valueCount := len(value)
if valueCount < 8 { if valueCount < 8 {
return false, errors.ErrNationalIDInvalid return false, errors.ErrNationalIDInvalid
@ -43,3 +55,10 @@ func calculateNationalIDNumbers(valueSlices *[]uint8) (sum int) {
} }
return sum return sum
} }
func IsValidPublicKey(value string) (bool, error) {
if !common.IsHexAddress(value) {
return false, errors.ErrWalletInvalid
}
return true, nil
}

View File

@ -1,15 +0,0 @@
package phone
import (
"backend/pkg/errors"
"regexp"
)
func IsValid(value string) (bool, error) {
re := regexp.MustCompile(`^(?:0|\+98|0098)(\d{10})$`)
result := re.FindStringSubmatch(value)
if len(result) > 0 {
return true, nil
}
return false, errors.ErrPhoneInvalid
}