JSON Web Tokens (JWTs) are a popular method for dealing with online authentication, and you can implement JWT authentication in any server-side programming language.
For background reading JWTs in general, I recommend learning more about JWTs, best practices, and securing RESTful APIs with JWTs with these articles on the LogRocket blog.
This article is aimed at helping you get started with implementing JWT authentication in your Go web applications using the
golang-jwt package is the most popular package for implementing JWTs in Go, owing to its features and ease of use. The
golang-jwt package provides functionality for generating and validating JWTs.
You’ll need to meet these basic requirements to get the most out of this tutorial.
- Go 1.16 or later installed on your machine (for security reasons)
- Experience building web applications in Go or any other language (optional)
Table of Contents
Getting started with the Golang-JWT package
After setting up your Go workspace and initializing the Go modules file
go.mod, run this command on your terminal in the workspace directory to install the
go get github.com/golang-jwt/jwt
Once you’ve installed the
golang-jwt, create a Go file and import these packages and modules.
import ( "log" "encoding/json" "github.com/golang-jwt/jwt" "net/http" "time" )
You’ll use these packages in this tutorial to log errors, set up a server, and set the token expiration time.
Setting up a web server in Go
Let’s start with creating a simple web server with an endpoint that will be secured with a JWT.
func main() http.HandleFunc("/home", handlePage) err := http.ListenAndServe(":8080", nil) if err != nil log.Println("There was an error listening on port :8080", err)
The main function sets up the home endpoint with a handler function
handlePage that you’ll set up. The
handlePage function will secure the page using JWTs. The server is set to listen on port
:8080, but you can use any port of your choice.
handlePage handler function will return the encoded JSON of the
Message struct as a response to the client if the request is authorized after the request body is encoded.
type Message struct Status string `json:"status"` Info string `json:"info"` func handlePage(writer http.ResponseWriter, request *http.Request) writer.Header().Set("Content-Type", "application/json") var message Message err := json.NewDecoder(request.Body).Decode(&message) if err != nil return err = json.NewEncoder(writer).Encode(message) if err != nil return
handlePage function, at this point, isn’t authenticated and making requests to the page will work freely. You’ll learn how to add authentication to your handler functions later in this tutorial.
Generating JWTs for authentication using the
You will need a secret key to generate JWT tokens using the
golang-jwt package. Here’s an example private key for this tutorial; however, you should use a cryptographically secure string for your secret key and load it from an environment variables file (.env).
Check out this article to learn how to use environment variables in your Go applications.
var sampleSecretKey = byte("SecretYouShouldHide")
Kindly note that whoever has the secret key you use for your JWTs can authenticate users of your application. The
sampleSecretKey variable holds the private key in this case.
Here’s a function for generating JWT tokens. The function should return a string and an error. If there’s an error generating the JWT, the function returns an empty string and the error. If there are no errors, the function returns the JWT string and the
More great articles from LogRocket:
func generateJWT() (string, error)
You can create a new token using the
New method of the JWT package. The
New method takes in a signing method (the cryptographic algorithm for the JWT) and returns a JWT token.
token := jwt.New(jwt.SigningMethodEdDSA)
If you want to modify the JWT, you can use the
Claims method of the token.
claims := token.Claims.(jwt.MapClaims) claims["exp"] = time.Now().Add(10 * time.Minute) claims["authorized"] = true claims["user"] = "username"
In this case, you’re setting an expiry time for the JWT, which is ten minutes, using the
time module and the username and authorization status. You’ll be able to retrieve the claims when attempting to verify the JWT.
The final part of generating a JWT is to sign the string using your secret key. You can sign your token string using the
SignedString method of the token. The
SignedString method takes the secret key and returns a signed token string.
tokenString, err := token.SignedString(sampleSecretKey) if err != nil return "", err return tokenString, nil
In cases where there are errors signing the token, you can return an empty string and the error.
Unlike cookies, you don’t need to store JWT; all you need is your signing key to verify tokens.
Verifying JWT tokens
The conventional method of verifying JWTs uses middleware (handler functions that take in other handler functions for operations). Here’s how to use middleware to verify that a request is authorized.
func verifyJWT(endpointHandler func(writer http.ResponseWriter, request *http.Request)) http.HandlerFunc
verifyJWT function is a middleware that takes in the handler function for the request you want to verify. The handler function uses the token parameter from the request header to verify the request and respond based on the status.
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) )
verifyJWT function returns the handler function passed in as a parameter if the request is authorized.
The first step to verifying JWTs is to inspect the token in the request’s header.
if request.Header["Token"] != nil
If there’s a token, you can proceed to verify the token and verify claims.
You’ll have to parse the token, and you can parse the token using the
Parse method of the
jwt package. The
parse method takes in the token and a JWT decorator function and returns an interface and an error.
You need to use the same signing method you used to sign the token when you generated it to verify the signature using the
Method method of the token. In this case, the signing method was the ECDSA method.
token, err := jwt.Parse(request.Header\["Token"\], func(token *jwt.Token) (interface, error) _, ok := token.Method.(*jwt.SigningMethodECDSA) if !ok writer.WriteHeader(http.StatusUnauthorized) _, err := writer.Write(byte("You're Unauthorized!")) if err != nil return nil, err return "", nil )
If the signature verification fails (the function returns
!ok), you can return a
StatusUnauthorized header to the client.
if err != nil writer.WriteHeader(http.StatusUnauthorized) _, err2 := writer.Write(byte("You're Unauthorized due to error parsing the JWT")) if err2 != nil return
In the code above, there’s an error parsing the token. Therefore, the user is unauthorized, and you can write a message and return an unauthorized status.
You can validate the token using the
Valid method of the token.
if token.Valid endpointHandler(writer, request) else writer.WriteHeader(http.StatusUnauthorized) _, err := writer.Write(byte("You're Unauthorized due to invalid token")) if err != nil return
If the token is valid, you can pass in the endpoint handler with the
request parameters of the handler function for the middleware function to return the endpoint.
else statement for a case where there’s no token in the header of the client’s request:
else writer.WriteHeader(http.StatusUnauthorized) _, err := writer.Write(byte("You're Unauthorized due to No token in the header")) if err != nil return
Since you’re using middleware, the handler function in your route declaration will be the
verifyJWT middleware with the handler function for the route as the argument.
Once you’ve added your verification function to the route, the endpoint is authenticated.
On the client side, the client has to provide an issued token. Here’s a function that uses the
generateJWT function to add tokens in requests.
func authPage(writer http.ResponseWriter, ) token, err := generateJWT() if err != nil return client := &http.Client request, _ := http.NewRequest("POST", "<http://localhost:8080/>", nil) request.Header.Set("Token", token) _, _ = client.Do(request)
authPage function, the
token variable holds the token from the
generateJWT function. Using a reference to the
Client type of the
http package, you can create a new client and make a request to the endpoint. The
request variable is the request instance and — using the
Set method of the
header method of the request instance — you can set the token in the request header as shown above.
You can also choose to set the token as a cookie and retrieve it for verification whenever a client makes a request to the authenticated endpoint.
When you’re generating a JWT, you can choose to embed information in the token. In the
generateJWT function, you added the
username variable to the
Here’s how you can extract the claims, using the
username claims as an example. You can use middleware or add the functionality to your verification function when verifying the token signature.
func extractClaims(_ http.ResponseWriter, request *http.Request) (string, error) if request.Header["Token"] != nil tokenString := request.Header\["Token"\] token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface, error) if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok return nil, fmt.Errorf("there's an error with the signing method") return sampleSecretKey, nil ) if err != nil return "Error Parsing Token: ", err
extractClaims functions, the process is the same as the
verifyJWT function; you retrieved the token from the header, parsed the token, and verified the signature.
claims, ok := token.Claims.(jwt.MapClaims) if ok && token.Valid username := claims["username"].(string) return username, nil return "unable to extract claims", nil
On validating the token, you can retrieve the claims using the
Claims method and use the claims map to retrieve the data in the JWT, as shown above.
This tutorial taught you how to use JWT authentication to authenticate your API and web page endpoints in Go with JSON Web Tokens by using the
golang-jwt package. You can find the complete code in this tutorial as a Github Gist.
Remember to use environment variables for your secret keys and do not hide sensitive data in JWTs. There are many JWT tutorials on the LogRocket blog that you can check out to get started in the language or framework you’re interested in using!
LogRocket: Full visibility into your web and mobile apps
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
Try it for free.