diff --git a/.env.example b/.env.example index 593a860..0fb76db 100644 --- a/.env.example +++ b/.env.example @@ -8,8 +8,5 @@ AUTH_PROVIDER=firebase FIREBASE_CREDENTIALS='{firebase json admin key}' FIREBASE_AUTH_CREDENTIALS='{filebase json auth key}' -# KRATOS_API_ENDPOINT=http://kratos:4433/ -# KRATOS_WEBHOOK_API_KEY=very-very-very-secure-api-key - AUTO_MIGRATE=true PORT=8088 \ No newline at end of file diff --git a/README.md b/README.md index 7e82e3e..fcfd790 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,14 @@ Any feedback and pull requests are welcome and highly appreciated. Feel free to - [Echo REST API boilerplate](#echo-rest-api-boilerplate) - [HOW TO USE THIS TEMPLATE](#how-to-use-this-template) + - [Overview](#overview) - [Features](#features) - [Running the project](#running-the-project) + - [Swagger Docs](#swagger-docs) - [Environment variables](#environment-variables) - [Commands](#commands) - - [Folder structure](#folder-structure) - - [Open source refs](#open-source-refs) + - [Folder Structure](#folder-structure) + - [Open Source Refs](#open-source-refs) - [Contributing](#contributing) - [TODOs](#todos) @@ -39,8 +41,6 @@ Any feedback and pull requests are welcome and highly appreciated. Feel free to ## Features @@ -86,8 +86,6 @@ Setting your config as Environment Variables is recommended as by 12-Factor App. | AUTH_PROVIDER | string | Optional | firebase_auth | | FIREBASE_CREDENTIALS | json | firebase json admin key | {firebase_admin_key} | | FIREBASE_AUTH_CREDENTIALS | json | filebase json auth key | {firebase_auth_key} | -| KRATOS_API_ENDPOINT | string | [DEPRECATED] Public endpoint of Kratos | http://kratos:4433/ | -| KRATOS_WEBHOOK_API_KEY | string | [DEPRECATED] Api key for Kratos integration | very-very-very-secure-api-key | ## Commands diff --git a/go.mod b/go.mod index b016e15..a3e366c 100644 --- a/go.mod +++ b/go.mod @@ -20,12 +20,10 @@ require ( github.com/lib/pq v1.10.6 github.com/lithammer/shortuuid/v4 v4.0.0 github.com/open-policy-agent/opa v0.54.0 - github.com/ory/kratos-client-go v0.11.1 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.2 github.com/swaggo/echo-swagger v1.4.0 github.com/swaggo/swag v1.16.1 - github.com/tidwall/gjson v1.17.0 github.com/tidwall/sjson v1.2.5 go.uber.org/dig v1.17.0 go.uber.org/zap v1.24.0 @@ -104,6 +102,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect @@ -118,12 +117,12 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.8.0 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/go.sum b/go.sum index 38a5043..2dd1d88 100644 --- a/go.sum +++ b/go.sum @@ -1070,8 +1070,6 @@ github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3 github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/ory/kratos-client-go v0.11.1 h1:2f+WZ94NReAUQlxdV/pp1ipScy5o5gYLHt2ThSNHHeA= -github.com/ory/kratos-client-go v0.11.1/go.mod h1:G31LzGZxgtMblK36qaTPWjG5OqnUWrncO1MjKkHsFUE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= @@ -1414,8 +1412,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1701,8 +1699,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1720,8 +1718,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/modules/core/handlers/kratos.go b/modules/core/handlers/kratos.go deleted file mode 100644 index 1cee510..0000000 --- a/modules/core/handlers/kratos.go +++ /dev/null @@ -1,118 +0,0 @@ -package handlers - -import ( - "io" - "net/http" - - "github.com/dzungtran/echo-rest-api/modules/core/dto" - "github.com/dzungtran/echo-rest-api/modules/core/usecases" - "github.com/dzungtran/echo-rest-api/pkg/logger" - "github.com/dzungtran/echo-rest-api/pkg/middlewares" - "github.com/dzungtran/echo-rest-api/pkg/utils" - "github.com/dzungtran/echo-rest-api/pkg/wrapper" - "github.com/jinzhu/copier" - "github.com/labstack/echo/v4" - "github.com/tidwall/gjson" -) - -type KratosHookHandler struct { - UserUC usecases.UserUsecase -} - -// NewKratosHookHandler will initialize the user resources endpoint -func NewKratosHookHandler(g *echo.Group, middManager *middlewares.MiddlewareManager, userUsecase usecases.UserUsecase) { - handler := &KratosHookHandler{ - UserUC: userUsecase, - } - - apiV1 := g.Group("hooks/kratos", middManager.KratosWebhookAuth) - apiV1.POST("/after-registration", wrapper.Wrap(handler.AfterRegistration)) - apiV1.POST("/after-settings", wrapper.Wrap(handler.AfterSettings)) -} - -// Create will store the user by given request body -func (h *KratosHookHandler) AfterRegistration(c echo.Context) wrapper.Response { - ctx := c.Request().Context() - resp, _ := io.ReadAll(c.Request().Body) - var err error - req := dto.CreateUserReq{ - Email: gjson.GetBytes(resp, "traits.email").String(), - FirstName: gjson.GetBytes(resp, "traits.name.first").String(), - LastName: gjson.GetBytes(resp, "traits.name.last").String(), - Code: gjson.GetBytes(resp, "identity_id").String(), - } - - if len(req.Email) == 0 { - return wrapper.Response{ - Status: http.StatusBadRequest, - Error: utils.NewError(err, "invalid payload"), - } - } - - user, err := h.UserUC.GetByEmail(ctx, req.Email) - if err == nil { - // update user code - updateReq := dto.UpdateUserReq{} - copier.Copy(&updateReq, user) - copier.Copy(&updateReq, req) - - err = h.UserUC.Update(ctx, user.Id, updateReq) - if err != nil { - logger.Log().Errorw("error while update user", "error", err) - return wrapper.Response{ - Status: http.StatusInternalServerError, - Error: utils.NewError(err, "error while update user"), - } - } - return wrapper.Response{Status: http.StatusOK} - } - - _, err = h.UserUC.Register(ctx, req) - if err != nil { - if utils.IsCueError(err) { - logger.Log().Debugw("invalid create user request", "error", err) - return wrapper.Response{ - Status: http.StatusBadRequest, - Error: utils.NewError(err, "invalid payload"), - } - } - - logger.Log().Errorw("error while create user", "error", err) - return wrapper.Response{ - Status: http.StatusInternalServerError, - Error: utils.NewError(err, "error while create user"), - } - } - - return wrapper.Response{Status: http.StatusCreated} -} - -func (h *KratosHookHandler) AfterSettings(c echo.Context) wrapper.Response { - ctx := c.Request().Context() - resp, _ := io.ReadAll(c.Request().Body) - var err error - userId := int64(0) - - req := dto.UpdateUserReq{ - FirstName: gjson.GetBytes(resp, "traits.name.first").String(), - LastName: gjson.GetBytes(resp, "traits.name.last").String(), - } - - if err = h.UserUC.Update(ctx, userId, req); err != nil { - if utils.IsCueError(err) { - logger.Log().Debugw("invalid update user request", "error", err) - return wrapper.Response{ - Status: http.StatusBadRequest, - Error: utils.NewError(err, "invalid payload"), - } - } - - logger.Log().Errorw("error while create user", "error", err) - return wrapper.Response{ - Status: http.StatusInternalServerError, - Error: utils.NewError(err, "error while update user"), - } - } - - return wrapper.Response{Status: http.StatusOK} -} diff --git a/modules/core/mod.go b/modules/core/mod.go index 90e4bac..4181a17 100644 --- a/modules/core/mod.go +++ b/modules/core/mod.go @@ -36,7 +36,6 @@ func (coreModule) RegisterHandlers(g *echo.Group, container *dig.Container) erro ) { handlers.NewOrgHandler(g, middManager, orgUsecase) handlers.NewUserHandler(g, middManager, userUsecase) - handlers.NewKratosHookHandler(g, middManager, userUsecase) handlers.NewAuthHandler(g, middManager, userUsecase, appConf) }) } diff --git a/pkg/kratos/client.go b/pkg/kratos/client.go deleted file mode 100644 index 6e54811..0000000 --- a/pkg/kratos/client.go +++ /dev/null @@ -1,17 +0,0 @@ -package kratos - -import ( - "net/http" - "net/http/cookiejar" - - ory "github.com/ory/kratos-client-go" -) - -func NewKratosSelfHostedClient(endpoint string, debug bool) *ory.APIClient { - conf := ory.NewConfiguration() - conf.Servers = ory.ServerConfigurations{{URL: endpoint}} - conf.Debug = debug - cj, _ := cookiejar.New(nil) - conf.HTTPClient = &http.Client{Jar: cj} - return ory.NewAPIClient(conf) -} diff --git a/pkg/middlewares/kratos.go b/pkg/middlewares/kratos.go deleted file mode 100644 index f16baed..0000000 --- a/pkg/middlewares/kratos.go +++ /dev/null @@ -1,73 +0,0 @@ -package middlewares - -import ( - "net/http" - - "github.com/dzungtran/echo-rest-api/modules/core/domains" - "github.com/dzungtran/echo-rest-api/pkg/constants" - "github.com/dzungtran/echo-rest-api/pkg/logger" - "github.com/labstack/echo/v4" - ory "github.com/ory/kratos-client-go" -) - -func (m *MiddlewareManager) KratosWebhookAuth(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if m.appConf.KratosWebhookApiKey != c.Request().Header.Get(constants.HeaderXApiKey) { - return c.JSON(http.StatusUnauthorized, map[string]interface{}{ - "message": "Unauthorized", - }) - } - return next(c) - } -} - -func (m *MiddlewareManager) KratosAuth(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - - var ss *ory.Session - var err error - - ctx := c.Request().Context() - ss, _, err = m.kratosClient.FrontendApi.ToSession(ctx). - Cookie(c.Request().Header.Get("Cookie")). - XSessionToken(c.Request().Header.Get("X-Session-Token")). - Execute() - - if err != nil { - logger.Log().Errorw("error while fetch identity from kratos", "error", err) - return c.JSON(http.StatusUnauthorized, map[string]interface{}{ - "message": err.Error(), - }) - } - - if ss.Active == nil || !*ss.Active { - return c.JSON(http.StatusUnauthorized, map[string]interface{}{ - "message": "user is inactive", - }) - } - - _, ok := ss.Identity.GetTraitsOk() - if !ok { - return c.JSON(http.StatusUnauthorized, map[string]interface{}{ - "message": "cannot get identity info", - }) - } - - u, err := m.fetchUserFromAuth(ctx, ss.Identity.Id, "") - if err != nil { - logger.Log().Errorw("error while fetch user", "error", err, "code", ss.Identity.Id) - return c.JSON(http.StatusUnauthorized, map[string]interface{}{ - "message": "cannot fetch user info", - }) - } - - if u.Status != domains.UserStatusActive { - return c.JSON(http.StatusUnauthorized, map[string]interface{}{ - "message": "user is not active", - }) - } - - c.Set(constants.ContextKeyUser, u) - return next(c) - } -} diff --git a/pkg/middlewares/middleware.go b/pkg/middlewares/middleware.go index 34a06d4..c2dea93 100644 --- a/pkg/middlewares/middleware.go +++ b/pkg/middlewares/middleware.go @@ -13,10 +13,8 @@ import ( "github.com/dzungtran/echo-rest-api/pkg/authz" "github.com/dzungtran/echo-rest-api/pkg/constants" "github.com/dzungtran/echo-rest-api/pkg/contexts" - "github.com/dzungtran/echo-rest-api/pkg/kratos" "github.com/dzungtran/echo-rest-api/pkg/utils" "github.com/labstack/echo/v4" - ory "github.com/ory/kratos-client-go" ) // MiddlewareManager ... @@ -29,8 +27,7 @@ type MiddlewareManager struct { orgRepo coreRepo.OrgRepository projectRepo projectRepo.ProjectRepository - userUC usecases.UserUsecase - kratosClient *ory.APIClient + userUC usecases.UserUsecase } // NewMiddlewareManager will create new an MiddlewareManager object @@ -45,12 +42,11 @@ func NewMiddlewareManager( userUC usecases.UserUsecase, ) *MiddlewareManager { return &MiddlewareManager{ - appConf: appConf, - userRepo: userRepo, - userOrgRepo: userOrgRepo, - orgRepo: orgRepo, - userUC: userUC, - kratosClient: kratos.NewKratosSelfHostedClient(appConf.KratosApiEndpoint, appConf.Environment == "development"), + appConf: appConf, + userRepo: userRepo, + userOrgRepo: userOrgRepo, + orgRepo: orgRepo, + userUC: userUC, } } @@ -58,8 +54,6 @@ func (m MiddlewareManager) Auth() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { switch m.appConf.AuthProvider { - case "kratos": - return m.KratosAuth(next) default: // Default auth method return m.FireBaseAuth(next)