#auth #development #golang

Today, let's have a look at how you can use JWT Authentication in Go. We are going to use it in combination with Labstack Echo (my preferred tool for creating web servers with Go).

Let's define a simple webserver supporting JWT Authentication like this:

 1package main
 2
 3import (
 4    "net/http"
 5    "time"
 6
 7    "github.com/dgrijalva/jwt-go"
 8    "github.com/labstack/echo/v4"
 9    "github.com/labstack/echo/v4/middleware"
10    "github.com/pieterclaerhout/go-log"
11)
12
13const secret = "secret"
14
15type jwtCustomClaims struct {
16    Name  string `json:"name"`
17    UUID  string `json:"uuid"`
18    Admin bool   `json:"admin"`
19    jwt.StandardClaims
20}
21
22func login(c echo.Context) error {
23
24    username := c.FormValue("username")
25    password := c.FormValue("password")
26
27    if username != "pieter" || password != "claerhout" {
28        return echo.ErrUnauthorized
29    }
30
31    claims := &jwtCustomClaims{
32        Name:  "Pieter Claerhout",
33        UUID:  "9E98C454-C7AC-4330-B2EF-983765E00547",
34        Admin: true,
35        StandardClaims: jwt.StandardClaims{
36            ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
37        },
38    }
39
40    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
41
42    t, err := token.SignedString([]byte(secret))
43    if err != nil {
44        return err
45    }
46
47    return c.JSON(http.StatusOK, map[string]string{
48        "token": t,
49    })
50}
51
52func accessible(c echo.Context) error {
53    return c.String(http.StatusOK, "Accessible")
54}
55
56func restricted(c echo.Context) error {
57    user := c.Get("user").(*jwt.Token)
58    claims := user.Claims.(*jwtCustomClaims)
59    log.InfoDump(claims, "claims")
60    name := claims.Name
61    return c.String(http.StatusOK, "Welcome "+name+"!")
62}
63
64func main() {
65
66    e := echo.New()
67    e.HideBanner = true
68    e.HidePort = true
69
70    e.Use(middleware.Logger())
71    e.Use(middleware.Recover())
72
73    e.POST("/login", login)
74    e.GET("/", accessible)
75
76    r := e.Group("/restricted")
77    config := middleware.JWTConfig{
78        Claims:     &jwtCustomClaims{},
79        SigningKey: []byte("secret"),
80    }
81    r.Use(middleware.JWTWithConfig(config))
82    r.GET("", restricted)
83
84    e.Logger.Fatal(e.Start(":8080"))
85}

Looking at the code, there are several endpoints defined:

  • /login: can be used to get a JWT token based on your login credentials
  • /: an endpoint which doesn't require authentication
  • /restricted: an endpoint which does require a valid JWT authentication token

The server will register itself on port 8080 and we're ready to go.

You can start the server like:

$ go run server.go

You can then use the /login endpoint to get a token:

$ curl -s -X POST -d 'username=pieter' -d 'password=claerhout' http://localhost:8080/login
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiUGlldGVyIENsYWVyaG91dCIsInV1aWQiOiI5RTk4QzQ1NC1DN0FDLTQzMzAtQjJFRi05ODM3NjVFMDA1NDciLCJhZG1pbiI6dHJ1ZSwiZXhwIjoxNjEwMzgzNTU0fQ.JqJ4x2yPXOmxJB1n4GqpfKnIMyorX2zF7kbWbfchX4c"}

You can then use this token to access the restricted URL:

$ curl http://localhost:8080/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiUGlldGVyIENsYWVyaG91dCIsInV1aWQiOiI5RTk4QzQ1NC1DN0FDLTQzMzAtQjJFRi05ODM3NjVFMDA1NDciLCJhZG1pbiI6dHJ1ZSwiZXhwIjoxNjEwMzgzNTU0fQ.JqJ4x2yPXOmxJB1n4GqpfKnIMyorX2zF7kbWbfchX4c"
Welcome Pieter Claerhout!

In addition, the server log will show you the contents of the authentication token:

claims &main.jwtCustomClaims{
  Name: "Pieter Claerhout",
  UUID: "9E98C454-C7AC-4330-B2EF-983765E00547",
  Admin: true,
  StandardClaims: jwt.StandardClaims{
    Audience: "",
    ExpiresAt: 1610383647,
    Id: "",
    IssuedAt: 0,
    Issuer: "",
    NotBefore: 0,
    Subject: "",
  },
}

The complete working implementation can be found on GitHub.