package main import ( "fmt" "log" "net/http" "time" "github.com/kataras/jwt" ) // The jwt package provides an extra feature to protect your claims, // you can declare what fields of your custom claims structure are required // (navigate through custom-validations example too). // Useful when the same key can generate more than one token claims type // and you want somehow to separate the token claims type (e.g. a refresh token and a access claims). // // [1] Just change the default Unmarshal function with the following: func init() { jwt.Unmarshal = jwt.UnmarshalWithRequired } // [2] And add a ',required' after your json tags: type userClaims struct { Expiry int64 `json:"exp"` Username string `json:"username,required"` } func main() { http.HandleFunc("/", getTokenHandler) http.HandleFunc("/protected", verifyTokenHandler) http.HandleFunc("/user", getUserTokenHandler) http.HandleFunc("/user/protected", verifyUserTokenHandler) log.Printf("Server listening on: http://localhost:8080") // http://localhost:8080 // http://localhost:8080/protected?token={token} (OK) // http://localhost:8080/user // http://localhost:8080/user/protected?token={user_token} (OK) // [3] http://localhost:8080/user/protected?token={token} (NOT OK) http.ListenAndServe(":8080", nil) } var sharedKey = []byte("sercrethatmaycontainch@r$32chars") // generate token to use. func getTokenHandler(w http.ResponseWriter, r *http.Request) { claims := jwt.Map{"foo": "bar"} // the "username" is missing. token, err := jwt.Sign(jwt.HS256, sharedKey, claims) if err != nil { log.Printf("Generate token failure: %v", err) http.Error(w, "failure: sign and encode the token", http.StatusInternalServerError) return } tokenString := jwt.BytesToString(token) w.Header().Set("Content-Type", "text/html;charset=utf-8") fmt.Fprintf(w, `Token: %s

/protected?token=%s
`, tokenString, tokenString, tokenString) // Add second link of the User Token, it should be result on 401: fmt.Fprintf(w, `
This shows the required usage, try to access the /user with the current "/" token:
/user/protected?token=%s`, tokenString, tokenString) } func verifyTokenHandler(w http.ResponseWriter, r *http.Request) { token := r.URL.Query().Get("token") if token == "" { log.Printf("Token is missing") unauthorized(w) return } verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, []byte(token)) if err != nil { log.Printf("Verify error: %v", err) unauthorized(w) return } var claims jwt.Map if err = verifiedToken.Claims(&claims); err != nil { log.Printf("Verify: decode claims: %v", err) unauthorized(w) return } fmt.Fprintf(w, "This is an authenticated request made of token: %q\n\n", token) fmt.Fprintf(w, "Claims:\n%#+v\n", claims) fmt.Fprintf(w, "Standard Claims:\n%#+v", verifiedToken.StandardClaims) } // generate token to use. func getUserTokenHandler(w http.ResponseWriter, r *http.Request) { claims := userClaims{ Expiry: time.Now().Add(1 * time.Minute).Unix(), Username: "kataras", } token, err := jwt.Sign(jwt.HS256, sharedKey, claims) if err != nil { log.Printf("Generate token failure: %v", err) http.Error(w, "failure: sign and encode the token", http.StatusInternalServerError) return } tokenString := jwt.BytesToString(token) w.Header().Set("Content-Type", "text/html;charset=utf-8") fmt.Fprintf(w, `Token: %s

/user/protected?token=%s`, tokenString, tokenString, tokenString) } func verifyUserTokenHandler(w http.ResponseWriter, r *http.Request) { token := r.URL.Query().Get("token") if token == "" { log.Printf("Token is missing") unauthorized(w) return } verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, []byte(token)) if err != nil { log.Printf("Verify error: %v", err) unauthorized(w) return } var claims userClaims if err = verifiedToken.Claims(&claims); err != nil { // If a different token, e.g. generated by root "/" path // is passed and a "username" field is missing then: // Verify: decode claims: token is missing a required field: "Username" log.Printf("Verify: decode claims: %v", err) unauthorized(w) return } fmt.Fprintf(w, "This is an authenticated request made of token: %q\n\n", token) fmt.Fprintf(w, "Claims:\n%#+v\n", claims) fmt.Fprintf(w, "Standard Claims:\n%#+v", verifiedToken.StandardClaims) } func unauthorized(w http.ResponseWriter) { http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) }