How to Build a JWT Auth Service with Go

Omer Goldberg
HackerNoon.com
6 min readJul 1, 2019

--

JSON Web Tokens (JWT) are a popular approach to ensure secure authentication in modern applications. A JWT allows us to securely transmit information between two parties as a JSON object. The information can be trusted because it can be digitally signed. JWTs can be signed in two ways.

  1. Using a secret with the HMAC algorithm- this is what we’ll do in this post 🤓🤓🤓.
  2. Public/Private key pair using RSA or ECDSA.

When should we use JWTs?

Authorization

The most popular scenario for using JWTis authorization. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access protected resources.

Information Exchange:

JWT is a good protocol for securely transmitting information between two parties, because it can be trusted due to its digitally signed nature.

But what exactly is a JWT?

Formally, RFC7519 defines a JWT as a safe means to represent claims to be transferred between two parties.

A JWT is an encoded, signed string with the following format:

The header will contain meta data about the type of token (JWT) as well as which algorithm we are using to sign it with.

From jwt.io

The payload will contain claims. Claims are statements about an entity and additional data. There are three types of claims (as defined on jwt.io): registered, public and private claims.

  • Registered claims: A set of predefined claims which are not mandatory but provide a useful set of interoperable claims. Some of them are: iss (issuer), exp (expiration time), sub (subject), aud(audience), and others.
  • Public claims: User defined — to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace.
  • Private claims: Custom claims created to share information between parties that agree on using them and are neither registered or public claims.

An example payload can be:

Here is an example on jwt.io of a header, payload and signature encoded/decoded.

Visualizing the flow

Before we jump into the code let’s visualize the flow of using an auth service for your application:

Let’s break down the flow of events:

  1. User signs in with credentials. Log in request is directed towards an Authentication service.
  2. If the log in attempt is successful the authentication service creates a signed JWT and returns it to the client.
  3. User makes subsequent requests to the application server with the JWT attached.
  4. JWT is verified for every request to the application server and then data is returned to client.

How are JWTs verified?

The authentication service and application server should share a secret in a secure manner before deploying the applications. Each request should have a JWT attached, usually in the Authorization header using the Bearer schema.

Once the request reaches the application server, the server can inspect the JWT, execute the signature step and check that the signature created matches the one it received. If it doesn’t, we know that the JWT is invalid.

Why do we trust JWTs?

A common misconception is that a JWT encrypts data. A JWT does not encrypt data, rather it proves that the sent data was created by an authenticated source. We know that it is authenticated because if we can verify the signature we know that the signer must have the secret (or public key when using the asymmetric approach). This paradigm of trust is valid as long as we know that the secret has not been compromised.

Building a prototype

Let’s build a working prototype so we can get a better feel for how this works in a real world environment. This will be quite generic so you’ll be able to apply this approach with any technology.

We will implement a server with 3 endpoints:

  1. signup-> when a client provides valid credentials, this endpoint will create an account for the user.
  2. login-> upon successful login (correct credentials) this will return a JWT to the client.
  3. protectedEndpoint -> an endpoint that can only be accessed with a valid JWT.

Let’s look at the main.go of our application:

line 22 -> connect to our db

line 23 -> define our mux router.

lines 27–30 -> define our endpoints + handlers

Let’s look at the implementation of our handlers.

signup

line 84 -> parse incoming signup request and extract credentials to user struct.

lines 86–97 -> verify that user sent necessary credentials

lines 98–107 -> create hash from user password

lines 117–120 -> stream response to client

login

line 25 -> parse user credentials from request

line 42 -> lookup user in db

line 45-> saved hashed pwd retrieved from db as hashedPassword

line 58 -> hash the password currently received from user and compare it to the hash we fetched from the db

line 66 -> generate token (we’ll talk about this soon)

line 75 -> write the JSON encoding to the output stream.

GenerateToken

The JWT will be created with the popular JWT library for go github.com/dgrijalva/jwt-go.

line 26 -> We grab the secret which would be set as an environment variable before deploying to production.

lines 28–33 -> create token with claims and sign with secret.

protectedEndpoint

This uses the protected endpoint and wraps it with the TokenVerifyMiddleware middleware.

lines 45–46 -> grab auth header from incoming request

line 51 -> parse and validate token. The jwt library is recreating the signature with the header, payload and application secret (we pass the secret as the first return value of the anonymous go function). This is explained thoroughly in the documentation.

line 66 -> if the token is valid this middleware proceeds to call the next function in line (in our case the protectedEndpoint handler function).

This full code can be found here. This repo and example was forked from https://github.com/codixir.

When should we use JWT?

Let’s compare JWT to Simple Web Tokens (SWT) and Security Assertion Markup Language Tokens (SAML).

JSON is less verbose than XML, and when it is encoded it is smaller and more compact than SAML.

SWT can only be signed using a symmetric approach. This requires a secure mechanism for secret sharing which requires significant overhead. Although we demonstrated a symmetric approach in our tutorial we can easily use an asymmetric approach when using JWT, simplifying the trust model and removing the vulnerable secret sharing process.

Conclusion

JWTs’ are an internet scale protocol used for authentication and are used behind the scenes in many applications in production today. Any other auth protocols you’d like to know about? Let me know in the comments!

If this post was helpful, please subscribe and click the clap 👏 button below to show your support! ⬇⬇

You can follow me on Instagram, Linkedin , and Medium for more tech related content!

--

--