rswag to generate documentation with and without auth

247 Views Asked by At

I have an API that accepts a JWT bearer token. Expected results:

token description status message, if any
valid token 200
corrupt token 400 Your JWT appears to be invalid
no token 401 Did not receive auth token
valid but unauth 401 You can't do that

My thinking is that in the past, I have worked on APIs where I wasn't sure if my auth was even getting through... am I using the wrong key for this token? So I let the user know "I got your token, but it's not right."

Here's the issue: Using rswag, I can't figure out how to test both sending an auth token and NOT sending an auth token.

# frozen_string_literal: true

require 'rails_helper'

RSpec.configure do |config|
  # Specify a root folder where Swagger JSON files are generated
  # NOTE: If you're using the rswag-api to serve API descriptions, you'll need
  # to ensure that it's configured to serve Swagger from the same folder
  config.swagger_root = Rails.root.join('swagger').to_s

  # Define one or more Swagger documents and provide global metadata for each one
  # When you run the 'rswag:specs:swaggerize' rake task, the complete Swagger will
  # be generated at the provided relative path under swagger_root
  # By default, the operations defined in spec files are added to the first
  # document below. You can override this behavior by adding a swagger_doc tag to the
  # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json'
  config.swagger_docs = {
    'v1/swagger.yaml' => {
      openapi: '3.0.1',
      info: {
        title: 'My API',
        version: 'v1'
      },
      paths: {},
      servers: [
        {
          url: 'https://{defaultHost}',
          variables: {
            defaultHost: {
              default: 'www.example.com'
            }
          }
        }
      ],
      components: {
        securitySchemes: {
          AuthToken: {
            type: :http, scheme: :bearer, bearerFormat: JWT
          }
        }
      },
      security: [ { AuthToken: [] } ]
    }
  }

  # Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'.
  # The swagger_docs configuration option has the filename including format in
  # the key, this may want to be changed to avoid putting yaml in json files.
  # Defaults to json. Accepts ':json' and ':yaml'.
  config.swagger_format = :yaml
end

the test:

RSpec.describe 'api/v1/unicorns', type: :request do

  path '/unicorns.json' do

    let(:user) { create(:perceptyx_owner) }
    let(:account) { create(:account, owners: [user]) }

    get 'retrieves a list of accessible unicorns' do
      tags 'unicorns'
      produces 'application/vnd.api+json'
      security [ {AuthToken: [], NoAuth: []} ]
      response '200', 'properly authenticated request' do
        let(:Authorization) { JWT.encode({ auth: { user_id: user.pyx_user_id } }, nil, 'none') }
        run_test!
      end

      response '400', 'malformatted auth token' do
        let(:Authorization) { 'AAABBBCCCDDDDEEE-invalid' }
        run_test!
      end

      response '401', 'no authentication' do
        # it's not clear how you would run a test that did
        # not include a bearer token header -- this tests results
        # in "undefined method `Authorization'"
 
        run_test!
      end
    end
  end
end

I thought about using another securityScheme list here setting up a secondary authorization like ApiKeyAuth, which my app would ignore. But when I try to set security in the test, rather than as a default, it seems to apply to every test within the "path", not the individual "response".

I also tried setting "Authorization" to nil... but obviously that sent a key with no value. Which isn't the same thing.

Can this be done? How? I haven't gotten to the step of seeing this in the documentation yet but any clues about what I can do to set myself up for success there would be appreciated -- though not part of this question. :)

1

There are 1 best solutions below

0
On

Try setting Authorization to nil.

response '401', 'no authentication' do
  let(:Authorization) { }

  run_test!
end