1

I am using Justinas NoSurf for CSRF http package in my Go code. When I try to sign up and submit the post on my form, I get a "bad request" error. The problem occurs every time I'm inside a form and I try the Post method. When I press submit to signup then I get the "bad request" error. I know it has to do with the nosurf csrf package because that's when I started seeing the error.

This is my routes:

func (app *application) routes() http.Handler {
    router := httprouter.New()
    fileServer := http.FileServer(http.Dir("./ui/static/"))
    router.Handler(http.MethodGet, "/static/*filepath", http.StripPrefix("/static", fileServer))

    dynamicMiddleware := alice.New(app.sessionManager.LoadAndSave, noSurf)

        router.Handler(http.MethodGet, "/signup", dynamicMiddleware.ThenFunc(app.signup))
    router.Handler(http.MethodPost, "/signup", dynamicMiddleware.ThenFunc(app.signupSubmit))

        //tidy up the middle wear
    standardMiddleware := alice.New(app.RecoverPanicMiddleware, app.logRequestMiddleware,                  
        securityHeadersMiddleware)

    return standardMiddleware.Then(router)
}

Inside my data.go, I have this:

type templateData struct {
    User            []*models.User
    ErrorsFromForm  map[string]string
    FormData        url.Values
    Flash           string //flash is the key
    CSRFToken       string
    IsAuthenticated bool
}

My handlers function look like this:

func (app *application) signup(w http.ResponseWriter, r *http.Request) {
    // remove the entry from the session manager
    flash := app.sessionManager.PopString(r.Context(), "flash")
    data := &templateData{ //putting flash into template data
        Flash:     flash,
        CSRFToken: nosurf.Token(r),
    }
    RenderTemplate(w, "signup.page.tmpl", data)
    csrfToken := nosurf.Token(r)
    fmt.Println("CSRF Token (GET):", csrfToken)

    }

    func (app *application) signupSubmit(w http.ResponseWriter, r *http.Request) {

    r.ParseForm()

    name := r.PostForm.Get("name")
    email := r.PostForm.Get("email")
    password := r.PostForm.Get("password")
    confirmpassword := r.PostForm.Get("confirmpassword")

    fmt.Println("Form Data:", r.PostForm) // Print the entire form data
    csrfToken := r.PostForm.Get("csrf_token")
    fmt.Println("CSRF Token (POST):", csrfToken)

    errors_user := make(map[string]string)

    if strings.TrimSpace(name) == "" {
        errors_user["name"] = "Name is required"
    }

    if strings.TrimSpace(email) == "" {
        errors_user["email"] = "Email is required"
    }

    emailRegex := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
    if !emailRegex.MatchString(email) {
        errors_user["email"] = "Invalid email"
    }

    if strings.TrimSpace(password) == "" {
        errors_user["password"] = "Password is required"
    } else if utf8.RuneCountInString(password) < 5 {
        errors_user["password"] = "This field is too short (minimum is 5 characters)"
    }

    if password != confirmpassword {
        errors_user["confirmpassword"] = "Password does not match"
    }

    if len(errors_user) > 0 {
        data := &templateData{
            ErrorsFromForm: errors_user,
            CSRFToken:      nosurf.Token(r),
        }
        RenderTemplate(w, "signup.page.tmpl", data)
        return
    }
    err := app.user.Insert(name, email, password, confirmpassword)
    if err != nil {
        if errors.Is(err, models.ErrDuplicateEmail) {
            app.sessionManager.Put(r.Context(), "flash", "Email already registered")
            http.Redirect(w, r, "/signup", http.StatusSeeOther)
            return
        }

        app.sessionManager.Put(r.Context(), "flash", "Email already registered")
        http.Redirect(w, r, "/signup", http.StatusSeeOther)
        return
    }

    app.sessionManager.Put(r.Context(), "flash", "Signup was successful")
    http.Redirect(w, r, "/login", http.StatusSeeOther)
}

My signup.tmpl looks like this:

                        <form action="/signup" method="POST" class="signup-form">
                        <input type="hidden" name="csrf_token" value="{{.CSRFToken}}"/> 

                        {{with .Flash}}
                        <div class = "flash"> {{.}}</div>
                        {{end}}

                        {{ with .ErrorsFromForm.name }}
                        <label class="error">{{ . }}</label>
                        {{end}}
                        <div class="input-field">
                            <input type="text" name="name" class="input" placeholder="Username" value="{{ .FormData.Get "name" }}">
                        </div>

                        {{ with .ErrorsFromForm.email }}
                        <label class="error">{{ . }}</label>
                        {{end}}
                        <div class="input-field">
                            <input type="text" name="email" class="input" placeholder="Email" value="{{ .FormData.Get "email" }}"> 
                        </div>

                        {{ with .ErrorsFromForm.password }}
                        <label class="error">{{ . }}</label>
                        {{end}}
                        <div class="input-field">
                            <input type="password" name="password" class="input"  placeholder="Password" value="{{ .FormData.Get "password" }}">
                        </div>

                        {{ with .ErrorsFromForm.confirmpassword }}
                        <label class="error">{{ . }}</label>
                        {{end}}
                        <div class="input-field">
                            <input type="password" name="confirmpassword" class="input" placeholder="Confirm Password" value="{{ .FormData.Get "confirmpassword" }}">
                        </div>
                        
                        <input type="submit" class="submit" value="Signup"> </input>                    
                        <label><a href="/login">Already have an account?<span> Login</span></a></label>

                     </form>

I know the error is because of the nosurf csrf. If I try to remove it from all the files then the post would work.

I'm not sure how to check if the CSRFToken correctly passed into the template.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
radgeek
  • 23
  • 4
  • `I'm not sure how to check if the CSRFToken correctly passed into the template`, check the response of `GET /signup` request. You can check it in the DevTools of your browser. – Zeke Lu Jun 26 '23 at 08:16
  • Hope this will help... https://stackoverflow.com/questions/76437217/golang-post-400-bad-request/76437985#76437985 – PRATHEESH PC Jun 26 '23 at 08:38
  • I do check the response of the GET /signup but I never get the POST response. func (app *application) signup(w http.ResponseWriter, r *http.Request) { csrfToken := nosurf.Token(r) fmt.Println("CSRF Token (GET):", csrfToken) // ... } func (app *application) signupSubmit(w http.ResponseWriter, r *http.Request) { csrfToken := r.PostForm.Get("csrf_token") fmt.Println("CSRF Token (POST):", csrfToken) // ... } I tried this but I don't get the POST /. – radgeek Jun 27 '23 at 05:40
  • See [Inspect network activity](https://developer.chrome.com/docs/devtools/network/). – Zeke Lu Jun 29 '23 at 00:36

0 Answers0