Authenticate Go-GraphQL with JWT
Protect your API with JWT
When you develop a web application, of course, authentication and authorization are normal things to apply to your application; to limit the guest access to some content of your resource.
There are several ways of authentication and authorization of a web application, one of the most famous ones is JWT.
What is JWT?
JWT (JSON Web Token) is a media to securely transmitting information between parties using a JSON format. JWT can be signed with a secret key (HMAC Algorithm) or Public Key (RSA or ECDSA)
JWT Structure is split into 3 parts xxxxx.yyyyy.zzzzz
:
- Header
- Payload
- Signature
Header contains the algorithm used by the token, and ‘JWT’.
Payload contains the claims (information) about the data.
Signature contains the sign of hashed Header, Payload, secret, and algorithm, this part is used to verify the JSON is valid.
Requirement
- github.com/99designs/gqlgen
- github.com/dgrijalva/jwt-go
- gorm.io/gorm
- github.com/gorilla/mux
- github.com/google/uuid
- docker & docker-compose (database / optional)
And of course, some basics of go programming and GraphQL schema. So you guys can understand better what I do.
With gqlgen, we will generate our GraphQL server. For the database, we will use MySQL and connect to the database using GORM, mux for the HTTP Router, and jwt-go to generate and validate the JWT.
Repository: https://github.com/david-yappeter/go-graphql-jwt
Graphql Server
First of all, we will create the project directory and initialize go modules:
$ mkdir go-graphql-jwt && cd go-graphql-jwt && go mod init myapp# Init Graphql Server
$ go get github.com/99designs/gqlgen
$ go run github.com/99designs/gqlgen init
The project directory should look like this
and then we will create our User model in the schema.graphqls
:
to help you generate code faster, try to write this down in your graph/resolver.go
//go:generate go run github.com/99designs/gqlgen
then you will be able to generate the code by running go generate ./...
on your terminal.
After generating your code, there should be some leftover code that gives you an error, just delete that part.
And then we will move our User
model outside to avoid overwritten by the generator.
We will add Password
attribute to the model so GORM can insert automatically insert the password.
Add the gorm
tag to the model, this will help us with the table migration later on.
Database Configuration
The next step is to create our database connection, we will use GORM which is go ORM that helps us develop faster and maintaining code better.
config/database.go
Optionally we can add a database close on our server.go
MySQL Database with Docker & Docker-Compose
Add a docker-compose.yml
file on your root directory:
And run it by using this command:
docker-compose up -d
Don’t forget to add the .env
in the root directory for database configuration, example below:
JWT & User Service
We will write our JWT & user service on service
directory.
Jwt will have JwtGenerate
and JwtValidate
.
User will have UserCreate
,UserGetByID
, and UserGetByEmail
Auth will have UserRegister
and UserLogin
.
service/jwt.go
With jwt.NewWithClaims
it will create a token with our Custom Claim that we pass in the second parameter, in this example JwtCustomClaim
. After the token is created, we need to sign the token with our JWT_SECRET
value to make the token secure.
To validate the token, later on, we use JwtValidate
which take the token as a parameter and return *jwt.Token
as a return value.
How jwt.ParseWithClaims
callback works are, first, we check the token Signing Method, we use a secret HS256
to sign the token, so we check if the Signing method of the token is *jwt.SigningMethodHMAC
or not, then if it is a yes, we will return the secret, so the Jwt.ParseWithClaims
will return <nil> error if the ‘secret’ is valid.
We will take the JwtCustomClaim
value later on from *Jwt.Token
in the middleware section.
Before we move to User
service, we will create a password hasher tools first.
tools/bcrypt.go
Then we will create our User
service
service/user.go
Before we create our user, first we hash the password and then give random uuid
generated by github.com/google/uuid
.
Then move on to auth.go
service.
service/auth.go
In UserRegister
, we will find the user first. If the user is not found, we can create the account and return the JWT.
For UserLogin
, we will find the user. If the user is found, we return the JWT.
Then we will apply the function to the schema.resolvers.go
graph/schema.resolvers.go
Add Database Table Migration
We will create the migration on migration
directory.
migration/migration.go
And we need to apply it to the server.go
:
Authentication Middleware & Directives
For the middleware, we will create it on middlewares
directory.
middlewares/auth.go
AuthMiddleware
will be assigned to mux
router, later on, it will get the value of Authorization
from the header and validate the token and assign it to the request context
, later we can use the value by calling CtxValue
that get the CustomClaim from the context
.
After this, we will create Auth
the directive which will be applied on GraphQL protected resources, we will need to change our graph/schema.graphqls
graph/schema.graphqls
Then generate our resolvers again. ‘Protected’ will be added in schema.resolvers.go
Add a simple return “Success”, nil.
The last thing we need to do is, make the function and apply it to the directive
Apply it to the server.go
by copying this part
Our part for middleware and directives is done
Applying Middleware
We will create new mux
Router on server.go
router.Use(middlewares.AuthMiddleware)
will do the job for authentication and Auth
the directive will handle the authorization inside the GraphQL.
Testing
Run our code by using go run server.go
.
When we try to access the Protected
without JWT it will return us Access Denied
which we got from the directive.
So let's try to register users & log in.
Now I will try to add the token to Header Authorization
Now it doesn’t return any error.
This is the end of Go-GraphQL JWT article. Hope it helps you :).