diff --git a/.env b/.env new file mode 100644 index 0000000..c57f4c3 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +REDIS_PASSWORD=ELdhsgqJt5QZUSWKU5vY3D9CXa1a0teIceeHqvCtoPkrDJ0Lge7XIe8187gFjd0qZLv9zwhGr62MqY +HYDRA_PASSWORD=CHANGE-ME-INSECURE-PASSWORD \ No newline at end of file diff --git a/api/auth/api.yaml b/api/auth/api.yaml index 33f5654..4600833 100644 --- a/api/auth/api.yaml +++ b/api/auth/api.yaml @@ -5,8 +5,10 @@ info: servers: - url: https://id.logidex.ru/api/auth paths: - /otp/request: + /auth/otp/request: post: + tags: + - auth summary: Request OTP requestBody: required: true @@ -28,8 +30,11 @@ paths: application/json: schema: $ref: '#/components/schemas/RequestOTPResponse' - /otp/verify: + /auth/otp/verify: + post: + tags: + - auth summary: Verify OTP requestBody: required: true @@ -50,6 +55,30 @@ paths: application/json: schema: $ref: '#/components/schemas/VerifyOTPResponse' + /auth/consent/accept: + post: + tags: + - auth + summary: Accept consent + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AcceptConsentRequest' + responses: + '200': + description: Consent accepted successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AcceptConsentResponse' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/AcceptConsentResponse' components: schemas: RequestOTPRequest: @@ -110,4 +139,31 @@ components: example: true required: - redirect_url - - ok \ No newline at end of file + - ok + AcceptConsentRequest: + type: object + properties: + consent_challenge: + type: string + description: The consent challenge to accept + example: "challenge123" + required: + - consent_challenge + AcceptConsentResponse: + type: object + properties: + redirect_url: + type: string + description: URL to redirect to after accepting consent + example: "https://example.com/consent-accepted" + ok: + type: boolean + description: Status of the consent acceptance + example: true + message: + type: string + example: "Consent accepted" + required: + - message + - ok + - redirect_url \ No newline at end of file diff --git a/cmd/api/main.go b/cmd/api/main.go index 5b36100..3e2a68b 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -1,47 +1,28 @@ package main import ( - "context" - "fmt" - todo_api "git.logidex.ru/fakz9/logidex-id/internal/api/todo/handler" - user_api "git.logidex.ru/fakz9/logidex-id/internal/api/user/handler" + authApi "git.logidex.ru/fakz9/logidex-id/internal/api/auth/handler" + "git.logidex.ru/fakz9/logidex-id/internal/config" + "git.logidex.ru/fakz9/logidex-id/internal/hydra_client" + "git.logidex.ru/fakz9/logidex-id/internal/redis" "github.com/gofiber/fiber/v2" - "github.com/ory/hydra-client-go" + "strconv" ) func main() { - loginChallange := "-MLI2zCD8VLsZ6CIGHBKGtV0YCnVw1ipQnciX6UUsMvlWoDRu4S6V8d-in9hbUb_Nvc8vg8L7YnVBRhort90-eB0La4q1xhatcJUdlAsCW1le21WsOp63urZwRGdbk0yY6O5kD0AXO_U-asXWCNVkyVGUXmOkjf40hFeztO7XBfU8GRQkBb9ZTbBjjoFUWUgFYtYuYvp2Jbb754ZEC5zmbxq3s4oXLDgJJrUGsEza4tQ9Tj3JrVyf86x1ATZ3KnSfpWfyttQiz7P_yyQDjF4Cc3QseFYFScjhTopjteg0rmleXcnqYGIKQWXzCQwb8uJdAnIIe_Yo3yhHb_dRjFNEuLVUW7FFzQMwsVK3UscW-PDoxme0gYUqIIhDx-mpscBBiBzFKFHtbq9WvN5SrzGegUWWEIxrpJCtMh-bRSMuckU2BuxTY70BTvAxDsBlvFmSy7mhe-WgY2f5GJjy1GpbGwk0A8rXO8kaxnqjZvNnDwPqjUvlkkZwqr2Y3Pz2UsIalulM33lzem5CMUr8bZhblRrKiRpPFlHfiCbQr6huwFco18upmcPRzByt2fyXqhFjRbPqgd-x_HFBgiCwVcqxPFB1Kawcl45HiRWPvoy6uqZ16C8kHWGdOhTd2mL8tJCVx2EO_dXCh0GLhzNN_f29oTig-mM-Vn_ONozJcM7tHQvnXaNa1U5zccGXF5essmmOp-c8Eeu7N6HwH8rhufrbfro87L3qD5JFNFcTnhq8oTAKqGk7nl17lqKVfN-6Fh8pIvfl1mBYNmwXySGisCF1RyPJa2MHSi-NS6GbiwRSMltqumjrgnZFG81G_typRa7EAyX2Hla6VSCzw_gQY-McICJSXg8kdf6nDXIvYHZVRT4Kc096KRLDwKbssvN7MEZVEfuil0TpmSfSS_tbmowmojCq727E08BYH65kHXTLN9vJUJfvqflAXND2slYrob4njkuu4aJhjV-MjqAydGOEPhf20kzGnyDsMebIppYVh5KZ7pxM2LQvVb4Ey5J6b_gp-wzWgQhc6w4EFtFMeu23wKjj4-PxtLqWWRN5K4KZeQXP8UNCEkDCNd7JPd2ecrBBt0rsQDGsgE9XfQ6_eod-Bq6_FwKM9Xguql45-gsi-tWfApvuCCHmbWBT64rQOdxTuGMMHUW" - cfg := client.NewConfiguration() - cfg.AddDefaultHeader("X-Secret", "CHANGE-ME-INSECURE-PASSWORD") - cfg.Servers = []client.ServerConfiguration{ - { - URL: "http://oauth2.logidex.ru/admin", - }, - } - cl := client.NewAPIClient(cfg) - ctx := context.Background() - //cl.PublicApi.Oauth2Token(ctx).Code() - - req := client.AcceptConsentRequest{} - req.SetGrantScope([]string{"openid"}) - - //req := client.AcceptLoginRequest{} - //req.SetSubject("someuser") - //req.SetRemember(true) - //req.SetRememberFor(3600) - //rsp, _, err := cl.AdminApi.AcceptLoginRequest(ctx).LoginChallenge(loginChallange).AcceptLoginRequest(req).Execute() - rsp, _, err := cl.AdminApi.AcceptConsentRequest(ctx).ConsentChallenge(loginChallange).AcceptConsentRequest(req).Execute() + config.Init() + err := redis.Init() if err != nil { - fmt.Println(err) - return + panic(err) } - fmt.Println(rsp.RedirectTo) - return + hydra_client.InitClient() + app := fiber.New() + api := app.Group("/api") + authApi.RegisterApp(api) - todo_api.RegisterApp(app) - user_api.RegisterApp(app) - - app.Listen(":8080") - fmt.Println("test") + err = app.Listen(":" + strconv.Itoa(config.Cfg.App.Port)) + if err != nil { + panic(err) + } } diff --git a/go.mod b/go.mod index 486cb66..0e82ea2 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,12 @@ require ( github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/structtag v1.2.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-sql-driver/mysql v1.9.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gofiber/fiber/v2 v2.52.9 // indirect github.com/google/cel-go v0.24.1 // indirect github.com/google/uuid v1.6.0 // indirect @@ -40,22 +42,30 @@ require ( github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/ory/hydra-client-go v1.11.8 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pganalyze/pg_query_go/v6 v6.1.0 // indirect github.com/pingcap/errors v0.11.5-0.20240311024730-e056997136bb // indirect github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86 // indirect github.com/pingcap/log v1.1.0 // indirect github.com/pingcap/tidb/pkg/parser v0.0.0-20250324122243-d51e00e5bbf0 // indirect + github.com/redis/rueidis v1.0.63 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/riza-io/grpc-go v0.2.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/samber/lo v1.51.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect github.com/speakeasy-api/jsonpath v0.6.0 // indirect github.com/speakeasy-api/openapi-overlay v0.10.2 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/sqlc-dev/sqlc v1.29.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tetratelabs/wazero v1.9.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect diff --git a/go.sum b/go.sum index 8e1ac9c..95224e5 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +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/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -83,6 +85,8 @@ github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +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/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -203,6 +207,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/ory/hydra-client-go v1.11.8 h1:GwJjvH/DBcfYzoST4vUpi4pIRzDGH5oODKpIVuhwVyc= github.com/ory/hydra-client-go v1.11.8/go.mod h1:4YuBuwUEC4yiyDrnKjGYc1tB3gUXan4ZiUYMjXJbfxA= +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/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pganalyze/pg_query_go/v6 v6.1.0 h1:jG5ZLhcVgL1FAw4C/0VNQaVmX1SUJx71wBGdtTtBvls= @@ -219,6 +225,8 @@ github.com/pingcap/tidb/pkg/parser v0.0.0-20250324122243-d51e00e5bbf0/go.mod h1: github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/redis/rueidis v1.0.63 h1:zSt5focn0YgrgBAE5NcnAibyKf3ZKyv+eCQHk62jEFk= +github.com/redis/rueidis v1.0.63/go.mod h1:Lkhr2QTgcoYBhxARU7kJRO8SyVlgUuEkcJO1Y8MCluA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -227,17 +235,27 @@ github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI= github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +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/speakeasy-api/jsonpath v0.6.0 h1:IhtFOV9EbXplhyRqsVhHoBmmYjblIRh5D1/g8DHMXJ8= github.com/speakeasy-api/jsonpath v0.6.0/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= github.com/speakeasy-api/openapi-overlay v0.10.2 h1:VOdQ03eGKeiHnpb1boZCGm7x8Haj6gST0P3SGTX95GU= github.com/speakeasy-api/openapi-overlay v0.10.2/go.mod h1:n0iOU7AqKpNFfEt6tq7qYITC4f0yzVVdFw0S7hukemg= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/sqlc-dev/sqlc v1.29.0 h1:HQctoD7y/i29Bao53qXO7CZ/BV9NcvpGpsJWvz9nKWs= github.com/sqlc-dev/sqlc v1.29.0/go.mod h1:BavmYw11px5AdPOjAVHmb9fctP5A8GTziC38wBF9tp0= @@ -248,6 +266,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= diff --git a/internal/api/auth/handler/generate.go b/internal/api/auth/handler/generate.go new file mode 100644 index 0000000..5f7e4ab --- /dev/null +++ b/internal/api/auth/handler/generate.go @@ -0,0 +1,4 @@ +package handler + +//go:generate go tool oapi-codegen -config ../../../../api/auth/cfg.yaml ../../../../api/auth/api.yam +//go:generate go tool oapi-codegen -config ../../../../api/auth/cfg.yaml ../../../../api/auth/api.yaml diff --git a/internal/api/auth/handler/impl.go b/internal/api/auth/handler/impl.go new file mode 100644 index 0000000..09aeb45 --- /dev/null +++ b/internal/api/auth/handler/impl.go @@ -0,0 +1,114 @@ +package handler + +import ( + "context" + "fmt" + "git.logidex.ru/fakz9/logidex-id/internal/hydra_client" + "git.logidex.ru/fakz9/logidex-id/internal/redis" + "github.com/gofiber/fiber/v2" + hydraApi "github.com/ory/hydra-client-go" +) + +type AuthHandler struct{} + +func (a AuthHandler) PostAuthConsentAccept(ctx context.Context, request PostAuthConsentAcceptRequestObject) (PostAuthConsentAcceptResponseObject, error) { + hydraClient := hydra_client.GetClient() + + hydraRequest := hydraApi.AcceptConsentRequest{} + hydraRequest.SetGrantScope([]string{"openid"}) + hydraRequest.SetRemember(true) + hydraRequest.SetRememberFor(3600) // 1 hour + + hydraResponse, r, err := hydraClient.AdminApi. + AcceptConsentRequest(ctx). + ConsentChallenge(request.Body.ConsentChallenge). + AcceptConsentRequest(hydraRequest). + Execute() + if err != nil { + return PostAuthConsentAccept400JSONResponse{ + RedirectUrl: "", + Ok: false, + Message: "Failed to accept consent request", + }, nil + } + fmt.Println(r) + return PostAuthConsentAccept200JSONResponse{ + RedirectUrl: hydraResponse.RedirectTo, + Ok: true, + Message: "Успешно", + }, nil +} + +func (a AuthHandler) PostAuthOtpRequest(ctx context.Context, request PostAuthOtpRequestRequestObject) (PostAuthOtpRequestResponseObject, error) { + redisClient := redis.GetClient() + + // TODO implement OTP request logic + + err := redisClient.Do(ctx, redisClient.B().Set().Key("otp:"+request.Body.PhoneNumber).Value("123456").Build()).Error() + if err != nil { + return PostAuthOtpRequest400JSONResponse{ + Message: "Failed to set OTP in Redis", + Ok: false, + }, nil + } + + return PostAuthOtpRequest200JSONResponse{ + Message: "Код успешно отправлен", + Ok: true, + }, nil +} + +func (a AuthHandler) PostAuthOtpVerify(ctx context.Context, request PostAuthOtpVerifyRequestObject) (PostAuthOtpVerifyResponseObject, error) { + redisClient := redis.GetClient() + hydraClient := hydra_client.GetClient() + + sentOtp, err := redisClient.Do(ctx, redisClient.B().Get().Key("otp:"+request.Body.PhoneNumber).Build()).ToString() + if err != nil { + return PostAuthOtpVerify400JSONResponse{ + RedirectUrl: "", + Ok: false, + }, nil + } + if sentOtp != request.Body.Otp { + return PostAuthOtpVerify400JSONResponse{ + RedirectUrl: "", + Ok: false, + }, nil + } + hydraRequest := hydraApi.AcceptLoginRequest{} + // TODO read user from database by phone number + + hydraRequest.SetSubject("some-user-id") // Replace with actual user ID + hydraRequest.SetRemember(true) + hydraRequest.SetRememberFor(3600) // 1 hour + + hydraResponse, r, err := hydraClient.AdminApi. + AcceptLoginRequest(ctx). + LoginChallenge(request.Body.LoginChallenge). + AcceptLoginRequest(hydraRequest). + Execute() + fmt.Println(r) + if err != nil { + return PostAuthOtpVerify400JSONResponse{ + RedirectUrl: "", + Ok: false, + }, nil + } + return PostAuthOtpVerify200JSONResponse{ + RedirectUrl: hydraResponse.RedirectTo, + Ok: true, + }, nil +} + +var _ StrictServerInterface = (*AuthHandler)(nil) + +func NewAuthHandler() *AuthHandler { + return &AuthHandler{} +} + +func RegisterApp(router fiber.Router) { + //authGroup := router.Group("/auth") + server := NewStrictHandler(NewAuthHandler(), nil) + RegisterHandlers(router, server) + +} diff --git a/internal/api/todo/handler/gen.go b/internal/api/todo/handler/gen.go deleted file mode 100644 index fa81810..0000000 --- a/internal/api/todo/handler/gen.go +++ /dev/null @@ -1,441 +0,0 @@ -// Package handler provides primitives to interact with the openapi HTTP API. -// -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. -package handler - -import ( - "context" - "fmt" - - "github.com/gofiber/fiber/v2" - "github.com/oapi-codegen/runtime" -) - -// Todo defines model for Todo. -type Todo struct { - Completed bool `json:"completed"` - Description *string `json:"description,omitempty"` - Id string `json:"id"` - Title string `json:"title"` -} - -// TodoCreate defines model for TodoCreate. -type TodoCreate struct { - Description *string `json:"description,omitempty"` - Title string `json:"title"` -} - -// TodoUpdate defines model for TodoUpdate. -type TodoUpdate struct { - Completed *bool `json:"completed,omitempty"` - Description *string `json:"description,omitempty"` - Title *string `json:"title,omitempty"` -} - -// PostTodosJSONRequestBody defines body for PostTodos for application/json ContentType. -type PostTodosJSONRequestBody = TodoCreate - -// PutTodosTodoIdJSONRequestBody defines body for PutTodosTodoId for application/json ContentType. -type PutTodosTodoIdJSONRequestBody = TodoUpdate - -// ServerInterface represents all server handlers. -type ServerInterface interface { - // Get all todos - // (GET /todos) - GetTodos(c *fiber.Ctx) error - // Create a new todo - // (POST /todos) - PostTodos(c *fiber.Ctx) error - // Delete a todo by ID - // (DELETE /todos/{todoId}) - DeleteTodosTodoId(c *fiber.Ctx, todoId string) error - // Get a todo by ID - // (GET /todos/{todoId}) - GetTodosTodoId(c *fiber.Ctx, todoId string) error - // Update a todo by ID - // (PUT /todos/{todoId}) - PutTodosTodoId(c *fiber.Ctx, todoId string) error -} - -// ServerInterfaceWrapper converts contexts to parameters. -type ServerInterfaceWrapper struct { - Handler ServerInterface -} - -type MiddlewareFunc fiber.Handler - -// GetTodos operation middleware -func (siw *ServerInterfaceWrapper) GetTodos(c *fiber.Ctx) error { - - return siw.Handler.GetTodos(c) -} - -// PostTodos operation middleware -func (siw *ServerInterfaceWrapper) PostTodos(c *fiber.Ctx) error { - - return siw.Handler.PostTodos(c) -} - -// DeleteTodosTodoId operation middleware -func (siw *ServerInterfaceWrapper) DeleteTodosTodoId(c *fiber.Ctx) error { - - var err error - - // ------------- Path parameter "todoId" ------------- - var todoId string - - err = runtime.BindStyledParameterWithOptions("simple", "todoId", c.Params("todoId"), &todoId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter todoId: %w", err).Error()) - } - - return siw.Handler.DeleteTodosTodoId(c, todoId) -} - -// GetTodosTodoId operation middleware -func (siw *ServerInterfaceWrapper) GetTodosTodoId(c *fiber.Ctx) error { - - var err error - - // ------------- Path parameter "todoId" ------------- - var todoId string - - err = runtime.BindStyledParameterWithOptions("simple", "todoId", c.Params("todoId"), &todoId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter todoId: %w", err).Error()) - } - - return siw.Handler.GetTodosTodoId(c, todoId) -} - -// PutTodosTodoId operation middleware -func (siw *ServerInterfaceWrapper) PutTodosTodoId(c *fiber.Ctx) error { - - var err error - - // ------------- Path parameter "todoId" ------------- - var todoId string - - err = runtime.BindStyledParameterWithOptions("simple", "todoId", c.Params("todoId"), &todoId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter todoId: %w", err).Error()) - } - - return siw.Handler.PutTodosTodoId(c, todoId) -} - -// FiberServerOptions provides options for the Fiber server. -type FiberServerOptions struct { - BaseURL string - Middlewares []MiddlewareFunc -} - -// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. -func RegisterHandlers(router fiber.Router, si ServerInterface) { - RegisterHandlersWithOptions(router, si, FiberServerOptions{}) -} - -// RegisterHandlersWithOptions creates http.Handler with additional options -func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, options FiberServerOptions) { - wrapper := ServerInterfaceWrapper{ - Handler: si, - } - - for _, m := range options.Middlewares { - router.Use(fiber.Handler(m)) - } - - router.Get(options.BaseURL+"/todos", wrapper.GetTodos) - - router.Post(options.BaseURL+"/todos", wrapper.PostTodos) - - router.Delete(options.BaseURL+"/todos/:todoId", wrapper.DeleteTodosTodoId) - - router.Get(options.BaseURL+"/todos/:todoId", wrapper.GetTodosTodoId) - - router.Put(options.BaseURL+"/todos/:todoId", wrapper.PutTodosTodoId) - -} - -type GetTodosRequestObject struct { -} - -type GetTodosResponseObject interface { - VisitGetTodosResponse(ctx *fiber.Ctx) error -} - -type GetTodos200JSONResponse []Todo - -func (response GetTodos200JSONResponse) VisitGetTodosResponse(ctx *fiber.Ctx) error { - ctx.Response().Header.Set("Content-Type", "application/json") - ctx.Status(200) - - return ctx.JSON(&response) -} - -type PostTodosRequestObject struct { - Body *PostTodosJSONRequestBody -} - -type PostTodosResponseObject interface { - VisitPostTodosResponse(ctx *fiber.Ctx) error -} - -type PostTodos201JSONResponse Todo - -func (response PostTodos201JSONResponse) VisitPostTodosResponse(ctx *fiber.Ctx) error { - ctx.Response().Header.Set("Content-Type", "application/json") - ctx.Status(201) - - return ctx.JSON(&response) -} - -type DeleteTodosTodoIdRequestObject struct { - TodoId string `json:"todoId"` -} - -type DeleteTodosTodoIdResponseObject interface { - VisitDeleteTodosTodoIdResponse(ctx *fiber.Ctx) error -} - -type DeleteTodosTodoId204Response struct { -} - -func (response DeleteTodosTodoId204Response) VisitDeleteTodosTodoIdResponse(ctx *fiber.Ctx) error { - ctx.Status(204) - return nil -} - -type DeleteTodosTodoId404Response struct { -} - -func (response DeleteTodosTodoId404Response) VisitDeleteTodosTodoIdResponse(ctx *fiber.Ctx) error { - ctx.Status(404) - return nil -} - -type GetTodosTodoIdRequestObject struct { - TodoId string `json:"todoId"` -} - -type GetTodosTodoIdResponseObject interface { - VisitGetTodosTodoIdResponse(ctx *fiber.Ctx) error -} - -type GetTodosTodoId200JSONResponse Todo - -func (response GetTodosTodoId200JSONResponse) VisitGetTodosTodoIdResponse(ctx *fiber.Ctx) error { - ctx.Response().Header.Set("Content-Type", "application/json") - ctx.Status(200) - - return ctx.JSON(&response) -} - -type GetTodosTodoId404Response struct { -} - -func (response GetTodosTodoId404Response) VisitGetTodosTodoIdResponse(ctx *fiber.Ctx) error { - ctx.Status(404) - return nil -} - -type PutTodosTodoIdRequestObject struct { - TodoId string `json:"todoId"` - Body *PutTodosTodoIdJSONRequestBody -} - -type PutTodosTodoIdResponseObject interface { - VisitPutTodosTodoIdResponse(ctx *fiber.Ctx) error -} - -type PutTodosTodoId200JSONResponse Todo - -func (response PutTodosTodoId200JSONResponse) VisitPutTodosTodoIdResponse(ctx *fiber.Ctx) error { - ctx.Response().Header.Set("Content-Type", "application/json") - ctx.Status(200) - - return ctx.JSON(&response) -} - -type PutTodosTodoId404Response struct { -} - -func (response PutTodosTodoId404Response) VisitPutTodosTodoIdResponse(ctx *fiber.Ctx) error { - ctx.Status(404) - return nil -} - -// StrictServerInterface represents all server handlers. -type StrictServerInterface interface { - // Get all todos - // (GET /todos) - GetTodos(ctx context.Context, request GetTodosRequestObject) (GetTodosResponseObject, error) - // Create a new todo - // (POST /todos) - PostTodos(ctx context.Context, request PostTodosRequestObject) (PostTodosResponseObject, error) - // Delete a todo by ID - // (DELETE /todos/{todoId}) - DeleteTodosTodoId(ctx context.Context, request DeleteTodosTodoIdRequestObject) (DeleteTodosTodoIdResponseObject, error) - // Get a todo by ID - // (GET /todos/{todoId}) - GetTodosTodoId(ctx context.Context, request GetTodosTodoIdRequestObject) (GetTodosTodoIdResponseObject, error) - // Update a todo by ID - // (PUT /todos/{todoId}) - PutTodosTodoId(ctx context.Context, request PutTodosTodoIdRequestObject) (PutTodosTodoIdResponseObject, error) -} - -type StrictHandlerFunc func(ctx *fiber.Ctx, args interface{}) (interface{}, error) - -type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc - -func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { - return &strictHandler{ssi: ssi, middlewares: middlewares} -} - -type strictHandler struct { - ssi StrictServerInterface - middlewares []StrictMiddlewareFunc -} - -// GetTodos operation middleware -func (sh *strictHandler) GetTodos(ctx *fiber.Ctx) error { - var request GetTodosRequestObject - - handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { - return sh.ssi.GetTodos(ctx.UserContext(), request.(GetTodosRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetTodos") - } - - response, err := handler(ctx, request) - - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } else if validResponse, ok := response.(GetTodosResponseObject); ok { - if err := validResponse.VisitGetTodosResponse(ctx); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PostTodos operation middleware -func (sh *strictHandler) PostTodos(ctx *fiber.Ctx) error { - var request PostTodosRequestObject - - var body PostTodosJSONRequestBody - if err := ctx.BodyParser(&body); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - request.Body = &body - - handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { - return sh.ssi.PostTodos(ctx.UserContext(), request.(PostTodosRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostTodos") - } - - response, err := handler(ctx, request) - - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } else if validResponse, ok := response.(PostTodosResponseObject); ok { - if err := validResponse.VisitPostTodosResponse(ctx); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// DeleteTodosTodoId operation middleware -func (sh *strictHandler) DeleteTodosTodoId(ctx *fiber.Ctx, todoId string) error { - var request DeleteTodosTodoIdRequestObject - - request.TodoId = todoId - - handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { - return sh.ssi.DeleteTodosTodoId(ctx.UserContext(), request.(DeleteTodosTodoIdRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "DeleteTodosTodoId") - } - - response, err := handler(ctx, request) - - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } else if validResponse, ok := response.(DeleteTodosTodoIdResponseObject); ok { - if err := validResponse.VisitDeleteTodosTodoIdResponse(ctx); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// GetTodosTodoId operation middleware -func (sh *strictHandler) GetTodosTodoId(ctx *fiber.Ctx, todoId string) error { - var request GetTodosTodoIdRequestObject - - request.TodoId = todoId - - handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { - return sh.ssi.GetTodosTodoId(ctx.UserContext(), request.(GetTodosTodoIdRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetTodosTodoId") - } - - response, err := handler(ctx, request) - - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } else if validResponse, ok := response.(GetTodosTodoIdResponseObject); ok { - if err := validResponse.VisitGetTodosTodoIdResponse(ctx); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} - -// PutTodosTodoId operation middleware -func (sh *strictHandler) PutTodosTodoId(ctx *fiber.Ctx, todoId string) error { - var request PutTodosTodoIdRequestObject - - request.TodoId = todoId - - var body PutTodosTodoIdJSONRequestBody - if err := ctx.BodyParser(&body); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - request.Body = &body - - handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { - return sh.ssi.PutTodosTodoId(ctx.UserContext(), request.(PutTodosTodoIdRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PutTodosTodoId") - } - - response, err := handler(ctx, request) - - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } else if validResponse, ok := response.(PutTodosTodoIdResponseObject); ok { - if err := validResponse.VisitPutTodosTodoIdResponse(ctx); err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - } else if response != nil { - return fmt.Errorf("unexpected response type: %T", response) - } - return nil -} diff --git a/internal/api/todo/handler/generate.go b/internal/api/todo/handler/generate.go deleted file mode 100644 index 3ff083b..0000000 --- a/internal/api/todo/handler/generate.go +++ /dev/null @@ -1,3 +0,0 @@ -package handler - -//go:generate go tool oapi-codegen -config ../../../../api/todo/cfg.yaml ../../../../api/todo/todo.yaml diff --git a/internal/api/todo/handler/impl.go b/internal/api/todo/handler/impl.go deleted file mode 100644 index 1638330..0000000 --- a/internal/api/todo/handler/impl.go +++ /dev/null @@ -1,46 +0,0 @@ -package handler - -import ( - "context" - "github.com/gofiber/fiber/v2" -) - -type TodoHandler struct { -} - -var _ StrictServerInterface = (*TodoHandler)(nil) - -func (t TodoHandler) GetTodos(ctx context.Context, request GetTodosRequestObject) (GetTodosResponseObject, error) { - //TODO implement me - panic("implement me") -} - -func (t TodoHandler) PostTodos(ctx context.Context, request PostTodosRequestObject) (PostTodosResponseObject, error) { - //TODO implement me - panic("implement me") -} - -func (t TodoHandler) DeleteTodosTodoId(ctx context.Context, request DeleteTodosTodoIdRequestObject) (DeleteTodosTodoIdResponseObject, error) { - //TODO implement me - panic("implement me") -} - -func (t TodoHandler) GetTodosTodoId(ctx context.Context, request GetTodosTodoIdRequestObject) (GetTodosTodoIdResponseObject, error) { - //TODO implement me - panic("implement me") -} - -func (t TodoHandler) PutTodosTodoId(ctx context.Context, request PutTodosTodoIdRequestObject) (PutTodosTodoIdResponseObject, error) { - //TODO implement me - panic("implement me") -} - -func NewTodoHandler() *TodoHandler { - return &TodoHandler{} -} - -func RegisterApp(router fiber.Router) { - server := NewStrictHandler(NewTodoHandler(), nil) - RegisterHandlers(router, server) - -} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..00d155f --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,52 @@ +package config + +import ( + "github.com/joho/godotenv" + "github.com/spf13/viper" + "log" +) + +type Config struct { + App struct { + Port int + } + Redis struct { + Host string + Port int + DB int + Password string + } + Hydra struct { + Host string + Password string + } +} + +var Cfg *Config + +func Init() { + err := godotenv.Load() + if err != nil { + log.Println("Error loading .env file") + } + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("Error reading config file, %s", err) + } + + viper.AutomaticEnv() + var config Config + + _ = viper.BindEnv("redis.password", "REDIS_PASSWORD") + _ = viper.BindEnv("hydra.password", "HYDRA_PASSWORD") + + err = viper.Unmarshal(&config) + if err != nil { + log.Fatalf("Unable to decode config into struct, %v", err) + } + Cfg = &config +} diff --git a/internal/hydra_client/hydra_client.go b/internal/hydra_client/hydra_client.go new file mode 100644 index 0000000..09253ad --- /dev/null +++ b/internal/hydra_client/hydra_client.go @@ -0,0 +1,28 @@ +package hydra_client + +import ( + "git.logidex.ru/fakz9/logidex-id/internal/config" + hydraApi "github.com/ory/hydra-client-go" + "sync" +) + +var ( + client *hydraApi.APIClient + initClient sync.Once +) + +func InitClient() { + cfg := hydraApi.NewConfiguration() + cfg.AddDefaultHeader("X-Secret", config.Cfg.Hydra.Password) + cfg.Servers = []hydraApi.ServerConfiguration{ + { + URL: config.Cfg.Hydra.Host, + }, + } + client = hydraApi.NewAPIClient(cfg) +} + +func GetClient() *hydraApi.APIClient { + initClient.Do(InitClient) + return client +} diff --git a/internal/redis/client.go b/internal/redis/client.go new file mode 100644 index 0000000..dc0f75a --- /dev/null +++ b/internal/redis/client.go @@ -0,0 +1,26 @@ +package redis + +import ( + "git.logidex.ru/fakz9/logidex-id/internal/config" + "github.com/redis/rueidis" + "strconv" +) + +var client rueidis.Client + +func Init() error { + var err error + client, err = rueidis.NewClient(rueidis.ClientOption{ + // Set the address of your Redis server + InitAddress: []string{config.Cfg.Redis.Host + ":" + strconv.Itoa(config.Cfg.Redis.Port)}, + Password: config.Cfg.Redis.Password, + }) + if err != nil { + return err + } + return nil +} + +func GetClient() rueidis.Client { + return client +} diff --git a/openapi.yaml b/openapi.yaml index 217a381..17b6c59 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -4,6 +4,13 @@ info: version: 1.0.0 paths: - /auth: - $ref: './api/auth/api.yaml#/paths' + $ref: './api/auth/api.yaml#/paths' + +components: + schemas: + $ref: './api/auth/api.yaml#/components/schemas' + +tags: + - name: auth + description: Authentication operations \ No newline at end of file