Skip to content

Instantly share code, notes, and snippets.

@thealexcons
Last active April 16, 2023 03:04
Show Gist options
  • Star 45 You must be signed in to star a gist
  • Fork 22 You must be signed in to fork a gist
  • Save thealexcons/4ecc09d50e6b9b3ff4e2408e910beb22 to your computer and use it in GitHub Desktop.
Save thealexcons/4ecc09d50e6b9b3ff4e2408e910beb22 to your computer and use it in GitHub Desktop.
JSON Web Tokens in Go
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)
}
@thealexcons
Copy link
Author

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

@wuriyanto48
Copy link

very awesome work, @thealexcons

@k0fi
Copy link

k0fi commented Jul 7, 2017

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!

@dverbeek84
Copy link

	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 :(

@wangshubo1989
Copy link

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)

}
`

@rouaks
Copy link

rouaks commented Jul 6, 2018

@wangshubo1989 Thank you!

@Clement-Jean
Copy link

I get this error when I do a POST on login:

  • Error signing token: key is invalid

can someone tell me why ?

@Clement-Jean
Copy link

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)

@steveperjesi
Copy link

steveperjesi commented Sep 6, 2019

@Clement-Jean Thanks! Your change makes everything work 👍

@ZoeZ-Code
Copy link

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?

@sagarvarat
Copy link

// needed to Parse before signing for me
signKey, err := jwt.ParseRSAPrivateKeyFromPEM(SignKey)
if err != nil {
log.Println(err)
}

tokenString, err := token.SignedString(signKey)

@Premm98
Copy link

Premm98 commented Jul 27, 2020

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??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment