Protecting routes on hte FE and improving authentication

This commit is contained in:
vinchent 2024-08-20 21:45:13 +02:00
parent 2f7300db0f
commit a6d54242bb
14 changed files with 98 additions and 12 deletions

View File

@ -289,3 +289,32 @@ func (app *application) LoginPage(w http.ResponseWriter, r *http.Request) {
app.errorLog.Println(err)
}
}
func (app *application) PostLoginPage(w http.ResponseWriter, r *http.Request) {
app.Session.RenewToken(r.Context())
err := r.ParseForm()
if err != nil {
app.errorLog.Println(err)
return
}
email := r.Form.Get("email")
password := r.Form.Get("password")
id, err := app.DB.Authenticate(email, password)
if err != nil {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
app.Session.Put(r.Context(), "userID", id)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func (app *application) Logout(w http.ResponseWriter, r *http.Request) {
app.Session.Destroy(r.Context())
app.Session.RenewToken(r.Context())
http.Redirect(w, r, "/login", http.StatusSeeOther)
}

View File

@ -12,6 +12,7 @@ import (
"os"
"time"
"github.com/alexedwards/scs/mysqlstore"
"github.com/alexedwards/scs/v2"
)
@ -91,16 +92,17 @@ func main() {
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
errorLog := log.New(os.Stdout, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
// set up session
session = scs.New()
session.Lifetime = 24 * time.Hour
conn, err := driver.OpenDB(cfg.db.dsn)
if err != nil {
errorLog.Fatal(err)
}
defer conn.Close()
// set up session
session = scs.New()
session.Lifetime = 24 * time.Hour
session.Store = mysqlstore.New(conn)
tc := make(map[string]*template.Template)
app := &application{

View File

@ -7,3 +7,12 @@ import (
func SessionLoad(next http.Handler) http.Handler {
return session.LoadAndSave(next)
}
func (app *application) Auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !app.Session.Exists(r.Context(), "userID") {
http.Redirect(w, r, "/login", http.StatusSeeOther)
}
next.ServeHTTP(w, r)
})
}

View File

@ -11,9 +11,14 @@ func (app *application) routes() http.Handler {
mux.Use(SessionLoad)
mux.Get("/", app.Home)
mux.Get("/virtual-terminal", app.VirtualTerminal)
mux.Post("/virtual-terminal-payment-succeeded", app.VirtualTerminalPaymentSucceeded)
mux.Get("/virtual-terminal-receipt", app.VirtualTerminalReceipt)
mux.Route("/admin", func(mux chi.Router) {
mux.Use(app.Auth)
mux.Get("/virtual-terminal", app.VirtualTerminal)
})
// mux.Post("/virtual-terminal-payment-succeeded", app.VirtualTerminalPaymentSucceeded)
// mux.Get("/virtual-terminal-receipt", app.VirtualTerminalReceipt)
mux.Get("/widget/{id}", app.ChargeOnce)
mux.Get("/receipt", app.Receipt)
@ -24,6 +29,8 @@ func (app *application) routes() http.Handler {
// auth routes
mux.Get("/login", app.LoginPage)
mux.Post("/login", app.PostLoginPage)
mux.Get("/logout", app.Logout)
fileServer := http.FileServer(http.Dir("./static"))
mux.Handle("/static/*", http.StripPrefix("/static", fileServer))

View File

@ -48,7 +48,7 @@
</ul>
</li>
<li id="vt-link" class="nav-item d-none">
<a class="nav-link" href="/virtual-terminal">Virtual Terminal</a>
<a class="nav-link" href="/admin/virtual-terminal">Virtual Terminal</a>
</li>
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li id="login-link" class="nav-item d-none">

View File

@ -6,7 +6,7 @@ Login
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="alert alert-danger text-center d-none" id="login-messages"></div>
<form action=""
<form action="/login"
method="post"
name="login-form"
id="login-form"

View File

@ -80,7 +80,7 @@ Virtual Terminal
<strong>Bank Return Code</strong>: <span id="bank-return-code"></span>
</p>
<p>
<a href="/virtual-terminal" class="btn btn-primary">
<a href="/admin/virtual-terminal" class="btn btn-primary">
Charge another card
</a>
</p>

1
go.mod
View File

@ -3,6 +3,7 @@ module myapp
go 1.22.5
require (
github.com/alexedwards/scs/mysqlstore v0.0.0-20240316134038-7e11d57e8885
github.com/alexedwards/scs/v2 v2.8.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1

3
go.sum
View File

@ -1,5 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/alexedwards/scs/mysqlstore v0.0.0-20240316134038-7e11d57e8885 h1:C7QAamNjR5yz6di4KJWAKcnxueKBgq4L/JGXhlnu35w=
github.com/alexedwards/scs/mysqlstore v0.0.0-20240316134038-7e11d57e8885/go.mod h1:p8jK3D80sw1PFrCSdlcJF1O75bp55HqbgDyyCLM0FrE=
github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw=
github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
@ -8,6 +10,7 @@ github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@ -3,8 +3,11 @@ package models
import (
"context"
"database/sql"
"errors"
"strings"
"time"
"golang.org/x/crypto/bcrypt"
)
// DBModel is the type for database connection values
@ -254,3 +257,26 @@ func (m *DBModel) GetUserByEmail(email string) (User, error) {
return u, nil
}
func (m *DBModel) Authenticate(email, password string) (int, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var id int
var hashedPassword string
row := m.DB.QueryRowContext(ctx, "SELECT id, password from users WHERE email = ?", email)
err := row.Scan(&id, &hashedPassword)
if err != nil {
return 0, err
}
err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
if err == bcrypt.ErrMismatchedHashAndPassword {
return 0, errors.New("incorrect password")
} else if err != nil {
return 0, err
}
return id, nil
}

View File

@ -0,0 +1 @@
DROP TABLE sessions;

View File

@ -0,0 +1,7 @@
CREATE TABLE sessions (
token CHAR(43) PRIMARY KEY,
data BLOB NOT NULL,
expiry TIMESTAMP(6) NOT NULL
);
CREATE INDEX sessions_expiry_idx ON sessions (expiry);

View File

@ -14,6 +14,6 @@ document.addEventListener("DOMContentLoaded", function () {
function logout() {
localStorage.removeItem("token");
localStorage.removeItem("token_expiry");
location.href = "/login";
location.href = "/logout";
}

View File

@ -36,7 +36,8 @@ export function val(api) {
localStorage.setItem("token", response.authentication_token.token);
localStorage.setItem("token_expiry", response.authentication_token.expiry);
showSuccess("login-messages", "Login successful.")
location.href = "/";
// location.href = "/";
document.getElementById("login-form").submit()
} else {
showError("login-messages", response.message)
}