Skip to content

Instantly share code, notes, and snippets.

@lyda
Created December 29, 2017 15:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lyda/80b252078c588b9b71e0ae5f0723b472 to your computer and use it in GitHub Desktop.
Save lyda/80b252078c588b9b71e0ae5f0723b472 to your computer and use it in GitHub Desktop.
JsonWebTokens Example with generated key.
/* rewritten example of JsonWebToken example
* from https://gist.github.com/thealexcons/4ecc09d50e6b9b3ff4e2408e910beb22
* Keys are generated and packed to PEM-format at server start
* Plus some fixes and refactors
*
* My respect to thealexcons
*/
package main
import (
"bytes"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"log"
"net/http"
"time"
"github.com/codegangsta/negroni"
jwt "github.com/dgrijalva/jwt-go"
)
//RSA KEYS AND INITIALISATION
var signingKey, verificationKey []byte
func initKeys() {
var (
err error
privKey *rsa.PrivateKey
pubKey *rsa.PublicKey
pubKeyBytes []byte
)
privKey, err = rsa.GenerateKey(cryptorand.Reader, 2048)
if err != nil {
log.Fatal("Error generating private key")
}
pubKey = &privKey.PublicKey //hmm, this is stdlib manner...
// Create signingKey from privKey
// prepare PEM block
var privPEMBlock = &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privKey), // serialize private key bytes
}
// serialize pem
privKeyPEMBuffer := new(bytes.Buffer)
pem.Encode(privKeyPEMBuffer, privPEMBlock)
//done
signingKey = privKeyPEMBuffer.Bytes()
fmt.Println(string(signingKey))
// create verificationKey from pubKey. Also in PEM-format
pubKeyBytes, err = x509.MarshalPKIXPublicKey(pubKey) //serialize key bytes
if err != nil {
// heh, fatality
log.Fatal("Error marshalling public key")
}
var pubPEMBlock = &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubKeyBytes,
}
// serialize pem
pubKeyPEMBuffer := new(bytes.Buffer)
pem.Encode(pubKeyPEMBuffer, pubPEMBlock)
// done
verificationKey = pubKeyPEMBuffer.Bytes()
fmt.Println(string(verificationKey))
}
//STRUCT DEFINITIONS
type userCredentials struct {
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 localhost:17000")
http.ListenAndServe(":17000", nil)
}
func main() {
initKeys()
startServer()
}
//////////////////////////////////////////
/////////////ENDPOINT HANDLERS////////////
/////////////////////////////////////////
func protectedHandler(w http.ResponseWriter, r *http.Request) {
resp := response{"Gained access to protected resource"}
jsonResponse(resp, 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 user.Username != "alexcons" || 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.SigningMethodRS256)
//set claims
signer.Claims["iss"] = "admin"
signer.Claims["iat"] = time.Now().Unix()
signer.Claims["exp"] = time.Now().Add(time.Minute * 20).Unix()
signer.Claims["jti"] = "1" // should be user ID(?)
signer.Claims["CustomUserInfo"] = struct {
Name string
Role string
}{user.Username, "Member"}
tokenString, err := signer.SignedString(signingKey)
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
resp := token{tokenString}
jsonResponse(resp, 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 verificationKey, 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"+err.Error())
}
}
//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)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment