feat: add EIP712 verification, auth service implemention and jwt gen
This commit is contained in:
parent
6d16a6ea8c
commit
5c5df48644
8
go.mod
8
go.mod
@ -12,6 +12,13 @@ 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/go-viper/mapstructure/v2 v2.2.1 // 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/pflag v1.0.6 // 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/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
|
||||
58
go.sum
58
go.sum
@ -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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/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/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
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/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/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/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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
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/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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/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/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/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
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/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
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/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
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 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -14,26 +14,23 @@ type SessionRepo interface {
|
||||
GetUserSessions(ctx context.Context, userID uuid.UUID) ([]UserSession, error)
|
||||
}
|
||||
|
||||
// TODO: add IP and UserAgent
|
||||
type UserSession struct {
|
||||
ID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
User User
|
||||
WalletID uuid.UUID
|
||||
IPaddress string
|
||||
Agent string
|
||||
WalletID string
|
||||
ExpiresAt time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt 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{
|
||||
ID: uuid.New(),
|
||||
UserID: userID,
|
||||
WalletID: walletID,
|
||||
IPaddress: ipAddress,
|
||||
Agent: agent,
|
||||
ExpiresAt: expiresAt,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
|
||||
@ -1,23 +1,25 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"backend/pkg/validate/national"
|
||||
"backend/pkg/validate/phone"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPhoneInvalid = errors.New("phone number is invalid")
|
||||
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")
|
||||
)
|
||||
|
||||
@ -25,6 +27,7 @@ type UserRepo interface {
|
||||
Create(ctx context.Context, user *User) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*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
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
}
|
||||
@ -53,25 +56,65 @@ type Challenge struct {
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// TODO: check EIP712 in here for challenge validation
|
||||
func (c *Challenge) IsExpired() bool {
|
||||
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) {
|
||||
|
||||
_, err := phone.IsValid(phoneNumber)
|
||||
_, err := validate.IsValidPhone(phoneNumber)
|
||||
if err != nil {
|
||||
return nil, ErrPhoneInvalid
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = national.IsValid(nationalID)
|
||||
_, err = validate.IsValidNationalID(nationalID)
|
||||
if err != nil {
|
||||
return nil, ErrNationalIDInvalid
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !common.IsHexAddress(pubKey) {
|
||||
return nil, ErrWalletInvalid
|
||||
_, err = validate.IsValidPublicKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &User{
|
||||
|
||||
@ -11,9 +11,7 @@ type UserSession struct {
|
||||
Base
|
||||
UserID uuid.UUID
|
||||
User *User
|
||||
WalletID uuid.UUID
|
||||
IP string
|
||||
Agent string
|
||||
WalletID string
|
||||
ExpireAt time.Time
|
||||
}
|
||||
|
||||
@ -28,8 +26,6 @@ func CastSessionToStorage(s domain.UserSession) *UserSession {
|
||||
UserID: s.UserID,
|
||||
User: CastUserToStorage(s.User),
|
||||
WalletID: s.WalletID,
|
||||
IP: s.IPaddress,
|
||||
Agent: s.Agent,
|
||||
ExpireAt: s.ExpiresAt,
|
||||
}
|
||||
}
|
||||
@ -40,8 +36,6 @@ func CastSessionToDomain(s UserSession) *domain.UserSession {
|
||||
UserID: s.UserID,
|
||||
User: *CastUserToDomain(*s.User),
|
||||
WalletID: s.WalletID,
|
||||
IPaddress: s.IP,
|
||||
Agent: s.Agent,
|
||||
ExpiresAt: s.ExpireAt,
|
||||
CreatedAt: s.CreatedAt,
|
||||
UpdatedAt: s.UpdatedAt,
|
||||
|
||||
@ -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 {
|
||||
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
|
||||
}
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"backend/config"
|
||||
"backend/internal/domain"
|
||||
"backend/pkg/jwt"
|
||||
"backend/pkg/validate/common"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
jwt2 "github.com/golang-jwt/jwt/v5"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@ -15,11 +21,10 @@ type AuthService interface {
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
userRepo *domain.UserRepo
|
||||
sessionRepo *domain.SessionRepo
|
||||
userRepo domain.UserRepo
|
||||
sessionRepo domain.SessionRepo
|
||||
challengeExp uint
|
||||
tokenExp uint
|
||||
refreshTokenExp uint
|
||||
cfg config.JWT
|
||||
}
|
||||
|
||||
type UserToken struct {
|
||||
@ -29,24 +34,25 @@ type UserToken struct {
|
||||
}
|
||||
|
||||
func NewAuthService(
|
||||
userRepo *domain.UserRepo,
|
||||
sessionRepo *domain.SessionRepo,
|
||||
userRepo domain.UserRepo,
|
||||
sessionRepo domain.SessionRepo,
|
||||
challengeExp uint,
|
||||
tokenExp uint,
|
||||
refreshTokenExp uint) AuthService {
|
||||
cfg config.JWT,
|
||||
) AuthService {
|
||||
return &authService{
|
||||
userRepo: userRepo,
|
||||
sessionRepo: sessionRepo,
|
||||
challengeExp: challengeExp,
|
||||
tokenExp: tokenExp,
|
||||
refreshTokenExp: refreshTokenExp,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *authService) GenerateChallenge(ctx context.Context, pubKey string) (*domain.Challenge, error) {
|
||||
if !common.IsHexAddress(pubKey) {
|
||||
return nil, domain.ErrWalletInvalid
|
||||
_, err := common.IsValidPublicKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
challenge := &domain.Challenge{
|
||||
Message: uuid.New(),
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -3,4 +3,5 @@ package errors
|
||||
var (
|
||||
ErrPhoneInvalid = NewAppError(400, "Invalid phone number", ErrorTypeValidation, nil)
|
||||
ErrNationalIDInvalid = NewAppError(400, "Invalid national ID", ErrorTypeValidation, nil)
|
||||
ErrWalletInvalid = NewAppError(400, "Invalid wallet address", ErrorTypeValidation, nil)
|
||||
)
|
||||
|
||||
@ -1,12 +1,24 @@
|
||||
package national
|
||||
package common
|
||||
|
||||
import (
|
||||
"backend/pkg/errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"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)
|
||||
if valueCount < 8 {
|
||||
return false, errors.ErrNationalIDInvalid
|
||||
@ -43,3 +55,10 @@ func calculateNationalIDNumbers(valueSlices *[]uint8) (sum int) {
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func IsValidPublicKey(value string) (bool, error) {
|
||||
if !common.IsHexAddress(value) {
|
||||
return false, errors.ErrWalletInvalid
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user