I'm a new beginner of Golang, I start learning Gorilla/Mux with JWT.
I'm just success in doing register a user to MongoDB and then login. I use Postman to test. But when I try to browse to protected route. I get the error "Missing auth token" as I have in the verification.
How can postman get the authorization to browse protected route?
Below is the code of register and login:
Router Function
func Router() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.Use(CommonMiddleware)
//router.HandleFunc("/", middleware.TestAPI).Methods("GET", "OPTIONS")
router.HandleFunc("/register", middleware.RegisterHandler).Methods("POST", "OPTIONS")
router.HandleFunc("/login", middleware.LoginHandler).Methods("POST", "OPTIONS")
secure := router.PathPrefix("/auth").Subrouter()
secure.Use(auth.JwtVerify)
secure.HandleFunc("/api/task", middleware.GetAllTask).Methods("GET", "OPTIONS")
secure.HandleFunc("/api/task", middleware.CreateTask).Methods("POST", "OPTIONS")
secure.HandleFunc("/api/task/{id}", middleware.TaskComplete).Methods("PUT", "OPTIONS")
secure.HandleFunc("/api/undoTask/{id}", middleware.UndoTask).Methods("PUT", "OPTIONS")
secure.HandleFunc("/api/deleteTask/{id}", middleware.DeleteTask).Methods("DELETE", "OPTIONS")
secure.HandleFunc("/api/deleteAllTask", middleware.DeleteAllTask).Methods("DELETE", "OPTIONS")
return router
}
func CommonMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Access-Control-Request-Headers, Access-Control-Request-Method, Connection, Host, Origin, User-Agent, Referer, Cache-Control, X-header")
next.ServeHTTP(w, r)
})
}
Register function
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user models.User
body, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(body, &user)
var res models.ResponseResult
if err != nil {
res.Error = err.Error()
json.NewEncoder(w).Encode(res)
return
}
var result models.User
err = usercollection.FindOne(context.TODO(), bson.D{{"username", user.Username}}).Decode(&result)
if err != nil {
if err.Error() == "mongo: no documents in result" {
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
if err != nil {
res.Error = "Error While Hashing Password, Try Again"
json.NewEncoder(w).Encode(res)
return
}
user.Password = string(hash)
_, err = usercollection.InsertOne(context.TODO(), user)
if err != nil {
res.Error = "Error While Creating User, Try Again"
json.NewEncoder(w).Encode(res)
return
}
res.Result = "Register Successful"
json.NewEncoder(w).Encode(res)
return
}
res.Error = err.Error()
json.NewEncoder(w).Encode(res)
return
}
res.Result = "Username already exists!!"
json.NewEncoder(w).Encode(res)
return
}
Login Function
func LoginHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user models.User
body, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(body, &user)
if err != nil {
log.Fatal(err)
}
//var resultToken models.Token
var resultUser models.User
var res models.ResponseResult
err = usercollection.FindOne(context.TODO(), bson.D{{"username", user.Username}}).Decode(&resultUser)
if err != nil {
res.Error = "Invalid username"
json.NewEncoder(w).Encode(res)
return
}
expiresAt := time.Now().Add(time.Minute * 100000).Unix()
errf := bcrypt.CompareHashAndPassword([]byte(resultUser.Password), []byte(user.Password))
if errf != nil && errf == bcrypt.ErrMismatchedHashAndPassword {
var res = map[string]interface{}{"status": false, "message": "Invalid login credential. Please try again"}
json.NewEncoder(w).Encode(res)
return
}
tk := &models.Token{
Username: user.Username,
StandardClaims: &jwt.StandardClaims{
ExpiresAt: expiresAt,
},
}
token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
tokenString, err := token.SignedString([]byte("secret"))
if err != nil {
res.Error = "Error while generating token, Try again"
json.NewEncoder(w).Encode(res)
return
}
var resp = map[string]interface{}{"status": false, "message": "logged in"}
resp["token"] = tokenString
resp["tk"] = tk
json.NewEncoder(w).Encode(resp)
}
Verification Function
func JwtVerify(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var header = r.Header.Get("x-access-token")
json.NewEncoder(w).Encode(r)
header = strings.TrimSpace(header)
if header == "" {
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(Exception{Message: "Missing auth token"})
return
}
tk := &models.Token{}
_, err := jwt.ParseWithClaims(header, tk, func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err != nil {
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(Exception{Message: err.Error()})
return
}
ctx := context.WithValue(r.Context(), "user", tk)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
Since you are checking for the token x-access-token
in the request header, you need to add the same while sending the request. This can be easily done in Postman as shown below -
The router I've used is -
package main
func router() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
secure := router.PathPrefix("/auth").Subrouter()
secure.Use(auth.JwtVerify)
secure.HandleFunc("/api", middleware.ApiHandler).Methods("GET")
return router
}
func main() {
r := router()
http.ListenAndServe(":8080", r)
}
The middleware I've used is -
package auth
func JwtVerify(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var header = r.Header.Get("x-access-token")
json.NewEncoder(w).Encode(r)
header = strings.TrimSpace(header)
if header == "" {
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode("Missing auth token")
return
} else {
json.NewEncoder(w).Encode(fmt.Sprintf("Token found. Value %s", header))
}
next.ServeHTTP(w, r)
})
}
and the handler is -
package middleware
func ApiHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode("SUCCESS!")
return
}