Go auth with JWT
What will we be building?
In this article, we will try to build a simple Golang API for authorization and authentication. We will use JWT to store a user to reduce database calls and Echo framework to build routing. PostreSQL and pgx will be used to handle the database part.
Building
So, let’s start with designing the service API. We need an authentication route, which compare given login and password with ones in the database. If login and password match we will generate a JWT token for a user and store it in the cookies.
To authorize a user, we have to register one. So, we will also make a registration API.
Database
So, let’s create a user table in the database. We will use tern package to perform migration. Firstly, we have to specify a config file with DB credentials in the migration directory and call it tern.conf:
Secondly, we need to specify the user’s migration itself. Tern package can generate migration file for you. Use tern new create_users
command to generate file and fill it with user table creation code:
Finally, we can perform migration using command tern migrate
in the migration directory.
Now, we need to write some code that will let us connect to the DB and perform queries:
API
Let’s write the routing skeleton using Echo framework. We need to create new Echo instance and define an API group(with name /v1
for example). We also pass the group to InitAPI method which we will consider later.
Now, we can initialize the v1 api handlers, using Echo group that we passed to InitAPI
before. users.SignUp
and users.Auth
are controllers from the user package that we consider next:
SignUp
While registration, we need two additional functions. First is a function, that performs searching in the users
table by login. The function is used to validate uniqueness of the given login. Second is a function, to create a user in users
table by given parameters. We will define these functions in the user model later.
Let’s define the models package.
Auth
Auth controller try to find the user by given login and compare passwords if user will be found. If user was found and password matched, it will created JWT token and add it to cookies. Let’s add the controller’s code to the users package.
We have to add two additional functions in the models package. First is the models.VerifyPassword
function, that compare a given password with user’s hashed password. Second is the user.GetJWT
function, that can create JWT token for a user.
Now, a JWT token contains the user model and we can extract it from the token. Let’s write the middlewares, one that can get a user from a JWT token and another one that will check the token and return Not Authorized
error if it is invalid.
Now, we can get a user from the echo.Context
in next handlers with code like this user:= e.Get(”user”).(*models.UserClaims)
. For example, GetProfile
handler, that returns the dencoded user:
Improvements
You can generate two tokens. First one to store a user’s data and user’s authorization, this token must expire in a short time span. Second token is a refresh token, which you can use to generate new token pair. Refresh token lifetime must be longer, so a user will be able to refresh tokens and continue using your API. Authorization token’s lifetime is short due to the fact that the token is sent to a server along with each request and can be intercepted. So it needs to be refreshed regularly.