Redirect user appropriately after login
If an unauthenticated user tries to visit GET /account/view
they will be redirected to the login page. Then after logging in successfully, they will be redirected to the GET /snippet/create
form. This is awkward and confusing for the user, as they end up on a different page to where they originally wanted to go.
Your goal in this exercise is to update the application so that users are redirected to the page they were originally trying to visit after logging in.
Step 1
Update the requireAuthentication()
middleware so that, before an unauthenticated user is redirected to the login page, the URL path that they are trying to visit is added to their session data.
Step 2
Update the userLogin
handler to check the user’s session for a URL path after they successfully log in. If one exists, remove it from the session data and redirect the user to that URL path. Otherwise, default to redirecting the user to /snippet/create
.
Suggested code
Suggested code for step 1
package main ... func (app *application) requireAuthentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !app.isAuthenticated(r) { // Add the path that the user is trying to access to their session // data. app.sessionManager.Put(r.Context(), "redirectPathAfterLogin", r.URL.Path) http.Redirect(w, r, "/user/login", http.StatusSeeOther) return } w.Header().Add("Cache-Control", "no-store") next.ServeHTTP(w, r) }) } ...
Suggested code for step 2
package main ... func (app *application) userLoginPost(w http.ResponseWriter, r *http.Request) { var form userLoginForm err := app.decodePostForm(r, &form) if err != nil { app.clientError(w, http.StatusBadRequest) return } form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank") form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address") form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank") if !form.Valid() { data := app.newTemplateData(r) data.Form = form app.render(w, r, http.StatusUnprocessableEntity, "login.tmpl", data) return } id, err := app.users.Authenticate(form.Email, form.Password) if err != nil { if errors.Is(err, models.ErrInvalidCredentials) { form.AddNonFieldError("Email or password is incorrect") data := app.newTemplateData(r) data.Form = form app.render(w, r, http.StatusUnprocessableEntity, "login.tmpl", data) } else { app.serverError(w, r, err) } return } err = app.sessionManager.RenewToken(r.Context()) if err != nil { app.serverError(w, r, err) return } app.sessionManager.Put(r.Context(), "authenticatedUserID", id) // Use the PopString method to retrieve and remove a value from the session // data in one step. If no matching key exists this will return the empty // string. path := app.sessionManager.PopString(r.Context(), "redirectPathAfterLogin") if path != "" { http.Redirect(w, r, path, http.StatusSeeOther) return } http.Redirect(w, r, "/snippet/create", http.StatusSeeOther) } ...