Adding API Support to your Masonite project is very simple. Masonite comes with supporting for returning JSON responses already but there are a few API features for authenticating and authorizing that are helpful.
Default projects don't come with these features so you'll have to simply register them.
First, register the ApiProvider in your project by adding the service provider to your PROVIDERS list:
This will register some required classes used for API development.
You should now create an api.py
file for adding your API routes to:
You will then have to add a binding to the container for the location of this file. You can do so in your Kernel.py
file inside the register_routes
method:
Any routes inside this file will be grouped inside an api middleware stack.
Next, you must choose a model you want to be responsible for authentication. This is typically your User model. You will have to inherit the AuthenticatesTokens
class onto this model:
This will allow the model to save tokens onto the table.
Next you will add a column to the models table for saving the token. Here we are naming it api_token
but this is configurable by adding a __TOKEN_COLUMN__
attribute to your model. Your migration file should look like this:
Then migrate your migrations by running:
Next, you can create a new API config file. You can do so simply by running the install command:
This will create a new api.py
config file in your configuration directory that looks like this:
This will attempt to import your user model but if you have a different model or if its in a different location you can change it in that model.
This command will also generate a secret key, you should store that secret key in an environment variable called JWT_SECRET
. This will be used as a salt for encoding and decoding the JWT token.
The authenticates
key is used as a check to check against the database on every request to see if the token is set on the user. By default, the database is not called to check if the token is assigned to a user. One of the benefits of JWT is the need to not have to make a database call to validate the user but if you want that behavior, you can set this option to True
You should add some routes to your web.py
file which can be used to authenticate users to give them JWT tokens:
The above parameters are the defaults already so if you don't want to change them then you don't have to specify them.
Since the routes in your api.py
file are wrapped in an api
middleware, you should add a middleware stack in your route middleware in your Kernel file:
This middleware will allow any routes set on this stack to be protected by JWT authorization.
By default, all routes in the
routes/api.py
file already have theapi
middleware stack on them so there is no need to specify the stack on all your API routes.
Once these steps are done, you may now create your API's
Once the setup is done, we may start building the API.
One of the ways to build an API is using controller and route resources.
A controller resource is a controller with several methods on them used to specify each action within an entity in the application (like users).
To create a controller resource you can run the controller command with an -a
flag:
This will create a controller with the following methods:
index
show
store
update
destroy
You can then create a route resource:
This will create the below routes which will match up with your API Controller methods:
The routes we added earlier contain 2 authentication methods. The /api/auth
route can be used to get a new authentication token:
First, send a POST
request with a username
and password
to get back a JWT token:
You should then send this token with either a token
input or a Authorization
header:
If you do not set a value for the expires
key in the configuration file then your JWT tokens will not expire and will be valid forever.
If you do set an expiration time in your configuration file then the JWT token will expire after that amount of minutes. If the token expires, you will need to reauthenticate to get a new token. This can be done easily by sending the old token to get back a new one:
You can do this by sending a POST request to /api/reauth
with a token
input containing the current JWT token. This will check the table for the token and if found, will generate a new token.
One of the issues with JWT tokens is there is little that can be done to invalidate JWT tokens. Once a JWT token is valid, it is typically valid forever.
One way to invalid JWT tokens, and force users to reauthenticate, is to specify a version. A JWT token authenticated will contain a version number if one exists. When the JWT token is validated, the version number in the token will check against the version number in the configuration file. If they do not match then the token is considered invalid and the user will have to reauthenticate to get back a new token.
Since we store the active api_token on the table we are able to retrieve the user using the LoadUserMiddleware
and a new guard
route middleware stack:
First add the guard middleware stack and add the LoadUserMiddleware
to the api
stack.
Lastly, in your route or route groups you can specify the guard middleware and specify the guard name:
Method | URL | Action | Route Name |
---|---|---|---|
GET
/users
index
users.show
GET
/users/@id
show
users.show
POST
/users
store
users.store
PUT/PATCH
/users/@id
update
users.update
DELETE
/users/@id
destroy
users.destroy