Photo by Markus Spiske on Unsplash

Document and test your authenticated API

Elson Akio Otake

--

Video showing the steps involved in creating authenticated API documentation

In my previous articles, I showed how to document and test API and how to add authentication to Rails API applications. I show how to create and test documentation with authenticated API in this article.

I will use the structure created in previous articles in this new article and invite you to reproduce the steps in your environment. For those without the source code, I suggest following the steps in the previous articles or cloning this repository.

We will follow these steps:

  • Create the documentation for login using email and password;
  • Setup the documentation to handle authentications;
  • Test the endpoints in the documentation.

Create documentation for login using email and password

In the previous article, we added the password field in the user’s table. Include the password in the user endpoints documentation. Update the spec/integration/users_spec.rb.

require 'swagger_helper'

describe 'Users' do
path '/api/v1/users' do
get 'List users' do
tags 'Users'
description 'List all users'
produces 'application/json'

response '200', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
password_digest: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end
end
end

path '/api/v1/users' do
post 'Create a user' do
tags 'Users'
consumes 'application/json'
produces 'application/json'

parameter name: :user,
in: :body,
description: 'Create a user',
schema: {
type: :object,
properties: {
email: { type: :string },
password: { type: :string },
description: { type: :string }
},
required: %w[email password]
}

response '201', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
password_digest: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end

response '422', 'Unprocessable entity' do
run_test!
end
end
end

path '/api/v1/users/{id}' do
get 'Show a user' do
tags 'Users'
description 'Show a user'
produces 'application/json'

parameter name: :id,
in: :path,
type: :integer,
required: true,
description: 'User identification'

response '200', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
password_digest: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end

response '401', 'Unauthorized' do
run_test!
end

response '404', 'Not Found' do
run_test!
end
end
end

path '/api/v1/users/{id}' do
put 'Updates a user' do
tags 'Users'
description 'Updates a user'
consumes 'application/json'
produces 'application/json'

parameter name: :id,
in: :path,
type: :integer,
required: true,
description: 'User identification'

parameter name: :user,
in: :body,
description: 'Updates a user',
schema: {
type: :object,
properties: {
email: { type: :string },
password: { type: :string },
description: { type: :string }
}
}
response '200', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
password_digest: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end

response '401', 'Unauthorized' do
run_test!
end

response '404', 'Not Found' do
run_test!
end

response '422', 'Unprocessable entity' do
run_test!
end
end
end

path '/api/v1/users/{id}' do
delete 'Delete a user' do
tags 'Users'
description 'Delete a user'
produces 'application/json'

parameter name: :id,
in: :path,
type: :integer,
required: true,
description: 'User identification'

response '204', 'OK' do
run_test!
end

response '404', 'Not Found' do
run_test!
end
end
end
end

Let’s document the endpoint for the user to log in to the application. We create the /auth/login endpoint in the authentication article. Create the spec/integration/auth_login_spec.rb file with the following content.

require 'swagger_helper'

describe 'Auth login' do
path '/api/v1/auth/login' do
post 'Valid user login' do
tags 'Login'
consumes 'application/json'
parameter name: :user,
in: :body,
description: 'Valid user login',
schema: {
type: :object,
properties: {
email: { type: :string },
password: { type: :string }
},
required: %w[email password]
}

response '200', 'OK' do
schema type: :object,
properties: {
token: { type: :string },
exp: { type: :string },
email: { type: :string },
id: { type: :integer }
}
run_test!
end

response '401', 'Unauthorized' do
run_test!
end
end
end
end

Setup documentation to handle authentications

The authentications can use Basic authentication, Bearer, Token, OAuth 2, or OpenID Connect Discovery. In this article, I will deal with authentication using a Token. The token authentication can use headers, query strings, or cookies. I will show how to document the use of token authentication via header. If you are interested in other types of authentication, check the rswag documentation.

Add the content below to the spec/swagger_helper.rb file.

servers: [
{
url: 'http://localhost:3000/',
variables: {
defaultHost: {
default: 'localhost:3000/'
}
}
}
],
components: {
securitySchemes: {
ApiKeyAuth: {
type: 'apiKey',
in: 'header',
name: 'Authorization'
}
}
}

The name ApiKeyAuth is arbitrary in this security scheme. You can set your preferred name. We chose the security type using a token (apiKey). We also chose the header (header) for token authentication through the Authorization (Authorization) field.

Use the command below to secure documentation endpoints.

security [{ ApiKeyAuth: [] }]

The empty array after ApiKeyAuth is the list of required security scopes for API calls. Only OAuth 2 and OpenID Connect Discovery require security scopes.

The show, update, and delete are protected endpoints defined in the User Controller. Add this command to these endpoints. Update the spec/integration/users_spec.rb.

...

path '/api/v1/users/{id}' do
get 'Show a user' do
security [{ ApiKeyAuth: [] }]
tags 'Users'

...

path '/api/v1/users/{id}' do
put 'Updates a user' do
security [{ ApiKeyAuth: [] }]
tags 'Users'

...

path '/api/v1/users/{id}' do
delete 'Delete a user' do
security [{ ApiKeyAuth: [] }]
tags 'Users'

...

We have completed the documentation content. Generate the documentation and check the new security padlocks.

rake rswag

Start the server.

rails server

In your browser, open http://localhost:3000/api-docs.

The button to fill in the token and the signs of protected endpoints are on the right side. The open padlock icon means missing validation. Endpoints without a padlock do not require authentication.

Test the endpoints in the documentation

Create a new user.

POST /api/v1/users Create a user

Change the password and description.

PUT /api/v1/users/{id} Updates a user

The result is an unauthorized user. Authentication is needed to use a protected endpoint. Authenticate the user using the login endpoint.

POST /api/v1/auth/login Valid user log in

Successful login generates a token for the user. Copy the token. Return to the main page, click the authorize button, and paste the copied token.

Click authorize. If authorized, you can use all protected endpoints.

Close the window to continue your tests, or log out to end the session.

The padlock icons change state when authentication is successful.

Try again to change the password and description.

PUT /api/v1/users/{id} Updates a user

After the authentication, the change is successful. Test the other endpoints to show and delete users.

If you liked the article, share it.

Suggestions for improvements are always welcome.

This code is available in my repository.

--

--