Geek Culture
Published in

Geek Culture

Securing APIs via JWT in GoLang

Json Web Token (JWT)

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Starting with Go Application

You can start directly with an empty Go project. We basically need a client and a server.

project structure

Creating a Simple Client on Go

We’ll start with an OAuth Client in GoLang. We’ll start with a main file, where we’ll need to import the jwt-go library.

import "github.com/golang-jwt/jwt/v4"

Creating a JWT generator

You can create a JWT generator function using the jwt-go library

func GenerateJWTToken() (string, error) {
token := jwt.New(jwt.SigningMethodHS256)

claims := token.Claims.(jwt.MapClaims)

claims["authorized"] = true
claims["user"] = "@binator_1308"
claims["exp"] = time.Now().Add(time.Minute * 30).Unix()

tokenString, err := token.SignedString(mySignedKey)

if err != nil {
fmt.Errorf("generating JWT Token failed")
return "", err
}

return tokenString, nil
}

You can run the method to check if the client can generate the JWT token

Next we can create a method that exposes this feature via http and can create a valid JWT token on a new request

func HomePage(w http.ResponseWriter, r *http.Request) {
validToken, err := GenerateJWTToken()
if err != nil {
fmt.Fprintf(w, err.Error())
}

fmt.Fprintf(w, validToken)
}

We’ll also need to create a handler for this. We’ll use the port 9001 for client

func handleRequests() {
http.HandleFunc("/", HomePage)
log.Fatal(http.ListenAndServe(":9001", nil))
}

You should be able to see a JWT token generated on running the application and visiting localhost:9001

We now have a basic client up and running that can generate a JWT token.

Creating a Simple Server on Go

We’ll now start with a simple server. We can initialise a simple main file.

We can create a dummy homepage that contains some secret information

func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Dummy secret information")
}

We’ll also need a handler that serves it on port 9000

func handleRequests() {
http.HandleFunc("/", homePage)
log.Fatal(http.ListenAndServe(":9000", nil))
}

We should be able to see the dummy secret information on localhost:9000

Now we want to limit this information to validated users who are authorized to access it.

We’ll start by creating a function to authorize requests. Let’s have a basic check for Token header being present for now.

func isAuthorized(endpoint func(w http.ResponseWriter, r *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header["Token"] != nil {

} else {
fmt.Fprintf(w, "Not Authorized")
}
})
}

Next we’ll replace the HandleFunc method with Handle within the handler, and wrap it with our authentication method.

Now we can move on to the authorization method. We can parse the Token header using the signing key and HMAC algorithm (which we used in our client). If it’s valid then we can serve the original endpoint.

func isAuthorized(endpoint func(w http.ResponseWriter, r *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header["Token"] != nil {
token, err := jwt.Parse(r.Header["Token"][0], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("there was an error")
}
return mySigningKey, nil
})

if err != nil {
fmt.Fprintf(w, err.Error())
}

if token.Valid {
endpoint(w, r)
}
} else {
fmt.Fprintf(w, "Not Authorized")
}
})
}

Now we can run the server again. It should still throw the Not Authorized exception on the browser

But on running it on postman with the valid header from client we should be able to fetch the secret information.

We can check the correct header on json.io debugger .The response should be something like this:

decrypting a JWT token

Authenticate requests made to server in Client

We need to add the TOKEN header with the valid JWT to the client to get the request authorized.

client := &http.Client{}
req, _ := http.NewRequest("GET", "http://localhost:9000", nil)
req.Header.Set("TOKEN", validToken)
res, err := client.Do(req)
if (err != nil) {
fmt.Fprintf(w, err.Error())
}

body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Fprintf(w, err.Error())
}

With this our token should be valid and all our requests to server should be serviced.

Conclusion

We now have a basic server and client on GoLang, and have authorized a ReST API via JWT token. The API is now secured.
The code for this article can be found here

Congratulations on making it to the end! Feel free to talk about tech or any cool projects on Twitter, GitHub, Medium, LinkedIn, or Instagram.

Thanks for reading!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Abhinav Singh

Abhinav Singh

Talks tech when excited, anxious, free or bored