Secure Your Api Using Jwt in Golang · schadokar.dev

A working example

Let’s take a quick look at this in the context of a small — but fully functioning — web application.

If you’d like to follow along, create a new basic-auth-example directory on your computer, add a main.go file, initialize a module, and create a pair of locally-trusted TLS certificates using the mkcert tool. Like so:

Complete code #

Create a new file main.go and paste the below code.

packagemainimport("encoding/json""fmt""log""net/http""os""strings""time"jwt"github.com/dgrijalva/jwt-go""github.com/gorilla/mux""github.com/joho/godotenv")// Secret key to uniquely sign the token
varkey[]byte// Credential User's login information
typeCredentialstruct{Usernamestring`json:"username"`Passwordstring`json:"password"`}// Token jwt Standard Claim Object
typeTokenstruct{Usernamestring`json:"username"`jwt.StandardClaims}// Create a dummy local db instance as a key value pair
varuserdb=map[string]string{"user1":"password123",}// assign the secret key to key variable on program's first run
funcinit(){// Load the .env file to access the environment variable
err:=godotenv.Load()iferr!=nil{log.Fatal("Error loading .env file")}// read the secret_key from the .env file
key=[]byte(os.Getenv("SECRET_KEY"))}funcmain(){r:=mux.NewRouter()r.HandleFunc("/login",login).Methods("POST")r.HandleFunc("/me",dashboard).Methods("GET")fmt.Println("Starting server on the port 8000...")log.Fatal(http.ListenAndServe(":8000",r))}// login user login function
funclogin(whttp.ResponseWriter,r*http.Request){// create a Credentials object
varcredsCredential// decode json to struct
err:=json.NewDecoder(r.Body).Decode(&creds)iferr!=nil{w.WriteHeader(http.StatusBadRequest)return}// verify if user exist or not
userPassword,ok:=userdb[creds.Username]// if user exist, verify the password
if!ok||userPassword!=creds.Password{w.WriteHeader(http.StatusUnauthorized)return}// Create a token object
vartokenObj=Token{Username:creds.Username,StandardClaims:jwt.StandardClaims{// Enter expiration in milisecond
ExpiresAt:time.Now().Add(10*time.Minute).Unix(),},}token:=jwt.NewWithClaims(jwt.SigningMethodHS256,tokenObj)tokenString,err:=token.SignedString(key)iferr!=nil{log.Fatal(err)}json.NewEncoder(w).Encode(tokenString)}// dashboard User's personalized dashboard
funcdashboard(whttp.ResponseWriter,r*http.Request){// get the bearer token from the reuest header
bearerToken:=r.Header.Get("Authorization")// validate token, it will return Token and error
token,err:=ValidateToken(bearerToken)iferr!=nil{// check if Error is Signature Invalid Error
iferr==jwt.ErrSignatureInvalid{// return the Unauthorized Status
w.WriteHeader(http.StatusUnauthorized)return}// Return the Bad Request for any other error
w.WriteHeader(http.StatusBadRequest)return}// Validate the token if it expired or not
if!token.Valid{// return the Unauthoried Status for expired token
w.WriteHeader(http.StatusUnauthorized)return}// Type cast the Claims to *Token type
user:=token.Claims.(*Token)// send the username Dashboard message
json.NewEncoder(w).Encode(fmt.Sprintf("%s Dashboard",user.Username))}// ValidateToken validates the token with the secret key and return the object
funcValidateToken(bearerTokenstring)(*jwt.Token,error){// format the token string
tokenString:=strings.Split(bearerToken," ")[1]// Parse the token with tokenObj
token,err:=jwt.ParseWithClaims(tokenString,&Token{},func(token*jwt.Token)(interface{},error){returnkey,nil})// return token and err
returntoken,err}

Create a directory

Create a directory called jwt-practice.

Похожее:  Передать показания и оплатить электричество всего за минуту можно в Личном кабинете клиента.

Create server and routes #

First, create a new instance of mux router using the mux.NewRouter() method.

Using the HandleFunc create 2 routes. /login as a POST request and /me as a GET request.The /login endpoint will execute login function and /me will execute the dashboard function.

Environment variable #

Create a new .env file and paste the below code in it.

Using the godotenv package, we can read the SECRET_KEY.

Exploring json web token

Under this section, we will comprehensively understand what is JWT, how does JSON Web token look like, and what JSON web token consists of.

Generate jwt

Write the following function to create Golang JWT:

The GenerateJWT() function takes email and role as input. Creates a token by HS256 signing method and adds authorized email, role, and exp into claims. Claims are pieces of information added into tokens.

Handling post authentication routes

Now that all logged in clients have session information stored on their end as cookies, we can use it to:

Let’s write our Welcome handler to do just that:

How jwt works? #

The JWT structured in three parts:

  • Header: It identify which algorithm is used
  • Payload: The information
  • Signature: The encryption of the information by a Secret Key using the Algorithm

The three parts are separated by the dot(.).For Ex:
Header.Payload.Signature

Let’s understand it with an example.
Suppose the Header is algorithm1 and algorithm1 is the below equation.

Then, the Payload is 11 and the SecretKey is 5.

When the Payload and SecretKey is passed to the algorithm1 it will generate a unique Signature.

The Signature is 55.

Then the JWT token will look like this.

This JWT token will be shared with the requested party. After this whenever this party raise a request to this party, it will also share this token to authenticate itself.When the first party receives the request with the token, it will first validate the token before processing the request.

As all the details are available in the token it is very easy task for the party to validate the token. It will take the Payload and passed it to the Header (algorithm1) using the SecretKey which it already have to generate the Signature.

The actual JWT Token looks like this.

Take a quick look and you can see the token is divided in 3 parts:

  • Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  • Payload: eyJuYW1lIjoiSSBhbSBJcm9uIE1hbi4ifQ
  • Signature: li-FDEyAdayupFIS5P2EKexN-Rm_SWe4LXO9Xjyja4o

The Header and Payload are base64 encoded.

Try it

Output

The algorithm in Header is HS256 which is used to sign the Payload.The Payload is a JSON object with a key as name and value as I am Iron Man.

To sign this payload, the SecretKey is secretKey. 😅

How the jwt signature works

So if the header and signature of a JWT can be accessed by anyone, what actually makes a JWT secure? The answer lies in how the third portion (the signature) is generated.

Implementation in go

Now that we’ve seen how JWT based authentication works, let’s implement it using Go.

Implementing golang jwt authentication and authorization

Follow these steps for Golang JWT Authentication and Authorization-

Jwt implementation #

In this section, we can divide the process in modules to understand clearly.

Making a request to a protected resource

Finally, when you need to access a protected resource, Go makes it very straightforward. All you need to do is call the r.SetBasicAuth() method on your request before executing it. Like so:

Prerequisites #

  • Go v1.11 or greater (I am using Go v1.14)
  • Code Editor (For ex. VS Code, Atom)
  • Postman – to test the APIs. Curl commands can also be used.

Project structure #

Create a new project go-jwt-app.Open the terminal inside the go-jwt-app and initialize the go modules using the below command.

Go modules is a dependency manager or a package manager. It will track all the dependencies used in the project with their version. You can read more about it here.

Protecting a web application

The simplest way to protect your application is to create some middleware. In this middleware we want to do three things:

Setting up the application

go build compiles the application. ./new is used to run the compiled file. This file will be located in your root folder and may have another name. I simply named mine new.

  • Create a folder (package) named “database”. This folder will contain a file which manages your database connection and opens your collections. Create the file and name “databaseConnection.go” and fill it with the following code.
  • Create a Main.go file in your root folder and leave it empty for now

Test the application #

Open the Postman and create a new POST request.

Send the request. It will send a JWT token in the response.
Copy the JWT Token.

login

Create a new GET request.

In the Token field, paste the JWT token from the last request and hit Send.

Verify the token #

When the /me endpoint hit, it will execute the dashboard function.We are going to send the token as Bearer Token. You can also send it as a key value pair in the request object.

In simple language, Bearer token is the same token with Bearer prefixed to it.

// dashboard User's personalized dashboard
funcdashboard(whttp.ResponseWriter,r*http.Request){// get the bearer token from the reuest header
bearerToken:=r.Header.Get("Authorization")// validate token, it will return Token and error
token,err:=ValidateToken(bearerToken)iferr!=nil{// check if Error is Signature Invalid Error
iferr==jwt.ErrSignatureInvalid{// return the Unauthorized Status
w.WriteHeader(http.StatusUnauthorized)return}// Return the Bad Request for any other error
w.WriteHeader(http.StatusBadRequest)return}// Validate the token if it expired or not
if!token.Valid{// return the Unauthoried Status for expired token
w.WriteHeader(http.StatusUnauthorized)return}// Type cast the Claims to *Token type
user:=token.Claims.(*Token)// send the username Dashboard message
json.NewEncoder(w).Encode(fmt.Sprintf("%s Dashboard",user.Username))}// ValidateToken validates the token with the secret key and return the object
funcValidateToken(bearerTokenstring)(*jwt.Token,error){// format the token string
tokenString:=strings.Split(bearerToken," ")[1]// Parse the token with tokenObj
token,err:=jwt.ParseWithClaims(tokenString,&Token{},func(token*jwt.Token)(interface{},error){returnkey,nil})// return token and err
returntoken,err}

Get the Bearer Token from the request header. The Bearer token’s key is Authorization.

Pass the bearerToken to the ValidateToken function. This function will validate the token if it is valid or not.Using the ParseWithClaims method from the jwt-go package, it will validate the token and returns a *jwt.Token object and an error.

What comprises a json web token?

A JSON Web Token consists of three parts which are separated using .(dot) :

What is basic authentication? when should i use it?

As a developer, you’re probably already familiar with the prompt that web browsers show when you visit a protected URL.

What is jwt? #

JWT stands for JSON Web Token. JWT represents a claim between two parties which is shared in a JSON format.

In simple words, it is similar to your ID cards. In school, college, office etc places this ID card is provided by the organization to you to authenticate yourself whenever you enter the premises. 🧐

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.

Conclusion #

In this tutorial, we used the HS256 algorithm which accepts a text as a Secret Key. For more security, you can use other algorithms like ECDSA.For ECDSA, you have to first create a private-public key pair.

JWT is not the only method to secure the APIs. You should check out them too.Thanks for reading.

Cover is designed in Canva

Install the dependencies #

There are 3 packages used in this Project.

  1. Gorilla/mux: It is a feature rich package to create the APIs and server.
  2. jwt-go: It is a Golang implementation of JSON Web Token(JWT). Using this package, we can create and verify the JWT tokens.
  3. godotenv: Using this package, we can access the .env file in which environment variables can be saved.

Initializing with go.mod

Initialize it with go.mod, for dependency management, using –

Downloading dependencies

Next, we will download the required dependencies. We will use

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *