I am trying to do a simple login. I followed a tutorial on Udemy. It went through rendering templates with a render function. I decided I wanted to handle templates differently and not have a render function and changed everything around. But I did not touch the PostLoginHandler. Since I made this change my post for "/login" has stopped working. I get "400 bad request" and I can't figure out why. I'll include the code that I think is important. I will say that I think it has something to do with the request, and/or CRSF token the tutorial added that I have not used since the changes. I am not using template.Must(template.ParseGlob("./templates/*.tmpl")) for handling templates.
Also, I'm sorry about the long post, I wasn't sure what info would be needed. Thank you in advance for any responses.
Old render function.
func RenderTemplate(w http.ResponseWriter, r *http.Request, t string, pd *models.PageData) {
var tmpl *template.Template
var err error
_, inMap := tmplCache[t]
if !inMap {
err = makeTemplateCache(t)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Template in cache")
}
}
tmpl = tmplCache[t]
pd = AddCSRFData(pd, r)
err = tmpl.Execute(w, pd)
if err != nil {
fmt.Println(err)
}
}
routes
mux := chi.NewRouter()
mux.Use(middleware.Recoverer)
mux.Use(NoSurf)
mux.Use(SetupSession)
mux.Post("/login", handlers.Repo.PostLoginHandler)
It's not even getting to the PostLoginHandler, but the code is
func (m *Repository) PostLoginHandler(w http.ResponseWriter, r *http.Request) {
log.Println("here")
//strMap := make(map[string]string)
_ = m.App.Session.RenewToken(r.Context())
err := r.ParseForm()
if err != nil {
log.Fatal(err)
}
email := r.Form.Get("email")
password := r.Form.Get("password")
form := forms.New(r.PostForm)
form.HasRequired("email", "password")
form.IsEmail("email")
if !form.Valid() {
err := m.App.UITemplates.ExecuteTemplate(w, "login.page.tmpl", &models.PageData{Form: form})
if err != nil {
return
}
//render.RenderTemplate(w, r, "login.page.tmpl", &models.PageData{Form: form})
return
}
id, _, err := m.DB.AuthenticateUser(email, password)
if err != nil {
m.App.Session.Put(r.Context(), "error", "Invalid Email OR Password")
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
m.App.Session.Put(r.Context(), "user_id", id)
m.App.Session.Put(r.Context(), "flash", "Valid Login")
http.Redirect(w, r, "/", http.StatusSeeOther)
//render.RenderTemplate(w, r, "page.page.tmpl", &models.PageData{StrMap: strMap})
}
and lastly, the HTML is a simple form
<form method="post" action="/login">
{{/*<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">*/}}
<h1 class="h3 mb-3 fw-normal">Please sign in</h1>
<div class="form-floating">
<input type="email" class="form-control" id="email" name="email" placeholder="name@example.com">
<label for="email">Email address</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="password" name="password" placeholder="Password">
<label for="password">Password</label>
</div>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>
400 bad request
is getting with Nosurf
middleware.
Firstly, you should un-comment this in your template. As this is required for crsf validation.
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
Secondly, you should ensure that the CSRFToken
correctly passed into the template. I hope this is updating within AddCSRFData
function.
I get "400 bad request" and I can't figure out why.
By default the package is not logging anything when an error happened. But it is possible to override the failure handler
to see what exact reason caused the 400 bad request
.
See the sample code
package main
import (
"fmt"
"html/template"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/justinas/nosurf"
)
var formTemplate = `
<html>
<body>
<form method="post" action="/submit">
<!-- comment this and see error -->
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"/>
<h1 class="h3 mb-3 fw-normal">Please sign in</h1>
<div class="form-floating">
<input type="email" class="form-control" id="email" name="email" placeholder="name@example.com">
<label for="email">Email address</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="password" name="password" placeholder="Password">
<label for="password">Password</label>
</div>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>
</body>
</html>
`
var tmpl = template.Must(template.New("t1").Parse(formTemplate))
// FailureFunction
// Overriding the default nosurf failure Handler
func FailureFunction() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Request Failed. Reason: %v", nosurf.Reason(r))
http.Error(w, http.StatusText(nosurf.FailureCode), nosurf.FailureCode)
})
}
// NoSurf
// Setting up the handler with overrriding default nosurf failure Handler
func NoSurf(handler http.Handler) http.Handler {
obj := nosurf.New(handler)
obj.SetFailureHandler(FailureFunction()) // Override default failure Handler
return obj
}
func main() {
r := chi.NewRouter()
// Add middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(NoSurf)
r.Get("/", HomeHandler)
r.Post("/submit", SubmitHandler)
http.ListenAndServe(":8080", r)
}
func HomeHandler(w http.ResponseWriter, r *http.Request) {
token := nosurf.Token(r) // generating the token
data := map[string]interface{}{
"CSRFToken": token, // comment this and see the error
}
err := tmpl.Execute(w, data)
if err != nil {
http.Error(w, "unable to execute the template", http.StatusInternalServerError)
return
}
}
func SubmitHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
if !nosurf.VerifyToken(nosurf.Token(r), r.PostForm.Get("csrf_token")) {
http.Error(w, "Invalid CSRF Token", http.StatusForbidden)
return
}
w.Write([]byte("success"))
}
Hoping this will help you to resolve your problem.