Learn how to secure a Rails API with Auth0 by letting you drive by tests.
When you're adding authorization to an application, there are two crucial questions:
- Are users that shouldn't have access actually out?
- Are users that should have access actually in?
If you can't answer with certainty both questions, how can you claim to have a secure application? This is something that you can test manually, but a better alternative is test automation. Concretely, I think the best methodology to get there is using Test-Driven Development (TDD).
This piece is about adding authorization to a Ruby on Rails API by following TDD. You can check out this article to learn in detail how to secure a Rails API with Auth0. Also, you can follow this article along with this repository.
The TDD Cycle
In its essence, TDD is about a loop with the three steps shown in the following picture:
[[*Source: Thoughtworks*](https://www.thoughtworks.com/insights/blog/test-driven-development-best-thing-has-happened-software-design)]
- First, you make a test for a new feature. Initially, the test will fail.
- Then, you write the minimum amount of code that makes the test pass.
- Lastly, you refactor the code to make the implementation more solid.
Simple, isn't it? It creates a feedback loop where you write code incrementally to fulfill the task at hand. Moreover, it ensures that you build testability, i.e., you write your code so that it can be tested.
To show how to use TDD, let's add authorization to our application step by step, starting with tests. I will use OAuth to authorize requests to the API via Auth0. As I explained in my previous article, you can sign up for a free Auth0 account. Check it out to get all the details about setting up Auth0.
The Starting Application
Our journey starts with an API bootstrapped with Rails 6. It has three routes that need different levels of protection:
-
/api/messages/public
: Public route. -
/api/messages/protected
: Requires a valid access token. -
/api/messages/admin
: Requires a valid access token. Since Auth0 uses JWT as its access token format, we can inspect it and make sure it has apermissions
claim that contains the scoperead:admin-messages
.
This branch is the correct starting point. These are the baseline tests for the three routes:
# spec/api/messages_controller_spec.rb
require 'rails_helper'
describe Api::MessagesController, type: :controller do
describe '#public' do
subject { get :public, params: { format: :json } }
it 'returns an accepted answer for the public endpoint' do
subject
expect(response).to be_ok
message = 'The API doesn\'t require an access token to share this message.'
expect(json_response!).to include('message' => message)
end
end
describe '#protected' do
subject { get :protected, params: { format: :json } }
it 'returns an accepted answer for the protected endpoint' do
subject
expect(response).to be_ok
message = 'The API successfully validated your access token.'
expect(json_response!).to include('message' => message)
end
end
describe '#admin' do
subject { get :admin, params: { format: :json } }
it 'returns an accepted answer for the admin endpoint' do
subject
expect(response).to be_ok
message = 'The API successfully recognized you as an admin.'
expect(json_response!).to include('message' => message)
end
end
end
We're not enforcing authorization yet. The requests work, but that'll change soon enough.
To launch the tests, move in the project's root folder and run the following command:
./go test
The go
script allows you to execute different tasks, but we will use it here to run our tests.
For now, we have a pleasant list of green tests, as you can see in the following screenshot:
Top comments (0)