-
-
Save thealexcons/4ecc09d50e6b9b3ff4e2408e910beb22 to your computer and use it in GitHub Desktop.
| package main | |
| import ( | |
| "io/ioutil" | |
| "log" | |
| "strings" | |
| "net/http" | |
| "encoding/json" | |
| "fmt" | |
| "time" | |
| "github.com/codegangsta/negroni" | |
| "github.com/dgrijalva/jwt-go" | |
| "os" | |
| ) | |
| //RSA KEYS AND INITIALISATION | |
| const ( | |
| privKeyPath = "path/to/keys/app.rsa" | |
| pubKeyPath = "path/to/keys/app.rsa.pub" | |
| ) | |
| var VerifyKey, SignKey []byte | |
| func initKeys(){ | |
| var err error | |
| SignKey, err = ioutil.ReadFile(privKeyPath) | |
| if err != nil { | |
| log.Fatal("Error reading private key") | |
| return | |
| } | |
| VerifyKey, err = ioutil.ReadFile(pubKeyPath) | |
| if err != nil { | |
| log.Fatal("Error reading public key") | |
| return | |
| } | |
| } | |
| //STRUCT DEFINITIONS | |
| type UserCredentials struct { | |
| Username string `json:"username"` | |
| Password string `json:"password"` | |
| } | |
| type User struct { | |
| ID int `json:"id"` | |
| Name string `json:"name"` | |
| Username string `json:"username"` | |
| Password string `json:"password"` | |
| } | |
| type Response struct { | |
| Data string `json:"data"` | |
| } | |
| type Token struct { | |
| Token string `json:"token"` | |
| } | |
| //SERVER ENTRY POINT | |
| func StartServer(){ | |
| //PUBLIC ENDPOINTS | |
| http.HandleFunc("/login", LoginHandler) | |
| //PROTECTED ENDPOINTS | |
| http.Handle("/resource/", negroni.New( | |
| negroni.HandlerFunc(ValidateTokenMiddleware), | |
| negroni.Wrap(http.HandlerFunc(ProtectedHandler)), | |
| )) | |
| log.Println("Now listening...") | |
| http.ListenAndServe(":8000", nil) | |
| } | |
| func main() { | |
| initKeys() | |
| StartServer() | |
| } | |
| ////////////////////////////////////////// | |
| /////////////ENDPOINT HANDLERS//////////// | |
| ///////////////////////////////////////// | |
| func ProtectedHandler(w http.ResponseWriter, r *http.Request){ | |
| response := Response{"Gained access to protected resource"} | |
| JsonResponse(response, w) | |
| } | |
| func LoginHandler(w http.ResponseWriter, r *http.Request) { | |
| var user UserCredentials | |
| //decode request into UserCredentials struct | |
| err := json.NewDecoder(r.Body).Decode(&user) | |
| if err != nil { | |
| w.WriteHeader(http.StatusForbidden) | |
| fmt.Fprintf(w, "Error in request") | |
| return | |
| } | |
| fmt.Println(user.Username, user.Password) | |
| //validate user credentials | |
| if strings.ToLower(user.Username) != "alexcons" { | |
| if user.Password != "kappa123" { | |
| w.WriteHeader(http.StatusForbidden) | |
| fmt.Println("Error logging in") | |
| fmt.Fprint(w, "Invalid credentials") | |
| return | |
| } | |
| } | |
| //create a rsa 256 signer | |
| signer := jwt.New(jwt.GetSigningMethod("RS256")) | |
| //set claims | |
| signer.Claims["iss"] = "admin" | |
| signer.Claims["exp"] = time.Now().Add(time.Minute * 20).Unix() | |
| signer.Claims["CustomUserInfo"] = struct { | |
| Name string | |
| Role string | |
| }{user.Username, "Member"} | |
| tokenString, err := signer.SignedString(SignKey) | |
| if err != nil { | |
| w.WriteHeader(http.StatusInternalServerError) | |
| fmt.Fprintln(w, "Error while signing the token") | |
| log.Printf("Error signing token: %v\n", err) | |
| } | |
| //create a token instance using the token string | |
| response := Token{tokenString} | |
| JsonResponse(response, w) | |
| } | |
| //AUTH TOKEN VALIDATION | |
| func ValidateTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | |
| //validate token | |
| token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error){ | |
| return VerifyKey, nil | |
| }) | |
| if err == nil { | |
| if token.Valid{ | |
| next(w, r) | |
| } else { | |
| w.WriteHeader(http.StatusUnauthorized) | |
| fmt.Fprint(w, "Token is not valid") | |
| } | |
| } else { | |
| w.WriteHeader(http.StatusUnauthorized) | |
| fmt.Fprint(w, "Unauthorised access to this resource") | |
| } | |
| } | |
| //HELPER FUNCTIONS | |
| func JsonResponse(response interface{}, w http.ResponseWriter) { | |
| json, err := json.Marshal(response) | |
| if err != nil { | |
| http.Error(w, err.Error(), http.StatusInternalServerError) | |
| return | |
| } | |
| w.WriteHeader(http.StatusOK) | |
| w.Header().Set("Content-Type", "application/json") | |
| w.Write(json) | |
| } |
very awesome work, @thealexcons
This code is obsolete now. Please update for go-jwt 3.0. As you said in your video a simple tutorial like this is so rare to find!
if strings.ToLower(user.Username) != "alexcons" {
if user.Password != "kappa123" {
w.WriteHeader(http.StatusForbidden)
fmt.Println("Error logging in")
fmt.Fprint(w, "Invalid credentials")
return
}
}
if username is other than alexcons it will also pass :(
the example should change something:
- remove os package and add "github.com/dgrijalva/jwt-go/request"
- change LoginHandler
- change ValidateTokenMiddleware
the whole code:
`package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
"github.com/codegangsta/negroni"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
)
//RSA KEYS AND INITIALISATION
const (
privKeyPath = "path/to/keys/app.rsa"
pubKeyPath = "path/to/keys/app.rsa.pub"
)
var VerifyKey, SignKey []byte
func initKeys() {
var err error
SignKey, err = ioutil.ReadFile(privKeyPath)
if err != nil {
log.Fatal("Error reading private key")
return
}
VerifyKey, err = ioutil.ReadFile(pubKeyPath)
if err != nil {
log.Fatal("Error reading public key")
return
}
}
//STRUCT DEFINITIONS
type UserCredentials struct {
Username string json:"username"
Password string json:"password"
}
type User struct {
ID int json:"id"
Name string json:"name"
Username string json:"username"
Password string json:"password"
}
type Response struct {
Data string json:"data"
}
type Token struct {
Token string json:"token"
}
//SERVER ENTRY POINT
func StartServer() {
//PUBLIC ENDPOINTS
http.HandleFunc("/login", LoginHandler)
//PROTECTED ENDPOINTS
http.Handle("/resource/", negroni.New(
negroni.HandlerFunc(ValidateTokenMiddleware),
negroni.Wrap(http.HandlerFunc(ProtectedHandler)),
))
log.Println("Now listening...")
http.ListenAndServe(":8000", nil)
}
func main() {
initKeys()
StartServer()
}
//////////////////////////////////////////
/////////////ENDPOINT HANDLERS////////////
/////////////////////////////////////////
func ProtectedHandler(w http.ResponseWriter, r *http.Request) {
response := Response{"Gained access to protected resource"}
JsonResponse(response, w)
}
func LoginHandler(w http.ResponseWriter, r *http.Request) {
var user UserCredentials
//decode request into UserCredentials struct
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, "Error in request")
return
}
fmt.Println(user.Username, user.Password)
//validate user credentials
if strings.ToLower(user.Username) != "alexcons" {
if user.Password != "kappa123" {
w.WriteHeader(http.StatusForbidden)
fmt.Println("Error logging in")
fmt.Fprint(w, "Invalid credentials")
return
}
}
//create a rsa 256 signer
signer := jwt.New(jwt.GetSigningMethod("RS256"))
//set claims
claims := make(jwt.MapClaims)
claims["iss"] = "admin"
claims["exp"] = time.Now().Add(time.Minute * 20).Unix()
claims["CustomUserInfo"] = struct {
Name string
Role string
}{user.Username, "Member"}
signer.Claims = claims
tokenString, err := signer.SignedString(SignKey)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "Error while signing the token")
log.Printf("Error signing token: %v\n", err)
}
//create a token instance using the token string
response := Token{tokenString}
JsonResponse(response, w)
}
//AUTH TOKEN VALIDATION
func ValidateTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
//validate token
token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor,
func(token *jwt.Token) (interface{}, error) {
return VerifyKey, nil
})
if err == nil {
if token.Valid {
next(w, r)
} else {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, "Token is not valid")
}
} else {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, "Unauthorised access to this resource")
}
}
//HELPER FUNCTIONS
func JsonResponse(response interface{}, w http.ResponseWriter) {
json, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
w.Write(json)
}
`
@wangshubo1989 Thank you!
I get this error when I do a POST on login:
- Error signing token: key is invalid
can someone tell me why ?
I found
you need to remove this :
//create a rsa 256 signer
signer := jwt.New(jwt.GetSigningMethod("RS256"))
//set claims
claims := make(jwt.MapClaims)
claims["iss"] = "admin"
claims["exp"] = time.Now().Add(time.Minute * 20).Unix()
claims["CustomUserInfo"] = struct {
Name string
Role string
}{user.Username, "Member"}
signer.Claims = claims
tokenString, err := signer.SignedString(SignKey)
and use this instead :
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iss": "admin",
"exp": time.Now().Add(time.Minute * 20).Unix(),
"CustomUserInfo": struct {
Name string
Role string
}{user.Username, "Member"},
})
tokenString, err := token.SignedString(SignKey)
@Clement-Jean Thanks! Your change makes everything work 👍
I found
you need to remove this :
//create a rsa 256 signer signer := jwt.New(jwt.GetSigningMethod("RS256")) //set claims claims := make(jwt.MapClaims) claims["iss"] = "admin" claims["exp"] = time.Now().Add(time.Minute * 20).Unix() claims["CustomUserInfo"] = struct { Name string Role string }{user.Username, "Member"} signer.Claims = claims tokenString, err := signer.SignedString(SignKey)and use this instead :
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "iss": "admin", "exp": time.Now().Add(time.Minute * 20).Unix(), "CustomUserInfo": struct { Name string Role string }{user.Username, "Member"}, }) tokenString, err := token.SignedString(SignKey)
Hi, thanks a lot! But it seems you are using HMAC signing method instead of the original RSA. I still got key is invalid error when using RSA method. Could you check if the RSA signing method works for you?
// needed to Parse before signing for me
signKey, err := jwt.ParseRSAPrivateKeyFromPEM(SignKey)
if err != nil {
log.Println(err)
}
tokenString, err := token.SignedString(signKey)
Hi,
I have an issue.
My login endpoint is working after using the jwt.ParseRSAPrivateKeyFromPEM() function, it is creating the token but when i am using the token for '/resource' end-point, the response i get is "Unauthorised access to resource".
Can you help me with this??
To access the protected resource at_ /resource_, make sure to add the following header to the request:
Key: Authorization
Value:Bearer long_token_string_goes_here