How to write user login error in login page - go

I'm new one for Golang programming. In Golang how to display the username error.
This is my code:
func LoginHandler(w http.ResponseWriter, req *http.Request){
if req.Method == http.MethodPost{
un := req.FormValue("username")
p := req.FormValue("password")
u, ok := dbUsers[un]
if !ok{
var body, _ = helpers.LoadFile("sss/login.html")
fmt.Fprintf(response, body)
//fmt.Fprintln(w, "incorrect user name")
//return
}
if u.Password != p{
fmt.Fprintln(w, "incorrect password")
return
}
http.Redirect(w, req, "/index.html", http.StatusSeeOther)
return
}
}

In go You have different ways for error handling
func SomeHandler(w http.ResponseWriter, r *http.Request) (int, error) {
if req.Method == http.MethodPost{
un := req.FormValue("username")
p := req.FormValue("password")
u, ok := dbUsers[un]
if !ok{
var body, _ = helpers.LoadFile("sss/login.html")
return 400, errors.New("can't load sss/login")
}
if u.Password != p{
return 400, errors.New("wrong password")
}
http.Redirect(w, req, "/index.html", http.StatusSeeOther)
return 302, nil
}
or
func SomeHandler(w http.ResponseWriter, r *http.Request) {
un := req.FormValue("username")
p := req.FormValue("password")
u, ok := dbUsers[un]
if !ok{
var body, _ = helpers.LoadFile("sss/login.html")
http.Error(w, "can't load sss/login", 400)
return
}
if u.Password != p{
http.Error(w, "wrong password", 400)
return
}
http.Redirect(w, req, "/index.html", http.StatusSeeOther)
return
}
For more information You can read here very helpful blogpost about middlewares:
https://www.nicolasmerouze.com/middlewares-golang-best-practices-examples/
Post with simple example:
https://blog.questionable.services/article/http-handler-error-handling-revisited/
Http package docs: https://golang.org/pkg/net/http/

You could do something a little bit more elegant.
Pass a context object to the template and render the error there.
func index(w http.ResponseWriter, r *http.Request) {
context := make(map[string]string]
context["Error"] = "Username error"
t := template.Must(template.ParseFiles("./templates/index.html"))
t.Execute(w, context)
}
Then, on the template render it like this
{{.Error}}}
If no error occurred, the field will be empty.
Cheers.

Related

Rewriting http HandlerFunc to gin HandlerFunc

I have a middleware written with httpHandler but now I am trying to use gin-gornic and needed to provide an alternative. I have tried rewriting but still not working as I want it to work.
func AuthMiddlewarex(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
if auth == "" {
h.ServeHTTP(w, r)
return
}
bearer := "Bearer "
auth = auth[len(bearer):]
validate, err := utils.JwtValidate(context.Background(), auth)
if err != nil || !validate.Valid {
http.Error(w, "Invalid token", http.StatusForbidden)
return
}
customClaim, _ := validate.Claims.(*utils.JwtCustomClaim)
ctx := context.WithValue(r.Context(), authString("auth"), customClaim)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
})
}
After rewriting it, I am left with this.
func AuthMiddlewareq() gin.HandlerFunc {
return func(c *gin.Context) {
auth := c.Request.Header.Get("Authorization")
if auth == "" {
c.Next()
// next.ServeHTTP(w, r)
return
}
bearer := "Bearer "
auth = auth[len(bearer):]
validate, err := utils.JwtValidate(context.Background(), auth)
if err != nil || !validate.Valid {
http.Error(c.Writer, "Invalid token", http.StatusForbidden)
// http.Error(w, "Invalid token", http.StatusForbidden)
return
}
customClaim, _ := validate.Claims.(*utils.JwtCustomClaim)
ctx := context.WithValue(c.Request.Context(), authString("auth"), customClaim)
c.Request = c.Request.WithContext(ctx)
c.Next()
// next.ServeHTTP(w, r)
}
}

Golang http server return html or json depending on content type

I am trying to write simple RESTful app in Golang using gorilla mux.
I wrote few handlers that look as follows:
func getUser(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-type") == "application/json" {
w.Header().Set("Content-Type", "application/json")
u, err := _getUser(r)
if err != nil {
http.NotFound(w, r)
return
}
json.NewEncoder(w).Encode(u) //asked for json, return json
} else {
w.Header().Set("Content-Type", "text/html")
u, err := _getUser(r)
if err != nil {
http.NotFound(w, r)
return
}
renderTemplate(w, "view", u) // asked for html, return html
}
}
func _getUser(r *http.Request) (*User, error) {
params := mux.Vars(r)
for _, u := range users {
if u.ID == params["id"] {
return &u, nil
}
}
return nil, errors.New("")
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/v1/users/{id}", getUser).Methods("GET")
}
The problem I got here is that I have got a lot of duplication. Every CRUD method would have to check for content type and return either json or html.
I thought about writing a closure
func htmlOrJSON(fn func(http.ResponseWriter, *http.Request) (interface {}, error), templateName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-type") == "application/json" {
w.Header().Set("Content-Type", "application/json")
result, err := fn(w, r)
if err != nil {
http.NotFound(w, r)
return
}
json.NewEncoder(w).Encode(result)
} else {
w.Header().Set("Content-Type", "text/html")
result, err := fn(w, r)
if err != nil {
http.NotFound(w, r)
return
}
renderTemplate(w, templateName, result)
}
}
}
// and use as:
router.HandleFunc("/v1/users/{id}", htmlOrJSON(getUser, "view")).Methods("GET")
To remove duplication but it also does not look well. Could anyone help me make this code cleaner?
Although this is a code review question and should be in the CodeReview community, I’ll try to answer it.
Write a generic function that handles HTML and JSON rendering. The error handling IMO should happen on each handler even if you duplicate some code. It makes more sense there and makes the code more readable and explicit. You will soon see that there will be other errors that require special handling.
On the logic, most APIs accept query parameters http://api.com/user/1?fomtat=json. This makes more sense because when a client accept more than content types you will be stuck.
const JSON = "application/json"
func getUser(w http.ResponseWriter, r *http.Request) {
u, err := _getUser(r)
if err != nil {
http.NotFound(w, r)
return
}
responseBody(u, r.Header.Get("Content-type"), &w)
}
func responseBody(u User, contentType string, w io.writer) {
switch contentType {
case JSON:
w.Header().Set("Content-Type", JSON)
json.NewEncoder(w).Encode(u) //asked for json, return json
default:
w.Header().Set("Content-Type", "text/html")
renderTemplate(w, "view", u) // asked for html, return html
}
}

Cannot get gorilla session.Value by key

The problem is, cannot get value from session.Value in another file in the same package
In the controllers.go file
func (c *AuthenticationControllers) AdminLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
//AUTHENTICATION
if answer {
w.Write([]byte("Successful Authenticated!"))
//1* Create Session
session, _ := store.Get(r, "session") //SESSION START
///Here I've setted true
session.Values["authenticated"] = true//Successful Authenticated
sessionToken, _ := uuid.NewV4() //Generate session token
session.Values["userid"] = sessionToken.String()
session.Save(r, w)
//Redirect
http.Redirect(w, r, "/admin/application", 302)
} else {
//http.Redirect(w, r, "/login", 302)
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
}
}
}
IN middleware.go file in the same package
func (m *AuthenticationMiddleWares) RequiresLogin(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session") //!!!
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
handler(w, r)
}
}
session.Values["authenticated"] //has nil value, but has to be true after authentification in order to get you access to the route
router.HandleFunc("/admin/applications", authMiddle.RequiresLogin(authContrl.Application()))
What I have done wrong? Why session.Values["authenticated"] returns "nil"
session, _ := store.Get(r, "session") //SESSION START, your session depends on the request r, which is a pointer that is created by the package that calls your controller. If you are calling session from another file, you need to ensure, that it is the same request that is passed into store.Get(). That could be the reason.

Gorilla session.AddFlash Does Not Add Flash Message

I have a registration page with two handlers, one for displaying the form, one for processing a form submission.
I am trying to use a session.AddFlash method to save an error, then do 302 redirect back to the registration form and display the error.
I set up a session store:
package web
import (
"github.com/gorilla/sessions"
)
var sessionStore = sessions.NewCookieStore([]byte(sessionSecret))
Then my handlers look like this:
package web
import (
"html/template"
"log"
"net/http"
)
func registerForm(w http.ResponseWriter, r *http.Request) {
session, err := sessionStore.Get(r, "mysession")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{}
log.Print("Flashes: ")
log.Print(session.Flashes())
if flashes := session.Flashes(); len(flashes) > 0 {
data["error"] = flashes[0]
}
tmpl, _ := template.ParseFiles("web/templates/register.html.tmpl")
tmpl.Execute(w, data)
}
func register(w http.ResponseWriter, r *http.Request) {
session, err := sessionStore.Get(r, "mysession")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
r.ParseForm()
username := r.Form["username"][0]
password := r.Form["password"][0]
if UserExists(username) {
log.Print("Username already taken")
session.AddFlash("Username already taken")
http.Redirect(w, r, "/web/register", http.StatusFound)
return
}
_, err = CreateUser(username, password)
log.Print(err)
if err != nil {
session.AddFlash(err.Error())
http.Redirect(w, r, "/web/register", http.StatusFound)
return
}
http.Redirect(w, r, "/web/login", http.StatusFound)
}
By adding logs I can see that UserExists returns true therefor a flash message should be added however after redirection to the form handler there is no flash message saved in the session.
I think you have to save the session before you redirect.
session.Save(r, w)
http://www.gorillatoolkit.org/pkg/sessions#Session.Save

golang session variables not working. what am i doing incorrectly?

I am using the gorilla/sessions package to implement sessions. the relevant code (or at least what I think is the only relevant part) is as follows:
// Function handler for executing HTML code
func lobbyHandler(w http.ResponseWriter, req *http.Request) {
if isLoggedIn := validateSession(w, req); isLoggedIn {
lobbyTempl.Execute(w, req.Host)
} else {
homeTempl.Execute(w, map[string]string{
"loginErrors": "Must log in first",
})
}
}
// Serves the files as needed, whenever they are requested
// used for all images, js, css, and other static files
func sourceHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:])
}
func loginHandler(w http.ResponseWriter, r *http.Request) {
un, pw := r.FormValue("lUn"), r.FormValue("lPw")
if usr := findUser(un, pw); usr != nil {
if createSession(w, r) {
http.Redirect(w, req, "/lobby.html", http.StatusFound)
}
} else {
homeTempl.Execute(w, map[string]string{
"loginErrors": "User not found",
})
}
}
func createSession(w http.ResponseWriter, r *http.Request) bool {
session, _ := store.Get(r, sessionName)
session.Values["isAuthorized"] = true
if err := session.Save(r, w); err != nil {
fmt.Println("saving error: ", err.Error())
return false
}
return true
}
func validateSession(w http.ResponseWriter, r *http.Request) bool {
if session, err := store.Get(r, sessionName); err == nil {
if v, ok := session.Values["isAuthorized"]; ok && v == true {
fmt.Println("Authorized user identified!")
return true
} else {
fmt.Println("Unauthorized user detected!")
return false
}
}
return false
}
func main() {
//...
// serving files for the game
http.HandleFunc("/", homeHandler)
http.Handle("/ws", websocket.Handler(wsLobbyHandler))
http.HandleFunc("/lobby.html", lobbyHandler)
http.HandleFunc("/formlogin", loginHandler)
//...
//http.HandleFunc("/*.html", SourceHandler)
if err := http.ListenAndServeTLS(*addr, "cert.pem", "key.pem", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}`
in my html i have:
<form id="login_form" action="/formlogin" method="post">
When logging in, the request is handled within loginHandler
The user is identified correctly from the database and a session is created (via createSession()) and placed into the cookie store.
But after the redirect to lobby.html, back in loginHandler
http.Redirect(w, req, "/lobby.html", http.StatusFound)
the validation within lobbyHandler does not work. Does this have to do with the store.Save(...) altering the headers?
I'm very new to go, as well as web apps in general, so I would really appreciate feedback.
Thanks to the comments, i was able to stumble across a similar search that works for me.
session.Options = &sessions.Options{
Path: "/lobby.html",
}
I needed to make sure the cookies know where they are going to be redirected properly.

Resources