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 464cdae..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.15.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 @@ -119,7 +118,7 @@ require ( go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.11.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.15.0 // indirect diff --git a/go.sum b/go.sum index 7cf089d..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= @@ -1241,8 +1239,8 @@ github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.15.0 h1:5n/pM+v3r5ujuNl4YLZLsQ+UE5jlkLVm7jMzT5Mpolw= -github.com/tidwall/gjson v1.15.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -1537,8 +1535,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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)