From 1c825d6196637dfb378756ca48e7aaff530f0063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20D=C3=B6ll?= Date: Sat, 19 Oct 2024 20:18:13 +0000 Subject: [PATCH] wip: setup credentials provider --- examples/main.go | 2 +- go.mod | 6 +- go.sum | 19 +++++- goth.go | 4 +- providers/credentials/credentials.go | 98 ++++++++++++++++++++++++++++ providers/entraid/entraid.go | 2 +- providers/github/github.go | 2 +- providers/providers.go | 4 +- 8 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 providers/credentials/credentials.go diff --git a/examples/main.go b/examples/main.go index 74bc146..fc4347d 100644 --- a/examples/main.go +++ b/examples/main.go @@ -137,7 +137,7 @@ func run(_ context.Context) error { return t.Execute(c.Response().BodyWriter(), providerIndex) }) app.Get("/session", goth.NewSessionHandler(gothConfig)) - app.Get("/login/:provider", goth.NewBeginAuthHandler(gothConfig)) + app.Use("/login/:provider", goth.NewBeginAuthHandler(gothConfig)) app.Get("/auth/:provider/callback", goth.NewCompleteAuthHandler(gothConfig)) app.Get("/logout", goth.NewLogoutHandler(gothConfig)) diff --git a/go.mod b/go.mod index c2e2018..e8cb2f9 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/katallaxie/pkg v0.6.6 github.com/spf13/cobra v1.8.1 github.com/valyala/fasthttp v1.56.0 - github.com/zeiss/pkg v0.1.12 + github.com/zeiss/pkg v0.1.13-0.20241019201052-9f5bf9d1a0df golang.org/x/oauth2 v0.23.0 gorm.io/driver/postgres v1.5.9 gorm.io/gorm v1.25.12 @@ -195,14 +195,18 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.28.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/tools v0.24.0 // indirect google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 0c8a0c3..0e30c81 100644 --- a/go.sum +++ b/go.sum @@ -427,8 +427,12 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeiss/pkg v0.1.12 h1:3fydksqtda+rH5EIFZvVhdriBN2SCTVUwFq6t4khaQk= -github.com/zeiss/pkg v0.1.12/go.mod h1:a/VpZ+rOSIet4wPu3MZVQ/H4h2JXV6hNe8xGUh91+6c= +github.com/zeiss/pkg v0.1.9 h1:4a7XOcdthHZvftvm8hNX6Ml4nT6/g9b2ottcUV0F6/0= +github.com/zeiss/pkg v0.1.9/go.mod h1:W2z48YhXJRD0aGCcRPNSnRrJf4Gw5IlbY6ORJ144f0E= +github.com/zeiss/pkg v0.1.13-0.20241019200515-79d2e81f249d h1:N2nr+ptk4BBC3W5PssYtyKhh3jqNDk4QRPfw9T3KVKs= +github.com/zeiss/pkg v0.1.13-0.20241019200515-79d2e81f249d/go.mod h1:RAQyzmnyfiXtnHJGb1o8E/Bf1MCiA0FYPBC1RuFpINk= +github.com/zeiss/pkg v0.1.13-0.20241019201052-9f5bf9d1a0df h1:RpHj41NcJ5d4AO8mBz5qYduPk/K6729ioHqlVSn7fxE= +github.com/zeiss/pkg v0.1.13-0.20241019201052-9f5bf9d1a0df/go.mod h1:RAQyzmnyfiXtnHJGb1o8E/Bf1MCiA0FYPBC1RuFpINk= gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= @@ -450,6 +454,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= @@ -484,8 +490,9 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -520,6 +527,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -536,6 +545,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -567,6 +578,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/goth.go b/goth.go index 5ab615f..4f56d88 100644 --- a/goth.go +++ b/goth.go @@ -179,7 +179,7 @@ func (BeginAuthHandler) New(cfg Config) fiber.Handler { return err } - intent, err := provider.BeginAuth(c.Context(), cfg.Adapter, state) + intent, err := provider.BeginAuth(c.Context(), cfg.Adapter, state, &Params{ctx: c}) if err != nil { return err } @@ -266,7 +266,7 @@ func (CompleteAuthCompleteHandler) New(cfg Config) fiber.Handler { } } -// NewBeginCompleteAuthHandler creates a new middleware handler to complete authentication. +// NewCompleteAuthHandler creates a new middleware handler to complete authentication. func NewCompleteAuthHandler(config ...Config) fiber.Handler { cfg := configDefault(config...) diff --git a/providers/credentials/credentials.go b/providers/credentials/credentials.go new file mode 100644 index 0000000..29645ca --- /dev/null +++ b/providers/credentials/credentials.go @@ -0,0 +1,98 @@ +package credentials + +import ( + "context" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/zeiss/fiber-goth/adapters" + "github.com/zeiss/fiber-goth/providers" + "github.com/zeiss/pkg/dbx" + "golang.org/x/crypto/bcrypt" + + "gorm.io/gorm" +) + +type User struct { + // ID is the unique identifier of the user. + ID uuid.UUID `gorm:"type:uuid;primary_key;default:uuid_generate_v4()"` + // Name is the name of the user. + Name string `gorm:"size:255"` + // Email is the email of the user. + Email string `gorm:"type:varchar(100);unique_index"` + // HashedPassword is the hashed password of the user. + HashedPassword []byte + // Active is true if the user is active. + Active bool + // CreatedAt is the creation time of the user. + CreatedAt time.Time `json:"created_at"` + // UpdatedAt is the update time of the user. + UpdatedAt time.Time `json:"updated_at"` + // DeletedAt is the deletion time of the user. + DeletedAt gorm.DeletedAt `json:"deleted_at"` +} + +// SetNewPassword set a new hashsed password to user. +func (user *User) SetNewPassword(password string) error { + hash, err := dbx.HashPassword([]byte(password)) + if err != nil { + return err + } + + user.HashedPassword = hash + + return nil +} + +type credentialsProvider struct { + db *gorm.DB + + providers.UnimplementedProvider +} + +type authIntent struct { + authURL string +} + +// GetAuthURL returns the URL for the authentication end-point. +func (a *authIntent) GetAuthURL() (string, error) { + if a.authURL == "" { + return "", providers.ErrNoAuthURL + } + + return a.authURL, nil +} + +// Opt is a function that configures the credentials provider. +type Opt func(*credentialsProvider) + +// New creates a new GitHub provider. +func New(db *gorm.DB, opts ...Opt) *credentialsProvider { + p := &credentialsProvider{ + db: db, + } + + for _, opt := range opts { + opt(p) + } + + return p +} + +// HashPassword returns the bcrypt hash of the password +func HashPassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", fmt.Errorf("failed to hash password: %w", err) + } + + return string(hashedPassword), nil +} + +// BeginAuth starts the authentication process. +func (e *credentialsProvider) BeginAuth(ctx context.Context, adapter adapters.Adapter, state string, _ providers.AuthParams) (providers.AuthIntent, error) { + return &authIntent{ + authURL: "", + }, nil +} diff --git a/providers/entraid/entraid.go b/providers/entraid/entraid.go index d076695..b1df643 100644 --- a/providers/entraid/entraid.go +++ b/providers/entraid/entraid.go @@ -117,7 +117,7 @@ const ( ) // BeginAuth starts the authentication process. -func (e *entraIdProvider) BeginAuth(ctx context.Context, adapter adapters.Adapter, state string) (providers.AuthIntent, error) { +func (e *entraIdProvider) BeginAuth(ctx context.Context, adapter adapters.Adapter, state string, _ providers.AuthParams) (providers.AuthIntent, error) { url := e.config.AuthCodeURL(state) return &authIntent{ diff --git a/providers/github/github.go b/providers/github/github.go index 77d8286..cdf14df 100644 --- a/providers/github/github.go +++ b/providers/github/github.go @@ -126,7 +126,7 @@ func (a *authIntent) GetAuthURL() (string, error) { } // BeginAuth starts the authentication process. -func (g *githubProvider) BeginAuth(ctx context.Context, adapter adapters.Adapter, state string) (providers.AuthIntent, error) { +func (g *githubProvider) BeginAuth(ctx context.Context, adapter adapters.Adapter, state string, _ providers.AuthParams) (providers.AuthIntent, error) { verifier := oauth2.GenerateVerifier() url := g.config.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier)) diff --git a/providers/providers.go b/providers/providers.go index 87ab40f..63fe72d 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -35,7 +35,7 @@ type Provider interface { // Type returns the provider's type. Type() ProviderType // BeginAuth starts the authentication process. - BeginAuth(ctx context.Context, adapter adapters.Adapter, state string) (AuthIntent, error) + BeginAuth(ctx context.Context, adapter adapters.Adapter, state string, params AuthParams) (AuthIntent, error) // CompleteAuth completes the authentication process. CompleteAuth(ctx context.Context, adapter adapters.Adapter, params AuthParams) (adapters.GothUser, error) } @@ -125,7 +125,7 @@ func (u *UnimplementedProvider) Debug(debug bool) { } // BeginAuth starts the authentication process. -func (u *UnimplementedProvider) BeginAuth(_ context.Context, _ adapters.Adapter, state string) (AuthIntent, error) { +func (u *UnimplementedProvider) BeginAuth(_ context.Context, _ adapters.Adapter, state string, params AuthParams) (AuthIntent, error) { return nil, ErrUnimplemented }