• Download
  • Contact
  • Terms of Service
  • Privacy Policy
  • About US
Codershood
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others
No Result
View All Result
Codershood
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others
No Result
View All Result
Codershood
No Result
View All Result

JWT authentication in GoLang Tutorial with Example API

by Shashank Tiwari
March 14, 2020
in GO lang
1
10 Minutes Read
JWT authentication in GoLang Tutorial with Example API Bannner

In this article, we will implement JWT authentication in GoLang. Today we will create REST API which will implement JWT authentication in GoLang, this API will have few endpoints which will demonstrate the working of the JWT authentication. Here we use MongoDB as a database for storing the user details while registration and we will fetch the data from the MongoDB database for login.

If you already know how JWT works, and just want to get started with the implementation, you can skip, or download the source code from here

 

 Download

 




 

Just in case if you don’t know what is JWT and why we use it? Here is a very short description,

1. What is JWT and why we use it?

  1. When a user logs into a system or registers into a system, the SYstem gives that user a token.
  2. Once the User receives that token, then the user should always use that token to make any new API calls. In more technical terms, you would send the token in the header of the request.
  3. Once the Token will be verified from the Backend Logic, then only you will receive the proper response or else you will be considered as logged out user.

A Typical JWT will look like this,

  
   
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.6Y-BVfKrjIPXRnE6KAegk_jN4jLQVBb5pD0eokpPjAs
    
   

There are three parts in JWT which are separated by the dot(.).

  1. The first part of the token is known as Header. The Header tells which algorithm is used to generate the token and what is the type of token.
  2. The second part is called the Payload. This part consists of the application-specific information, for example, token expiry time or any other data that you would like to store in it.
  3. The last part is called a Signature, it is implemented using the first two parts i.e. Header and Payload along with the secret key that you would provide.

If you really wanna dig deep into it, I’ll encourage you to check thejwt.io. You can play with the realtime representation of JWT as you change the Payload or secret key.

2. Implementing the Rest API with JWT

We will create a small JWT Rest API, which will demonstrate the working of the JWT. In this application, we will create below-listed endpoints,

  • /signin (POST): The /signin API will make the user login and along with that will create a new token and give back that token in the response.
  • /signup (POST): The /signupAPI will create a new user and along with that will create a new token and give back that token in the response.
  • /userDetails (GET): The /userDetails API will identify the user with the help of the JWT token and based on that it will fetch the result from the database and it will send the response.

Of course, you will get a full-fledged application when you will download or clone the project but in this article, we will cover only the API implementation.
Since the frontend here is not that big and it doesn’t contain the heavy logics so we won’t be covering that here.

3. Understanding the project structure

Here we will give a very little bit of styling to our web application just to make it look presentable. And we won’t be using any frontend libraries or frameworks, for the sake of simplicity. Inside thepublicfolder, we will write down the Javascript scripts and inside view folder, we will write down the MARKUP.

JWT authentication in GoLang Tutorial with Example API Project Structure

 

Apart from the public folder we have only go files, let’s understand the purpose of each file why we need them.

db.go:The file will have code to connect the Redis database.

routes-handlers.go:This file will have the actual logic of the application. Here we will write code to store the Hash Key with the actual URL and the Redirection Logic will be handled here only.

routes.go: As the name suggests, this file will contain the endpoints that we will define in this application.

server.go: To create the Golang server we will use server.go file.

jwt.go:This file contains the code related to only JWT, such as creating a JWT token and verifying the JWT tokens.

structs.go:All the structs used in this application will go under this file.

4. Creating a GoLang Server

Create aserver.goin the root of the project, which will be our entry point for the project. Here we will make a connection with the MongoDB database and we will define our application routes.

=>Inside the main() function, First we are printing some information.

=>Then using ConnectDatabse() function, we will make a MongoDB connection.

=>In the next line, we will create route variable, which will hold Route instance.

=>Then AddApproutes() function register application routes.

=>And at the end, using http.ListenAndServe() we will start our GO server.

server.go:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {

    log.Println("Server will start at http://localhost:8000/")

    ConnectDatabase()

    route := mux.NewRouter()

    AddApproutes(route)

    log.Fatal(http.ListenAndServe(":8000", route))
}

5. Connecting GoLang to MongoDB

Create adb.goin the root of the project, Here we will connect our GoLang app with MongoDB database.

=>In the below code, first we have to include the MongoDB Go driver i.e.go-mongodb-driver.

=>Then we have created variableClient, which will hold the MongoDB connection instance. ThisClientvariable will be available inside all the files under themainpackage.

=>Inside theConnectDatabse()function we will create MongoDB connection as shown below,

db.go:

package main

import (
    "context"
    "log"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

// Client is exported Mongo Database client
var Client *mongo.Client

// ConnectDatabase is used to connect the MongoDB database
func ConnectDatabase() {
    log.Println("Database connecting...")
    // Set client options
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    // Connect to MongoDB
    client, err := mongo.Connect(context.TODO(), clientOptions)
    Client = client
    if err != nil {
        log.Fatal(err)
    }

    // Check the connection
    err = Client.Ping(context.TODO(), nil)

    if err != nil {
        log.Fatal(err)
    }

    log.Println("Database Connected.")
}

6. Adding GoLang Structs

Create astructs.goin the root of the project, Here we will register all structs used in this application. The reason for creating a separate file is that your code will look more clear and it will become very easy to read.

structs.go:

package main

import jwt "github.com/dgrijalva/jwt-go"

// ErrorResponse is struct for sending error message with code.
type ErrorResponse struct {
    Code    int
    Message string
}

// SuccessResponse is struct for sending error message with code.
type SuccessResponse struct {
    Code     int
    Message  string
    Response interface{}
}

// Claims is  a struct that will be encoded to a JWT.
// jwt.StandardClaims is an embedded type to provide expiry time
type Claims struct {
    Email string
    jwt.StandardClaims
}

// RegistationParams is struct to read the request body
type RegistationParams struct {
    Name     string `json:"name"`
    Email    string `json:"email"`
    Password string `json:"password"`
}

// LoginParams is struct to read the request body
type LoginParams struct {
    Email    string `json:"email"`
    Password string `json:"password"`
}

// SuccessfulLoginResponse is struct to send the request response
type SuccessfulLoginResponse struct {
    Email     string
    AuthToken string
}

// UserDetails is struct used for user details
type UserDetails struct {
    Name     string
    Email    string
    Password string
}

7. Creating and Verifying JWT token

Now let’s write code for creating and verifying the JWT token. In this application, we will use thedgrijalva/jwt-go library for implementing and verifying the JWT token. In this file we will have two methods, the first method will create a JWT token and the second method will verify the token.

=> The CreateJWT() function will create a JWT token and the VerifyToken() function will verify the token. You will find more explanations of the code and how it works.

=> Create jwt.go file and copy-paste the below code,

jwt.go:

package main

import (
    "time"

    "github.com/dgrijalva/jwt-go"
)

var jwtSecretKey = []byte("jwt_secret_key")

// CreateJWT func will used to create the JWT while signing in and signing out
func CreateJWT(email string) (response string, err error) {
    expirationTime := time.Now().Add(5 * time.Minute)
    claims := &Claims{
        Email: email,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, err := token.SignedString(jwtSecretKey)
    if err == nil {
        return tokenString, nil
    }
    return "", err
}

// VerifyToken func will used to Verify the JWT Token while using APIS
func VerifyToken(tokenString string) (email string, err error) {
    claims := &Claims{}

    token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
        return jwtSecretKey, nil
    })

    if token != nil {
        return claims.Email, nil
    }
    return "", err
}

Explanation:

  1. First, we will create a JWT secret and covert it into a byte array.
  2. The function CreateJWT() will expect the email and parameter and returns the JWT token if everything goes fine. Otherwise, it will return an error.
  3. In the CreateJWT() function, first, we create expiration time which will be used to expire the token.
  4. After that, we will create a claim in which we will specify the Email or anything that you want here it can be userid or just a username.
  5. The function jwt.NewWithClaims() expects the algorithm in which the token will be encoded and the claim that you just created.
  6. The method token.SignedString() will give you the actual Authentication token
  7. The VerifyToken() will expect the token string a parameter and returns the email with which it was generated. If anything goes south then it gives an error.
  8. In VerifyToken() method, first, we will create a claims variable that refers to the memory address of the Claims structs.
  9. The method jwt.ParseWithClaims() gives the email if we pass the token string and claims that we just created.

8. Adding GoLang routes and implementing the endpoints

Create aroutes.goin the root of the project, Here we will register application routes. Here we will use gorilla/mux package to register routes.

=>Then AddApproutes() function will register all the routes in the application. Here we have only one route to add which will be used by the FrontEnd javascript.

routes.go:

package main

import (
    "log"

    "github.com/gorilla/mux"
)

// AddApproutes will add the routes for the application
func AddApproutes(route *mux.Router) {

    log.Println("Loadeding Routes...")

    route.HandleFunc("/signin", SignInUser).Methods("POST")

    route.HandleFunc("/signup", SignUpUser).Methods("POST")

    route.HandleFunc("/userDetails", GetUserDetails).Methods("GET")

    log.Println("Routes are Loaded.")
}

Now let’s finish the endpoints by creating the handlers of each respective API. We create three methods as follows SignInUser(), SignUpUser() and GetUserDetails(). The name of the above functions are self-explanatory and we will write them inside the routes-handlers.go.

Now create a routes-handlers.gofile and write down the below code,

routes-handlers.go:

// SignInUser Used for Signing In the Users
func SignInUser(response http.ResponseWriter, request *http.Request) {
    var loginRequest LoginParams
    var result UserDetails
    var errorResponse = ErrorResponse{
        Code: http.StatusInternalServerError, Message: "It's not you it's me.",
    }

    decoder := json.NewDecoder(request.Body)
    decoderErr := decoder.Decode(&loginRequest)
    defer request.Body.Close()

    if decoderErr != nil {
        returnErrorResponse(response, request, errorResponse)
    } else {
        errorResponse.Code = http.StatusBadRequest
        if loginRequest.Email == "" {
            errorResponse.Message = "Last Name can't be empty"
            returnErrorResponse(response, request, errorResponse)
        } else if loginRequest.Password == "" {
            errorResponse.Message = "Password can't be empty"
            returnErrorResponse(response, request, errorResponse)
        } else {

            collection := Client.Database("test").Collection("users")

            ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
            var err = collection.FindOne(ctx, bson.M{
                "email":    loginRequest.Email,
                "password": loginRequest.Password,
            }).Decode(&result)

            defer cancel()

            if err != nil {
                returnErrorResponse(response, request, errorResponse)
            } else {
                tokenString, _ := CreateJWT(loginRequest.Email)

                if tokenString == "" {
                    returnErrorResponse(response, request, errorResponse)
                }

                var successResponse = SuccessResponse{
                    Code:    http.StatusOK,
                    Message: "You are registered, login again",
                    Response: SuccessfulLoginResponse{
                        AuthToken: tokenString,
                        Email:     loginRequest.Email,
                    },
                }

                successJSONResponse, jsonError := json.Marshal(successResponse)

                if jsonError != nil {
                    returnErrorResponse(response, request, errorResponse)
                }
                response.Header().Set("Content-Type", "application/json")
                response.Write(successJSONResponse)
            }
        }
    }
}

// SignUpUser Used for Signing up the Users
func SignUpUser(response http.ResponseWriter, request *http.Request) {
    var registationRequest RegistationParams
    var errorResponse = ErrorResponse{
        Code: http.StatusInternalServerError, Message: "It's not you it's me.",
    }

    decoder := json.NewDecoder(request.Body)
    decoderErr := decoder.Decode(&registationRequest)
    defer request.Body.Close()

    if decoderErr != nil {
        returnErrorResponse(response, request, errorResponse)
    } else {
        errorResponse.Code = http.StatusBadRequest
        if registationRequest.Name == "" {
            errorResponse.Message = "First Name can't be empty"
            returnErrorResponse(response, request, errorResponse)
        } else if registationRequest.Email == "" {
            errorResponse.Message = "Last Name can't be empty"
            returnErrorResponse(response, request, errorResponse)
        } else if registationRequest.Password == "" {
            errorResponse.Message = "Country can't be empty"
            returnErrorResponse(response, request, errorResponse)
        } else {
            tokenString, _ := CreateJWT(registationRequest.Email)

            if tokenString == "" {
                returnErrorResponse(response, request, errorResponse)
            }

            var registrationResponse = SuccessfulLoginResponse{
                AuthToken: tokenString,
                Email:     registationRequest.Email,
            }

            collection := Client.Database("test").Collection("users")
            ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
            _, databaseErr := collection.InsertOne(ctx, bson.M{
                "email":    registationRequest.Email,
                "password": registationRequest.Password,
                "name":     registationRequest.Name,
            })
            defer cancel()

            if databaseErr != nil {
                returnErrorResponse(response, request, errorResponse)
            }

            var successResponse = SuccessResponse{
                Code:     http.StatusOK,
                Message:  "You are registered, login again",
                Response: registrationResponse,
            }

            successJSONResponse, jsonError := json.Marshal(successResponse)

            if jsonError != nil {
                returnErrorResponse(response, request, errorResponse)
            }
            response.Header().Set("Content-Type", "application/json")
            response.WriteHeader(successResponse.Code)
            response.Write(successJSONResponse)
        }
    }
}

// GetUserDetails Used for getting the user details using user token
func GetUserDetails(response http.ResponseWriter, request *http.Request) {
    var result UserDetails
    var errorResponse = ErrorResponse{
        Code: http.StatusInternalServerError, Message: "It's not you it's me.",
    }
    bearerToken := request.Header.Get("Authorization")
    var authorizationToken = strings.Split(bearerToken, " ")[1]

    email, _ := VerifyToken(authorizationToken)
    if email == "" {
        returnErrorResponse(response, request, errorResponse)
    } else {
        collection := Client.Database("test").Collection("users")

        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        var err = collection.FindOne(ctx, bson.M{
            "email": email,
        }).Decode(&result)

        defer cancel()

        if err != nil {
            returnErrorResponse(response, request, errorResponse)
        } else {
            var successResponse = SuccessResponse{
                Code:     http.StatusOK,
                Message:  "You are logged in successfully",
                Response: result.Name,
            }

            successJSONResponse, jsonError := json.Marshal(successResponse)

            if jsonError != nil {
                returnErrorResponse(response, request, errorResponse)
            }
            response.Header().Set("Content-Type", "application/json")
            response.Write(successJSONResponse)
        }
    }
}

Explanation:

  1. In the SignInUser() function, first, we validate the user’s input and if any required input is missing then return a proper error response with HTTP error code.
  2. After that, we use MongoDB client connection object and we check if the user exists in the database or not. If the user not found then again we return a proper error response with HTTP error code.
  3. If the user is found then we useCreateJWT() function and return the success response along with the Authentication token.
  4. In the SignUpUser() function, first, we again validate the user’s input and if any required input is missing then return a proper error response with HTTP error code.
  5. Then we use CreateJWT() function and create an Authentication token.
  6. After that, we use MongoDB client connection object and store the user’s input into the MongoDB database collection.
  7. Once all the above mentioned steps are done then we return the success response along with the Authentication token.
  8. Lastly in GetUserDetails() function, first we read the request header with Authorization. This key should contain the Authorization token as shown below,
    Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6InNoYW5zaGFuazY5QGdtYWlsLmNvbSIsImV4cCI6MTU4MzY5MzU5MX0.bmPs9XkaRlksNkcEEcIe3pOFUlQAFPy0pVA2KKKHhhc
  9. Then we split it by a space " " and get the first index value from the array, which is nothing but our token.
  10. Then using VerifyToken(), we verify the token and get the email in return from VerifyToken() function.
  11. After that using the email we fetch the result from the MongoDB database just to check if the user exists with that email in our database collection.
  12. If the user not found with that email then again we return a proper error response with HTTP error code or else will return the success response along with the Users Details

9. Running the application

To run the application, you need to build the application and then run the executable file as shown below,

> Go build
> ./JSON_Web_Tokens

Now you can test this application using Postman or just a Simple Curl. for example, the response of /signup API will be something like,

{
    "Code": 200,
    "Message": "You are registered, login again",
    "Response": {
        "Email": "shashank@codershood.info",
        "AuthToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6InNoYXNoYW5rQGNvZGVyc2hvb2QuaW5mbyIsImV4cCI6MTU4NDA2OTAxNX0.XmA08EAeN0g6PhlW-H5_xWer_1Brb8CSMjbBk-I8fE8"
    }
}

And the respective CURL request is written below,

curl -X POST \
  http://localhost:8000/signup \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
    "name" : "Shashank",
    "email" : "shashank@codershood.info",
    "password" : "1234567890"
}'

The response of /signup API,

{
    "Code": 200,
    "Message": "You are logged in successfully",
    "Response": "Shashank"
}

And the respective CURL request is written below,

curl -X GET \
  http://localhost:8000/userDetails \
  -H 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6InNoYXNoYW5rQGNvZGVyc2hvb2QuaW5mbyIsImV4cCI6MTU4NDA2OTAxNX0.XmA08EAeN0g6PhlW-H5_xWer_1Brb8CSMjbBk-I8fE8' \
  -H 'cache-control: no-cache'

10. Conclusion

In this application, we implemented JWT authentication as a rest API in GoLang. But there is one part missing and that is refreshing the Authentication token, which is topic another blog post.
Meanwhile, you can play with code and if something doesn’t work let me know in comments.

Tags: Gogo athenticationgo mongodbGolangGoLang apiGolang serverJSON Web TokensJWT
Previous Post

Building a URL Shortener with GoLang and Redis

Next Post

How to Send e-mail using GoLang

Related Posts

Real time private chatting app using React, Golang and mongodb banner-part 2
GO lang

Real time private chatting app using GoLang, React and mongodb – Part 2

July 4, 2020
Real time private chatting app using React, Golang and mongodb banner
GO lang

Real time private chatting app using GoLang, React and Mongodb – Part 1

July 4, 2020
Sending message to specific user with GoLang WebSocket
GO lang

Sending message to specific user with GoLang WebSocket

August 6, 2023
Next Post
How to Send e-mail using GoLang

How to Send e-mail using GoLang

Comments 1

  1. Eric S says:
    5 years ago

    In `routes-handlers.go`, why are you not returning from the routes in the event of an error?

    E.g. you call `returnErrorResponse()`, which returns the error response via HTTP. After this, you continue with the route function. Why is this?

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *




https://codershood.info

www.codershood.info programming blog dedicated to providing high-quality coding tutorial and articles on web development, Angular, React, Laravel, AngularJs, CSS, Node.js, ExpressJs and many more. We also provide ebook based on complicated web application along with the source code.

  • Download
  • Contact
  • Terms of Service
  • Privacy Policy
  • About US

www.codershood.info is licensed under a Creative Commons Attribution 4.0 International License.

No Result
View All Result
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others

www.codershood.info is licensed under a Creative Commons Attribution 4.0 International License.