From 8bc7b1e27bcaaf7dd42f0ea44aa1e9bf3d835b03 Mon Sep 17 00:00:00 2001 From: Achim Rohn Date: Wed, 17 Sep 2025 16:30:18 +0200 Subject: [PATCH] Add first login route --- starter/ent/googleauth.go | 4 +- starter/ent/googleauth_create.go | 4 +- starter/ent/googleauth_update.go | 10 +-- starter/ent/mutation.go | 14 ++--- starter/{google => ent/schema}/credentials.go | 2 +- starter/ent/schema/google_auth.go | 3 +- starter/env/environment.go | 6 +- starter/google/google_auth.go | 63 +++++++++++++++++++ starter/main.go | 19 ++++++ 9 files changed, 104 insertions(+), 21 deletions(-) rename starter/{google => ent/schema}/credentials.go (86%) create mode 100644 starter/google/google_auth.go diff --git a/starter/ent/googleauth.go b/starter/ent/googleauth.go index c823219..3e4ca51 100644 --- a/starter/ent/googleauth.go +++ b/starter/ent/googleauth.go @@ -5,8 +5,8 @@ package ent import ( "encoding/json" "ersteller-lib/starter/ent/googleauth" + "ersteller-lib/starter/ent/schema" "ersteller-lib/starter/ent/user" - "ersteller-lib/starter/google" "fmt" "strings" "time" @@ -25,7 +25,7 @@ type GoogleAuth struct { // UpdatedAt holds the value of the "updated_at" field. UpdatedAt time.Time `json:"updated_at,omitempty"` // Credentials holds the value of the "credentials" field. - Credentials google.Credentials `json:"credentials,omitempty"` + Credentials schema.Credentials `json:"credentials,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the GoogleAuthQuery when eager-loading is set. Edges GoogleAuthEdges `json:"edges"` diff --git a/starter/ent/googleauth_create.go b/starter/ent/googleauth_create.go index b0ccb63..bf0fa91 100644 --- a/starter/ent/googleauth_create.go +++ b/starter/ent/googleauth_create.go @@ -6,8 +6,8 @@ import ( "context" "errors" "ersteller-lib/starter/ent/googleauth" + "ersteller-lib/starter/ent/schema" "ersteller-lib/starter/ent/user" - "ersteller-lib/starter/google" "fmt" "time" @@ -51,7 +51,7 @@ func (_c *GoogleAuthCreate) SetNillableUpdatedAt(v *time.Time) *GoogleAuthCreate } // SetCredentials sets the "credentials" field. -func (_c *GoogleAuthCreate) SetCredentials(v google.Credentials) *GoogleAuthCreate { +func (_c *GoogleAuthCreate) SetCredentials(v schema.Credentials) *GoogleAuthCreate { _c.mutation.SetCredentials(v) return _c } diff --git a/starter/ent/googleauth_update.go b/starter/ent/googleauth_update.go index 5bfd814..0d58f7d 100644 --- a/starter/ent/googleauth_update.go +++ b/starter/ent/googleauth_update.go @@ -7,8 +7,8 @@ import ( "errors" "ersteller-lib/starter/ent/googleauth" "ersteller-lib/starter/ent/predicate" + "ersteller-lib/starter/ent/schema" "ersteller-lib/starter/ent/user" - "ersteller-lib/starter/google" "fmt" "time" @@ -37,13 +37,13 @@ func (_u *GoogleAuthUpdate) SetUpdatedAt(v time.Time) *GoogleAuthUpdate { } // SetCredentials sets the "credentials" field. -func (_u *GoogleAuthUpdate) SetCredentials(v google.Credentials) *GoogleAuthUpdate { +func (_u *GoogleAuthUpdate) SetCredentials(v schema.Credentials) *GoogleAuthUpdate { _u.mutation.SetCredentials(v) return _u } // SetNillableCredentials sets the "credentials" field if the given value is not nil. -func (_u *GoogleAuthUpdate) SetNillableCredentials(v *google.Credentials) *GoogleAuthUpdate { +func (_u *GoogleAuthUpdate) SetNillableCredentials(v *schema.Credentials) *GoogleAuthUpdate { if v != nil { _u.SetCredentials(*v) } @@ -190,13 +190,13 @@ func (_u *GoogleAuthUpdateOne) SetUpdatedAt(v time.Time) *GoogleAuthUpdateOne { } // SetCredentials sets the "credentials" field. -func (_u *GoogleAuthUpdateOne) SetCredentials(v google.Credentials) *GoogleAuthUpdateOne { +func (_u *GoogleAuthUpdateOne) SetCredentials(v schema.Credentials) *GoogleAuthUpdateOne { _u.mutation.SetCredentials(v) return _u } // SetNillableCredentials sets the "credentials" field if the given value is not nil. -func (_u *GoogleAuthUpdateOne) SetNillableCredentials(v *google.Credentials) *GoogleAuthUpdateOne { +func (_u *GoogleAuthUpdateOne) SetNillableCredentials(v *schema.Credentials) *GoogleAuthUpdateOne { if v != nil { _u.SetCredentials(*v) } diff --git a/starter/ent/mutation.go b/starter/ent/mutation.go index 4a9adbe..5a7e4bd 100644 --- a/starter/ent/mutation.go +++ b/starter/ent/mutation.go @@ -7,8 +7,8 @@ import ( "errors" "ersteller-lib/starter/ent/googleauth" "ersteller-lib/starter/ent/predicate" + "ersteller-lib/starter/ent/schema" "ersteller-lib/starter/ent/user" - "ersteller-lib/starter/google" "fmt" "sync" "time" @@ -38,7 +38,7 @@ type GoogleAuthMutation struct { id *int created_at *time.Time updated_at *time.Time - credentials *google.Credentials + credentials *schema.Credentials clearedFields map[string]struct{} user *int cleareduser bool @@ -218,12 +218,12 @@ func (m *GoogleAuthMutation) ResetUpdatedAt() { } // SetCredentials sets the "credentials" field. -func (m *GoogleAuthMutation) SetCredentials(_go google.Credentials) { - m.credentials = &_go +func (m *GoogleAuthMutation) SetCredentials(s schema.Credentials) { + m.credentials = &s } // Credentials returns the value of the "credentials" field in the mutation. -func (m *GoogleAuthMutation) Credentials() (r google.Credentials, exists bool) { +func (m *GoogleAuthMutation) Credentials() (r schema.Credentials, exists bool) { v := m.credentials if v == nil { return @@ -234,7 +234,7 @@ func (m *GoogleAuthMutation) Credentials() (r google.Credentials, exists bool) { // OldCredentials returns the old "credentials" field's value of the GoogleAuth entity. // If the GoogleAuth object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GoogleAuthMutation) OldCredentials(ctx context.Context) (v google.Credentials, err error) { +func (m *GoogleAuthMutation) OldCredentials(ctx context.Context) (v schema.Credentials, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCredentials is only allowed on UpdateOne operations") } @@ -389,7 +389,7 @@ func (m *GoogleAuthMutation) SetField(name string, value ent.Value) error { m.SetUpdatedAt(v) return nil case googleauth.FieldCredentials: - v, ok := value.(google.Credentials) + v, ok := value.(schema.Credentials) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } diff --git a/starter/google/credentials.go b/starter/ent/schema/credentials.go similarity index 86% rename from starter/google/credentials.go rename to starter/ent/schema/credentials.go index 5147f22..4871e58 100644 --- a/starter/google/credentials.go +++ b/starter/ent/schema/credentials.go @@ -1,4 +1,4 @@ -package google +package schema import "golang.org/x/oauth2" diff --git a/starter/ent/schema/google_auth.go b/starter/ent/schema/google_auth.go index 47a5a7f..41221fd 100644 --- a/starter/ent/schema/google_auth.go +++ b/starter/ent/schema/google_auth.go @@ -2,7 +2,6 @@ package schema import ( ersteller_ent "ersteller-lib/schema/ent" - google "ersteller-lib/starter/google" "entgo.io/ent" "entgo.io/ent/schema/edge" @@ -21,7 +20,7 @@ func (GoogleAuth) Mixin() []ent.Mixin { func (GoogleAuth) Fields() []ent.Field { return []ent.Field{ - field.JSON("credentials", google.Credentials{}), + field.JSON("credentials", Credentials{}), } } diff --git a/starter/env/environment.go b/starter/env/environment.go index e5170a3..3d4d1d9 100644 --- a/starter/env/environment.go +++ b/starter/env/environment.go @@ -133,8 +133,10 @@ func GenerateEnvFile(rootPath string, overwrite bool) error { // Define default values and comments for specific keys defaults := map[string]string{ - EnvKeyIsLocal: "true", - EnvKeyIsDev: "true", + EnvKeyDatabaseURL: "\"db/starter.db?_fk=1\"", + EnvKeyBaseURL: "\"http://localhost:8090\"", + EnvKeyIsLocal: "true", + EnvKeyIsDev: "true", } comments := map[string]string{ diff --git a/starter/google/google_auth.go b/starter/google/google_auth.go new file mode 100644 index 0000000..db6473d --- /dev/null +++ b/starter/google/google_auth.go @@ -0,0 +1,63 @@ +package google + +import ( + "crypto/rand" + "encoding/base64" + . "ersteller-lib" + "ersteller-lib/starter/ent" + "ersteller-lib/starter/env" + "net/http" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" +) + +const oAuthStateCookieName = "oauthstate" +const GoogleLogin = "/login/google" +const GoogleLoginCallback = "/google/authenticated" + +type GoogleAuth struct { + db *ent.Client + server *http.ServeMux + environment env.Environment + config oauth2.Config +} + +func NewGoogleAuth(db *ent.Client, server *http.ServeMux, environment env.Environment) *GoogleAuth { + config := oauth2.Config{ + ClientID: environment.GoogleClientId, + ClientSecret: environment.GoogleClientSecret, + Endpoint: google.Endpoint, + RedirectURL: environment.BaseUrl + GoogleLoginCallback, + Scopes: []string{}, + } + return &GoogleAuth{db: db, server: server, environment: environment, config: config} +} + +func (g *GoogleAuth) AddRoutes() { + g.server.HandleFunc("GET "+GoogleLogin, func(writer http.ResponseWriter, request *http.Request) { + state := g.generateStateOauthCookie(writer) + url := g.config.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.ApprovalForce) + http.Redirect(writer, request, url, http.StatusTemporaryRedirect) + }) +} + +func (g *GoogleAuth) generateStateOauthCookie(w http.ResponseWriter) string { + b := make([]byte, 16) + _, err := rand.Read(b) + if err != nil { + LogError("Failed to read random state: %v", err) + } + state := base64.URLEncoding.EncodeToString(b) + + var expiration = time.Now().Add(time.Hour) + cookie := http.Cookie{Name: oAuthStateCookieName, Value: state, Expires: expiration, HttpOnly: true, Path: "/", Secure: false} + if g.environment.IsLocal { + cookie.SameSite = http.SameSiteLaxMode + cookie.Secure = false + } + http.SetCookie(w, &cookie) + + return state +} diff --git a/starter/main.go b/starter/main.go index 12e9166..d83d690 100644 --- a/starter/main.go +++ b/starter/main.go @@ -1,11 +1,14 @@ package main import ( + "context" . "ersteller-lib" + "ersteller-lib/starter/ent" "ersteller-lib/starter/env" "ersteller-lib/starter/routes" "log" "net/http" + "time" ) func main() { @@ -13,6 +16,22 @@ func main() { environment := env.LoadEnvironment() Debug(environment) + + client, err := ent.Open("sqlite3", environment.DatabaseUrl, + ent.Log(log.Println), ent.Debug()) + if err != nil { + log.Fatalf("failed opening connection to sqlite: %v", err) + } + log.Println("client", client) + defer client.Close() + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) + defer cancel() + + if err := client.Schema.Create(ctx); err != nil { + log.Fatalf("failed creating schema resources: %v", err) + } + Debug("starting white label app on port 8090") handler := routes.CreateApi() log.Fatal(http.ListenAndServe(":8090", handler))