Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>
wip auth
auth/middleware.go | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/server.go | 8 ++ go.mod | 1 go.sum | 1 types/user.go | 2
diff --git a/auth/middleware.go b/auth/middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..2fc687ab90e439a3786394bd3f7f728967993bee --- /dev/null +++ b/auth/middleware.go @@ -0,0 +1,129 @@ +package auth + +import ( + "context" + "fmt" + "net/http" + "strings" + "time" + + "golang.org/x/crypto/bcrypt" + + "git.eletrotupi.com/git/dinheiro/db" + "git.eletrotupi.com/git/dinheiro/types" +) + +var authCtxKey = &contextKey{"auth"} + +type contextKey struct { + name string +} + +// This is the representation of our currentUser +type AuthContext struct { + UserID int + Email string + CreatedAt time.Time + UpdatedAt time.Time +} + +type AuthCookie struct { + UserID int `json:"id"` +} + +func authError(writer http.ResponseWriter, reason string, statusCode int) { + writer.WriteHeader(statusCode) + writer.Write([]byte(reason)) +} + +func LookupUserAndGenerateCtx(ctx context.Context, email string) (*AuthContext, error) { + var ( + authCtx AuthContext + err error + ) + + dbConn := db.ForContext(ctx) + row := dbConn.QueryRow(`SELECT id, email, created_at, updated_at FROM users WHERE email = $1`, email) + + if err = row.Scan(&authCtx.UserID, &authCtx.Email, &authCtx.CreatedAt, &authCtx.UpdatedAt); err != nil { + return nil, fmt.Errorf("Something went off when creating the auth context %s", err) + } + + return &authCtx, nil +} + +// This method authenticate the user, populate a AuthContext and set the cookie +// on the request +func authenticate(w http.ResponseWriter, r *http.Request, next http.Handler) { + var ( + //authCookie AuthCookie + user types.User + err error + ) + + err = r.ParseForm() + if err != nil { + authError(w, "Couldn't parse form", http.StatusInternalServerError) + } + + email := r.Form.Get("email") + password := r.Form.Get("password") + + dbConn := db.ForContext(r.Context()) + row := dbConn.QueryRow( + `SELECT id, email, encrypted_password FROM users WHERE email = $1`, + email) + + if err = row.Scan(&user.ID, &user.Email, &user.EncryptedPassword); err != nil { + authError(w, "Email wasn't found", http.StatusUnauthorized) + return + } + + err = bcrypt.CompareHashAndPassword([]byte(user.EncryptedPassword), []byte(password)) + if err != nil { + authError(w, "Incorrect Password", http.StatusUnauthorized) + return + } + + authCtx, err := LookupUserAndGenerateCtx(r.Context(), user.Email) + if err != nil { + authError(w, err.Error(), http.StatusForbidden) + + return + } + + // TODO: Write the cookie part here + //authCookie = ... + + ctx := context.WithValue(r.Context(), authCtxKey, authCtx) + r = r.WithContext(ctx) + next.ServeHTTP(w, r) +} + +// This authenticate from an existing cookie sent by the browser +// It'll decrypt the cookie, unmarshal into a AuthCookie struct +// Then populate the AuthContext (our current user) +func authFromCookie(cookie *http.Cookie, w http.ResponseWriter, r *http.Request, next http.Handler) { +} + +func Middleware() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + cookie, err := r.Cookie("dinheiro.v1") + if err == nil { + authFromCookie(cookie, w, r, next) + + return + } + + if strings.HasPrefix(r.URL.Path, "/auth") { + authenticate(w, r, next) + + return + } + + next.ServeHTTP(w, r) + return + }) + } +} diff --git a/cmd/server.go b/cmd/server.go index 82e95bff497f0a492f0959bcc470a48ab263df5f..297e9b11235a804fdbd81cb5789eef7f05357750 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -7,7 +7,6 @@ "path/filepath" "strings" "net/http" "database/sql" - "encoding/json" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" @@ -18,7 +17,7 @@ "git.eletrotupi.com/git/dinheiro/config" "git.eletrotupi.com/git/dinheiro/db" "git.eletrotupi.com/git/dinheiro/keys" "git.eletrotupi.com/git/dinheiro/redis" - "git.eletrotupi.com/git/dinheiro/types" + "git.eletrotupi.com/git/dinheiro/auth" ) func FileServer(router chi.Router, path string, root http.FileSystem) { @@ -64,6 +63,10 @@ } w.Write(fileData) }) + + router.Post("/auth", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("authenticated!")) + }) } func Server() { @@ -96,6 +99,7 @@ rc := goRedis.NewClient(ropts) router.Use(config.Middleware(appConfig)) router.Use(db.Middleware(dbConn)) + router.Use(auth.Middleware()) router.Use(redis.Middleware(rc)) router.Use(middleware.RealIP) router.Use(middleware.Logger) diff --git a/go.mod b/go.mod index c035ff8fb6192a29add238c08eb04b9cc6030af4..c3d720e4dc20ab7c24acbe89f256f0c48ce6508e 100644 --- a/go.mod +++ b/go.mod @@ -9,4 +9,5 @@ github.com/go-chi/chi/v5 v5.0.3 github.com/go-redis/redis/v8 v8.9.0 // indirect github.com/lib/pq v1.10.2 // indirect github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect ) diff --git a/go.sum b/go.sum index 1845be0f720dd971af642d44c2f261d8fe593839..88929f2f47d685e7f1567bfd7ade279f15af9388 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,7 @@ go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/types/user.go b/types/user.go index d0875c4528ba4e4ca9bbd1d19c529169072971d1..5356baebebdb7e4a0fdd64742d1e156502d14a20 100644 --- a/types/user.go +++ b/types/user.go @@ -9,6 +9,8 @@ ID int FirstName string LastName string Username string + Email string CreatedAt time.Time UpdatedAt time.Time + EncryptedPassword string }