Login fully working (via HTMX and Cookies)

Moved code bits around for more Exported functions
Fixed JWT validation
This commit is contained in:
Caleb Gardner
2024-11-11 13:25:38 -06:00
parent e183eefee8
commit 8eb031c64f
4 changed files with 21 additions and 18 deletions
+13 -9
View File
@@ -15,7 +15,7 @@ import (
var editorFS embed.FS var editorFS embed.FS
const loginPage = ` const loginPage = `
<form id="loginForm" hx-post="/login"> <form id="loginForm" hx-post="/login" hx-target="#formResult">
<label for="username">Username:</label> <label for="username">Username:</label>
<input name="username" id="usernameInput" onkeydown="return event.key != 'Enter';"></input> <input name="username" id="usernameInput" onkeydown="return event.key != 'Enter';"></input>
<label for="password">Password:</label> <label for="password">Password:</label>
@@ -25,19 +25,25 @@ const loginPage = `
</form> </form>
` `
func LoginPage(w http.ResponseWriter, r *http.Request) { func loginPageRequest(w http.ResponseWriter, r *http.Request) {
sendContent(w, r, loginPage, "", "") sendContent(w, r, loginPage, "", "")
} }
func TrueLogin(w http.ResponseWriter, r *http.Request) { func trueLoginRequest(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("HX-Request") != "true" { if r.Header.Get("HX-Request") != "true" {
sendContent(w, r, "<p>Bad request</p>", "", "") sendContent(w, r, "<p>Bad request</p>", "", "")
return
} }
u, err := back.TryLogin(r.Context(), r.URL.Query().Get("username"), r.URL.Query().Get("password")) err := r.ParseForm()
if err != nil {
sendContent(w, r, "<p>Bad request</p>", "", "")
return
}
u, err := back.TryLogin(r.Context(), r.FormValue("username"), r.FormValue("password"))
if err != nil { if err != nil {
if err == backend.ErrLoginTimeout { if err == backend.ErrLoginTimeout {
sendContent(w, r, fmt.Sprint("<p>Timed out for", time.Unix(u.Timeout, 0).Sub(time.Now()), "</p>"), "", "") sendContent(w, r, fmt.Sprint("<p>Timed out for", time.Unix(u.Timeout, 0).Sub(time.Now()), "</p>"), "", "")
} else if err == backend.ErrLoginTimeout { } else if err == backend.ErrLoginIncorrect {
sendContent(w, r, "<p>Username or password invalid</p>", "", "") sendContent(w, r, "<p>Username or password invalid</p>", "", "")
} else { } else {
log.Println("error trying to login:", err) log.Println("error trying to login:", err)
@@ -55,10 +61,9 @@ func TrueLogin(w http.ResponseWriter, r *http.Request) {
sendContent(w, r, "<p hx-get='/editor' hx-push-url='true' hx-trigger='load' hx-target='#content'>Successful Login</p>", "", "") sendContent(w, r, "<p hx-get='/editor' hx-push-url='true' hx-trigger='load' hx-target='#content'>Successful Login</p>", "", "")
} }
func Editor(w http.ResponseWriter, r *http.Request) { func editorRequest(w http.ResponseWriter, r *http.Request) {
authCookie, err := r.Cookie("blogAuthToken") authCookie, err := r.Cookie("blogAuthToken")
if err != nil { if err != nil {
w.WriteHeader(http.StatusUnauthorized)
if err != http.ErrNoCookie { if err != http.ErrNoCookie {
log.Println("error getting auth cookie:", err) log.Println("error getting auth cookie:", err)
} }
@@ -67,7 +72,6 @@ func Editor(w http.ResponseWriter, r *http.Request) {
} }
usr, err := back.VerifyUser(r.Context(), authCookie.Value) usr, err := back.VerifyUser(r.Context(), authCookie.Value)
if err != nil { if err != nil {
w.WriteHeader(http.StatusUnauthorized)
if err != backend.ErrTokenUnauthorized { if err != backend.ErrTokenUnauthorized {
log.Println("error authorizing JWT token:", err) log.Println("error authorizing JWT token:", err)
} }
@@ -95,5 +99,5 @@ func editorRedirect(w http.ResponseWriter, r *http.Request, path string) {
w.Header().Set("HX-Location", `{"path": "`+path+`", "target":"#content"}`) w.Header().Set("HX-Location", `{"path": "`+path+`", "target":"#content"}`)
return return
} }
http.Redirect(w, r, "https://darkstorm.tech"+path, http.StatusSeeOther) http.Redirect(w, r, "https://darkstorm.tech"+path, http.StatusFound)
} }
+1 -1
View File
@@ -48,7 +48,7 @@ func (b *Backend) ParseHeader(r *http.Request) (*ParsedHeader, error) {
if apiKey.Death > 0 && time.Unix(apiKey.Death, 0).Before(time.Now()) { if apiKey.Death > 0 && time.Unix(apiKey.Death, 0).Before(time.Now()) {
return nil, ErrApiKeyUnauthorized return nil, ErrApiKeyUnauthorized
} }
out.Key = apiKey out.Key = &apiKey
} else { } else {
fmt.Println("origin:", r.Header.Get("origin")) fmt.Println("origin:", r.Header.Get("origin"))
keys, err := b.keyTable.Find(r.Context(), map[string]any{"allowedOrigins": r.Header.Get("origin")}) keys, err := b.keyTable.Find(r.Context(), map[string]any{"allowedOrigins": r.Header.Get("origin")})
+4 -4
View File
@@ -37,10 +37,10 @@ func (b *Backend) GenerateJWT(r *ReqestUser) (string, error) {
return "", errors.New("user management not enabled") return "", errors.New("user management not enabled")
} }
return jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.RegisteredClaims{ return jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.RegisteredClaims{
ID: r.ID,
Issuer: "darkstorm.tech", Issuer: "darkstorm.tech",
IssuedAt: jwt.NewNumericDate(time.Now()), IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(12 * time.Hour)), ExpiresAt: jwt.NewNumericDate(time.Now().Add(12 * time.Hour)),
Subject: r.ID,
}).SignedString(b.jwtPriv) }).SignedString(b.jwtPriv)
} }
@@ -68,7 +68,7 @@ func (b *Backend) TryLogin(ctx context.Context, username, password string) (User
if err == ErrNotFound { if err == ErrNotFound {
return User{}, ErrLoginIncorrect return User{}, ErrLoginIncorrect
} }
if len(users) > 0 { if len(users) > 1 {
log.Println("duplicate username detected, fix immediately:", username) log.Println("duplicate username detected, fix immediately:", username)
} }
user := users[0] user := users[0]
@@ -106,7 +106,7 @@ func (b *Backend) VerifyUser(ctx context.Context, token string) (*User, error) {
return nil, err return nil, err
} }
usr, err := b.userTable.Get(ctx, sub) usr, err := b.userTable.Get(ctx, sub)
if err == jwt.ErrInvalidKey { if err == ErrNotFound {
return nil, ErrTokenUnauthorized return nil, ErrTokenUnauthorized
} else if err != nil { } else if err != nil {
return nil, err return nil, err
@@ -120,7 +120,7 @@ func (b *Backend) VerifyUser(ctx context.Context, token string) (*User, error) {
if usr.PasswordChange > 0 && iss.Time.Before(time.Unix(usr.PasswordChange, 0)) { if usr.PasswordChange > 0 && iss.Time.Before(time.Unix(usr.PasswordChange, 0)) {
return nil, ErrTokenUnauthorized return nil, ErrTokenUnauthorized
} }
return usr, nil return &usr, nil
} }
func NewUser(username, password, email string) (User, error) { func NewUser(username, password, email string) (User, error) {
+3 -4
View File
@@ -147,13 +147,12 @@ func setupWebsite(mux *http.ServeMux) {
url, _ := url.Parse("https://localhost:30000") url, _ := url.Parse("https://localhost:30000")
mux.Handle("rpg.darkstorm.tech/", httputil.NewSingleHostReverseProxy(url)) mux.Handle("rpg.darkstorm.tech/", httputil.NewSingleHostReverseProxy(url))
} }
edit := NewBlogEditor(blogApp, back)
mux.HandleFunc("GET /files/{w...}", filesRequest) mux.HandleFunc("GET /files/{w...}", filesRequest)
mux.HandleFunc("GET /portfolio", portfolioRequest) mux.HandleFunc("GET /portfolio", portfolioRequest)
mux.HandleFunc("GET /list", blogListHandle) mux.HandleFunc("GET /list", blogListHandle)
mux.HandleFunc("GET /login", edit.LoginPage) mux.HandleFunc("GET /login", loginPageRequest)
mux.HandleFunc("GET /editor", edit.Editor) mux.HandleFunc("GET /editor/", editorRequest)
mux.HandleFunc("POST /login", edit.TrueLogin) mux.HandleFunc("POST /login", trueLoginRequest)
mux.HandleFunc("/", mainHandle) mux.HandleFunc("/", mainHandle)
} }