golang

Any web application that is targeted to more than one user is likely required to have some kind of authorization technique so that it can determine if given user can execute certain actions. The problem how to do this remains, despite dozens of years passed in software development world. One of the approaches, gaining much popularity recently are JSON Web Tokens. This short post shows how you can use them in your Golang application.

In general, most web applications implement quite a common pattern when it comes to authentication and authorization of their users. It all starts with a request with some login credentials (most common are a username-password pair), which is responded with some kind of key that is later used to assure that subsequent calls to the system are in fact carried out by the same client. You may generate some random hashes in response, but there are some things that can make your life easier with JWT.

JSON Web Token

The idea behind this is to generate a token of a specific structure so that you get more than an identifier of a user session. JSON Web Tokens consist of three elements: header, payload and signature, which are encrypted using chosen algorithm and separated with periods ("."). The first one determines the type of token (JWT) and an algorithm used for hashing everything. Signature, on the other hand, contain a passphrase that is used to de-cypher the contents. Fortunately, when using any 3rd party library we won't need to know much about those parts and can focus on the most important, business-wise, one - the payload.

The main part of the token stores all the information that is necessary for it to be useful. According to the documentation, this data is structured into a key-value map called claims. Some of them (their names) are reserved, and they keep meta-information, such as issuer, expiration time, subject, etc. You can also define your own, custom claims to keep anything you'll ever need to pass between two entities in your system. In authorization, this often contains some user profile information, such as their name, permissions list, etc.

JSON Web Tokens have two main advantages - their size and independence. First of all, as they are built using JSON, they are shorter (and therefore travel faster) than their XML-based equivalents. While this may not be that noticeable, the fact that JWTs are self-contained surely is. If you think about it, you don't need to query the database about user details every time a query comes in. All you need to do is check if the token is valid and hasn't expired yet, both of those actions you can do on the fly, without any DB queries.

Sample project

We'll take a look at some simple usage of JWT in Go application, using a library called jwt-go. We start by creating some UserProfile struct:

type UserProfile struct {
    Name        string   `json:"name"`
    Permissions []string `json:"permissions"`
}

Then we'll need to create some claims struct, which will be based on the standard ones, also provided by the library:

type UserClaims struct {
    Profile UserProfile `json:"profile"`
    jwt.StandardClaims
}

When the user logs in successfully, we can build a new token using their profile instance, signing algorithm (in our case HS256) and signing key:

var signingKey = []byte("signing-key")
...
claims := UserClaims{
    UserProfile{Name: "James Smith", Permissions: []string{"doStuff"}},
    jwt.StandardClaims{
        Issuer: "test-project",
    },
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(signingKey)
...

Reading the token is quite simple, as the library provides several utility functions for that. The most common pattern in web app development is to send your auth token as a part of HTTP Authorization header (Bearer <TOKEN>). To read the token in such a case, you can use ParseFromRequestWithClaims function:

parsing token from HTTP Header

token, err := jwtreq.ParseFromRequestWithClaims(req, jwtreq.AuthorizationHeaderExtractor, &claims, signingKeyFn)
    if err != nil {
        rw.WriteHeader(500)
        rw.Write([]byte("Failed to parse token"))
        log.Println("Failed to parse token")
        return
    }

Then we can easily check the validity of the token since it comes with Valid flag built-in:

    if !token.Valid {
        log.Println("Invalid token")
        ...
    }

Finally, we can get all the information from the token, eg. to check if given user has required permissions by accessing claims.Profile.Permissions.

The source code for this example is available on Github.