rswag/rswag

[BUG] Authorization Bearer header not passed to controller

xkraty opened this issue · 12 comments

xkraty commented

Describe the bug

My test fail becuase the Authorization header with the Bearer <token> is not passed to the controller unless i set the parameter manually.

Everything works fine in the Swagger UI.

Steps to Test or Reproduce

Spec file

# frozen_string_literal: true

require 'swagger_helper'

describe 'Token info' do
  path '/api/auth/tokens/info' do
    # rubocop:disable RSpec/VariableName
    get 'Info' do
      tags :tokens
      # Without the parameter the test fail
      # parameter name: :Authorization, in: :header, type: :string
      security [bearer: []]
      produces 'application/json'

      response '200', 'Success' do
        let('Authorization') { "Bearer #{create(:devise_api_token).access_token}" }

        run_test!
      end
    end
    # rubocop:enable RSpec/VariableName
  end
end

Generated swagger.yaml

---
openapi: 3.0.3
info:
  title: API V1
  version: v1
paths:
  "/api/auth/tokens/info":
    get:
      summary: Info
      tags:
      - tokens
      parameters:
      - name: Authorization
        in: header
        schema:
          type: string
      security:
      - bearer: []
      responses:
        '200':
          description: Success
          content:
            application/json:
              examples:
                Success:
                  value:
                    id: cdd5f039-3f61-4f41-a09f-2b9aa5de2c07
                    email: elene@boyer.example
- name: tokens
  description: Tokens generations
security:
- bearer: []
components:
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT

Expected behavior

Expect request.headers['Authorization'] to be set with Bearer so test would pass.

Additional context

I'm using devise with devise-api not sure this can be helpful.
I've been reading all the related issues I've found such us #74 #418 #377 but nothing seems to be fixing my problem.

Dependency versions

The version of are you using for:

  • Rswag: 2.9
  • RSpec: 6.0
  • Rails: 7.0.5
  • Ruby: 3.2.2

Relates to which version of OAS (OpenAPI Specification)

  • OAS2
  • OAS3
  • OAS3.1

Thanks in advance
xKraty

zachGl commented

Could you try to use in your swag_helper:
security: [ { bearerAuth: [] } ]

xkraty commented

Could you try to use in your swag_helper: security: [ { bearerAuth: [] } ]

Hello thanks for your answer,

you mean trying to rename bearer to bearerAuth, I've tried and it doesn't work, also my swagger output is correct, with that syntax, it's just failing the test

zachGl commented

The other difference between my code and your code is that I am using OpenAPI 3.0.1

That's my swag_helper:

config.swagger_docs = { 'v1/swagger.yaml' => { openapi: '3.0.1', info: { title: 'API V1', version: 'v1' }, paths: {}, servers: [ { url: 'http://{defaultHost}', variables: { defaultHost: { default: 'localhost:3000/api/v1' } } } ], components: { securitySchemes: { bearerAuth: { type: :http, scheme: :bearer, bearerFormat: JWT } } }, security: [ { bearerAuth: [] } ] } }

xkraty commented

Even with 3.0.1 test still fails, Swagger UI it would work fine

I'm also experiencing this bug - i'm not sure what's going on & tried the suggestions in #74 #418 #377 and none seem to work

@xkraty did you ever find a solution for this?

@dantonyblanco nope, I'm still passing the header params and explained to devs to ignore it into swagger

I think the parameter is the only thing that worked for me although it wasn't really intuitive intially.
Here is my test in case it helps anyone:

# frozen_string_literal: true

require "swagger_helper"

RSpec.describe "api/v1/onboardings", type: :request do
  before do
    post auth_login_index_path
    @token = response.parsed_body["token"]
  end
  path("/api/v1/onboardings") do
    let!(:Authorization) { "Bearer #{@token}" }
    get("list onboardings") do
      security [Bearer: []]
      tags "Onboardings"
      produces "application/json"
      parameter name: :status, in: :query, type: :string, enum: %w[active finished], required: false,
        description: "Filter onboardings by status"
      parameter name: :Authorization, in: :header, type: :string, required: true
      response(200, "successful") do
        let(:status) { "active" }
        schema type: :array,
          items: {
            type: :object,
            properties: {
              id: { type: :integer, example: 1 },
              description: { type: :string, nullable: true, example: "Onboarding Description" },
              end_date: { type: :string, format: "date-time", nullable: true, example: "2023-10-10T17:00:00Z" },
              learning: { type: :string, nullable: true, example: "Learning Content" },
              start_date: { type: :string, format: "date-time", example: "2023-10-05T09:00:00Z" },
              title: { type: :string, example: "Onboarding Title" },
              created_at: { type: :string, format: "date-time", example: "2023-10-04T20:48:46.929Z" },
              updated_at: { type: :string, format: "date-time", example: "2023-10-05T09:00:00Z" },
              progress: { type: :integer, example: 50 },
              status: { type: :string, enum: %w[active finished], example: "finished" },
              user_id: { type: :integer, example: 1 },
            },
          }
        run_test!
      end

      response(400, "bad request") do
        let(:status) { "invalid_status" }

        schema type: :object,
          properties: {
            error: {
              type: :string,
              example: "Invalid status parameter. Allowed values are active, finished",
            },
          }

        run_test!
      end
    end
  end
end

@dantonyblanco that's the workaround I applied but AFAIK you should not add it. I've tried to enable the dry run, and the documentation get generated correctly without it, problem is rspec fail without.

-     parameter name: :Authorization, in: :header, type: :string, required: true

For those who's experimenting this issue, have you defined correctly the securitySchemes within config.swagger_docs ?
This seems to be working on my project with this configuration:

{
  openapi: '3.0.1',
  ...,
  components: {
    securitySchemes: {
      bearer_auth: {
        type: :http,
        scheme: :bearer
      }
    }
  } ,
 ...
}

Then Use it with something like this:

get 'Returns the current logged in user' do
  tags 'Users'
  security [bearer_auth: []]

  response '200', 'user detail' do

    let(:Authorization) { "Bearer #{access_token.token}" }
    
    run_test!
  end
end

@Ksm125 do you have dry run disabled?

  RSpec.configure do |config|
    config.rswag_dry_run = false
  end
    config.rswag_dry_run = false

@xkraty well, i didn't add this line on my configuration, so i'm using the default configuration (which if i'm not mistaken true)
and i run this command when i want to generate the doc:

 RAILS_ENV=test RSWAG_DRY_RUN=0 rails rswag

here is my sample config

require 'rails_helper'
require_relative 'swagger_schema/version1'

RSpec.configure do |config|
  config.swagger_root = Rails.root.join('swagger').to_s

  config.swagger_strict_schema_validation = true

  config.swagger_docs = {...}

  config.swagger_format = :json
end