Protecting routes on hte FE and improving authentication
This commit is contained in:
parent
2f7300db0f
commit
a6d54242bb
@ -289,3 +289,32 @@ func (app *application) LoginPage(w http.ResponseWriter, r *http.Request) {
|
|||||||
app.errorLog.Println(err)
|
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)
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/alexedwards/scs/mysqlstore"
|
||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,16 +92,17 @@ func main() {
|
|||||||
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
|
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
|
||||||
errorLog := log.New(os.Stdout, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
|
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)
|
conn, err := driver.OpenDB(cfg.db.dsn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorLog.Fatal(err)
|
errorLog.Fatal(err)
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
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)
|
tc := make(map[string]*template.Template)
|
||||||
|
|
||||||
app := &application{
|
app := &application{
|
||||||
|
@ -7,3 +7,12 @@ import (
|
|||||||
func SessionLoad(next http.Handler) http.Handler {
|
func SessionLoad(next http.Handler) http.Handler {
|
||||||
return session.LoadAndSave(next)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -11,9 +11,14 @@ func (app *application) routes() http.Handler {
|
|||||||
mux.Use(SessionLoad)
|
mux.Use(SessionLoad)
|
||||||
|
|
||||||
mux.Get("/", app.Home)
|
mux.Get("/", app.Home)
|
||||||
|
|
||||||
|
mux.Route("/admin", func(mux chi.Router) {
|
||||||
|
mux.Use(app.Auth)
|
||||||
mux.Get("/virtual-terminal", app.VirtualTerminal)
|
mux.Get("/virtual-terminal", app.VirtualTerminal)
|
||||||
mux.Post("/virtual-terminal-payment-succeeded", app.VirtualTerminalPaymentSucceeded)
|
})
|
||||||
mux.Get("/virtual-terminal-receipt", app.VirtualTerminalReceipt)
|
|
||||||
|
// mux.Post("/virtual-terminal-payment-succeeded", app.VirtualTerminalPaymentSucceeded)
|
||||||
|
// mux.Get("/virtual-terminal-receipt", app.VirtualTerminalReceipt)
|
||||||
|
|
||||||
mux.Get("/widget/{id}", app.ChargeOnce)
|
mux.Get("/widget/{id}", app.ChargeOnce)
|
||||||
mux.Get("/receipt", app.Receipt)
|
mux.Get("/receipt", app.Receipt)
|
||||||
@ -24,6 +29,8 @@ func (app *application) routes() http.Handler {
|
|||||||
|
|
||||||
// auth routes
|
// auth routes
|
||||||
mux.Get("/login", app.LoginPage)
|
mux.Get("/login", app.LoginPage)
|
||||||
|
mux.Post("/login", app.PostLoginPage)
|
||||||
|
mux.Get("/logout", app.Logout)
|
||||||
|
|
||||||
fileServer := http.FileServer(http.Dir("./static"))
|
fileServer := http.FileServer(http.Dir("./static"))
|
||||||
mux.Handle("/static/*", http.StripPrefix("/static", fileServer))
|
mux.Handle("/static/*", http.StripPrefix("/static", fileServer))
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li id="vt-link" class="nav-item d-none">
|
<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>
|
</li>
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<li id="login-link" class="nav-item d-none">
|
<li id="login-link" class="nav-item d-none">
|
||||||
|
@ -6,7 +6,7 @@ Login
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 offset-md-3">
|
<div class="col-md-6 offset-md-3">
|
||||||
<div class="alert alert-danger text-center d-none" id="login-messages"></div>
|
<div class="alert alert-danger text-center d-none" id="login-messages"></div>
|
||||||
<form action=""
|
<form action="/login"
|
||||||
method="post"
|
method="post"
|
||||||
name="login-form"
|
name="login-form"
|
||||||
id="login-form"
|
id="login-form"
|
||||||
|
@ -80,7 +80,7 @@ Virtual Terminal
|
|||||||
<strong>Bank Return Code</strong>: <span id="bank-return-code"></span>
|
<strong>Bank Return Code</strong>: <span id="bank-return-code"></span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="/virtual-terminal" class="btn btn-primary">
|
<a href="/admin/virtual-terminal" class="btn btn-primary">
|
||||||
Charge another card
|
Charge another card
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module myapp
|
|||||||
go 1.22.5
|
go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/alexedwards/scs/mysqlstore v0.0.0-20240316134038-7e11d57e8885
|
||||||
github.com/alexedwards/scs/v2 v2.8.0
|
github.com/alexedwards/scs/v2 v2.8.0
|
||||||
github.com/go-chi/chi/v5 v5.1.0
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
|
3
go.sum
3
go.sum
@ -1,5 +1,7 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
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 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw=
|
||||||
github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
|
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=
|
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/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 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
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 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
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=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
@ -3,8 +3,11 @@ package models
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DBModel is the type for database connection values
|
// DBModel is the type for database connection values
|
||||||
@ -254,3 +257,26 @@ func (m *DBModel) GetUserByEmail(email string) (User, error) {
|
|||||||
|
|
||||||
return u, nil
|
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
|
||||||
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE sessions;
|
@ -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);
|
@ -14,6 +14,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
function logout() {
|
function logout() {
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
localStorage.removeItem("token_expiry");
|
localStorage.removeItem("token_expiry");
|
||||||
location.href = "/login";
|
location.href = "/logout";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,8 @@ export function val(api) {
|
|||||||
localStorage.setItem("token", response.authentication_token.token);
|
localStorage.setItem("token", response.authentication_token.token);
|
||||||
localStorage.setItem("token_expiry", response.authentication_token.expiry);
|
localStorage.setItem("token_expiry", response.authentication_token.expiry);
|
||||||
showSuccess("login-messages", "Login successful.")
|
showSuccess("login-messages", "Login successful.")
|
||||||
location.href = "/";
|
// location.href = "/";
|
||||||
|
document.getElementById("login-form").submit()
|
||||||
} else {
|
} else {
|
||||||
showError("login-messages", response.message)
|
showError("login-messages", response.message)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user