Using Axum Framework To Create REST API (Part II)
This is the Part II of the article `Using Axum Framework To Create Rest API`. In this article, we are going to learn how to protect routes/endpoints of API using JWT Token.
Note: To setup the project, please refer Part I of this article.
You can get the code for this article on GitHub.
The flow of authenticating endpoints
In Part I, we created /register
a route. Now we will create a/login
route to authenticate a user. This route creates and sends a JWT Token for authenticated registered users. The/user_profile
route is a protected route that can only be accessed by authenticated users.
Login route
First, add the below crates to the cargo.toml file
// Cargo.toml
[dependencies]
jsonwebtoken = "8.0"
once_cell = "1.8"
`once_cell` for define secret for the `jsonwebtoken`, in a production server you save it somewhere else and call it inside the server or set an environment variable for it.
Models
Add the below code to src/models/auth.rs
The user model should already be in your code if you followed part I. Keys model is used to store keys for encoding and decoding the JWT token, and `Claims` is used to store any information we want to store in the token, while `exp` expiry of the token in the timestamp.
The login handler
Modify file src/controllers/auth.rs
with the below code. There will be no changes in the register function. Add the login function and import the missing modules and structs.
For login, we are first checking if credentials are not empty, then fetching the user from the database with that credential, if the user exists then check if the password matches if it does then generate a token and send it as a response.
Now the code won’t run yet, since we have not created utils
module.
Utils module
First, create a file src/utils.rs
and add the below code to it.
The get_timestamp_8_hours_from_now
the function is used to get a timestamp of 8 hours from the current time for the token expiry.
In Axum, we can derive FromRequest
trait for any custom type to extract it from the request body from the handler, so after deriving FromRequest
for the `Claims` struct, we can extract it from handlers like
// extract Claims from request
pub async fn handler(claims: Claims) -> Json<serde_json::Value>{}
So it’s like middleware, whenever we extract Claims
in a handler, it will verify the token first, so it becomes protected.
Protected route
let’s create a protected route /user_profile
, this route fetches the user profile of an authenticated user.
Create a file src/controllers/user.rs
and the below code to it
and add the following line to src/controllers.rs
to add this file to the `controllers` module
// src/controllers.rs
pub mod user;
Here we are first fetching the Claims
model/struct, so we will get an error if we don’t pass the token in the request or if the token is not valid, if the token is valid we will get the email in a JSON as a response.
Testing
For testing, I will be using httpie command . You can also use curl or postman or others that work for you.
First, send a login request to get the JWT token
http localhost:3000/login email=abhinav14@gmail.com password=pass@123
you will get the token in the response if the credentials are correct.
Then use that token to access the /user_profile
route
http -A bearer -a eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImFiaGluYXYxNEBnbWFpbC5jb20iLCJleHAiOjE2NTk0NjE0NTV9.R1oNc1MS4xJZXcuyxaCcHpbPwDlsCcIsPuPF2kPXsXE localhost:3000/user_profile
Here I am setting the authentication type to bearer and passing the token. In response, I have set to return “email” as an example.
Debugging
You can compare your code with code on GitHub. Remember to set the DATABASE_URL env variable. Ensure that there is a table with ID, email, and password columns in the database.
Conclusion
If you have any questions, please feel free to reach out. Thanks for reading!