Skip to content

formData will not validate schemas with oneOf  #267

@CameronGo

Description

@CameronGo

If you are using a content type of application/x-www-form-urlencoded you cannot validate a request whose schema uses oneOf. The result says that the schema does not match oneOf. I've tried moving the oneOf schemas around to diff locations and the result is always the same. Here is a stripped down example of the api doc being used.

openapi: 3.0.0
info:
  description: This is a sample of the API
  version: '{version}'
  title: sample API
tags:
  - name: authorization
    description: Create and validate authorization tokens using oauth
paths:
  '/oauth2/token':
    post:
      tags:
      - authorization
      security:
      - basicAuth: []
      - {}
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AccessTokenRequest'
            examples:
              ClientCredentialsTokenRequest:
                value:
                  grant_type: client_credentials
                  scope: 'member:read member:write'
              PasswordTokenRequest:
                value:
                  grant_type: password
                  client_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
                  scope: 'member:read member:write'                  
                  username: '[email protected]'
                  password: '2R.kNmv@(H'
              DeviceCodeTokenRequest:
                value:
                  grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
                  client_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
                  device_code: '1d5baab516044f26a43550a2dc7ea8ae'
              RefreshTokenRequest:
                value:
                  grant_type: refresh_token
                  client_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
                  refresh_token: '2fbd6ad96acc4fa99ef36a3e803b010b'
      responses:
        '200':
          description: 'The request was successful and a token was issued.'
          headers:
            Cache-Control:
              description: 'This header must always be returned to ensure proper handling of the token.'
              schema:
                type: string
                enum:
                - 'no-store'
                example: 'no-store'
            Pragma: 
              description: 'This header must always be returned to ensure proper handling of the token.'
              schema:
                type: string
                enum:
                - 'no-cache'
                example: 'no-cache'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessToken'
              examples:
                ClientCredentialsToken:
                  $ref: '#/components/examples/ClientCredentialsToken'
                UserPasswordToken:
                  $ref: '#/components/examples/UserPasswordToken'
                DeviceToken:
                  $ref: '#/components/examples/DeviceToken'
                RefreshToken:
                  $ref: '#/components/examples/RefreshToken'  
        '400':
          description: 'Bad request. The request submitted is formatted incorrectly or contains bad data. Fix the request and try again.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OauthError'
        '401':
          description: 'The supplied credentials are invalid or have expired. Please fix the credentials and try again.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OauthError'
              example:
                error: invalid_client
        '403':
          description: 'The authenticated client is not authorized to use this authorization grant type.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OauthError'
              example:
                error: unauthorized_client                
        '500':
          $ref: '#/components/responses/500' 
         
components:
  responses:
    '500':
      description: >-
        There was an unexpected error or failure. You should wait a moment and
        retry your request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMessage'
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic            
  schemas:
    ErrorMessage:
      description: >-
        The schema to which all error responses should conform with and
        explanation of the issue in the message attribute.
      type: object
      required:
        - message
      properties:
        message:
          type: string
          example: Error message
    OauthError:
      description: 'Please refer to oauth RFC on error responses: https://tools.ietf.org/html/rfc6749#section-5.2'
      type: object
      properties:
        error:
          type: string
          enum:
          - invalid_request
          - invalid_client
          - invalid_grant
          - unauthorized_client
          - unsupported_grant_type
          - invalid_scope
          - authorization_pending
          - access_denied
          - expired_token
        error_description:
          type: string
          example: the request did not specify a grant type
      example:
        error: 'invalid_request'
      required:
      - error

    AccessTokenRequest:
      description: 'Describes all of the potential access token requests that can be received'
      type: object
      oneOf:
      - $ref: '#/components/schemas/ClientCredentialsTokenRequest'
      - $ref: '#/components/schemas/PasswordTokenRequest'
      - $ref: '#/components/schemas/DeviceAccessTokenRequest'
      - $ref: '#/components/schemas/RefreshTokenRequest'
    ClientCredentialsTokenRequest:
      description: 'The client_id and client_secret properties should only be sent in form data if the client does not support basic authentication for sending client credentials.'
      properties:
        grant_type:
          type: string
          enum:
          - client_credentials
          example: 'client_credentials'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope'      
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id'
        client_secret:
          description: 'A secret code that would be setup for the client to exchange for an access token.'
          type: string
          example: 'fac663c0-e8b5-4c02-9ad3-ddbd1bbc6964'
      required:
      - grant_type
      - scope
    PasswordTokenRequest:
      type: object
      properties:
        grant_type:
          type: string
          enum:
          - password
          example: 'password'
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id'
        username:
          description: 'The username for the user account on whose behalf the token will be used.'
          type: string
          example: '[email protected]'
        password:
          description: 'The password for the user account on whose behalf the token will be used.'
          type: string
          format: password
          example: '2R.kNmv@(H'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope'          
      required:
      - grant_type
      - client_id
      - username
      - password
      - scope
    DeviceAccessTokenRequest:
      type: object
      properties:
        grant_type:
          type: string
          enum:
          - urn:ietf:params:oauth:grant-type:device_code
          example: urn:ietf:params:oauth:grant-type:device_code
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id'
        device_code:
          description: 'This is a long string that the device will use to eventually exchange for an access token'
          type: string
          minLength: 32
          example: '1d5baab516044f26a43550a2dc7ea8ae'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope'          
      required:
      - grant_type
      - client_id
      - device_code
      - scope
    RefreshTokenRequest:
      type: object
      properties:
        grant_type:
          type: string
          enum:
          - refresh_token
          example: 'refresh_token'
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id'
        refresh_token:
          $ref: '#/components/schemas/AccessToken/properties/refresh_token'
      required:
      - grant_type
      - client_id
      - refresh_token
    AccessToken:
      type: object
      properties:
        access_token:
          description: 'The token which should be passed in the authorization header to gain access to the resource server'
          type: string
          minLength: 32
          example: '8551c99d47f847bebe718e483bfb5c65'
        token_type:
          description: 'All tokens for oauth will be of type Bearer.'
          type: string
          enum:
          - Bearer
          example: Bearer
        expires_in:
          description: 'The number of seconds in which the access token expires'
          type: integer
          example: 3600
        scope:
          description: 'A space separated list of scopes requested for the token'
          type: string
          example: 'member:read member:write'
        client_id:
          description: 'The ID provided when the client application was registered'
          type: string
          example: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
        refresh_token:
          description: 'A long lived one time use token that is issued only in cases where the client can be offline or restarted and where the authorization should persist.'
          type: string
          minLength: 32
          example: '2fbd6ad96acc4fa99ef36a3e803b010b'
        user_id:
          description: 'Identifies the UUID of the account in the case of a token issued for user authentication. Will not be used in client credentials or device grant types. This property is being deprecated and the client application should not expect it to be present.'
          deprecated: true
          type: string
          format: uuid
          example: ''
      required:
      - access_token
      - token_type
      - scope
      - expires_in
      - client_id
  examples:
    ClientCredentialsToken:
      value:
        access_token:
          $ref: '#/components/schemas/AccessToken/properties/access_token/example'
        token_type:
          $ref: '#/components/schemas/AccessToken/properties/token_type/example'
        expires_in:
          $ref: '#/components/schemas/AccessToken/properties/expires_in/example'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope/example'
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id/example'
    UserPasswordToken:
      value:
        access_token:
          $ref: '#/components/schemas/AccessToken/properties/access_token/example'
        token_type:
          $ref: '#/components/schemas/AccessToken/properties/token_type/example'
        expires_in:
          $ref: '#/components/schemas/AccessToken/properties/expires_in/example'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope/example'
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id/example'
        user_id:
          $ref: '#/components/schemas/AccessToken/properties/user_id/example'
    DeviceToken:
      value:
        access_token:
          $ref: '#/components/schemas/AccessToken/properties/access_token/example'
        token_type:
          $ref: '#/components/schemas/AccessToken/properties/token_type/example'
        expires_in:
          $ref: '#/components/schemas/AccessToken/properties/expires_in/example'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope/example'
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id/example'
        refresh_token:
          $ref: '#/components/schemas/AccessToken/properties/refresh_token/example'
    RefreshToken:
      value:
        access_token:
          $ref: '#/components/schemas/AccessToken/properties/access_token/example'
        token_type:
          $ref: '#/components/schemas/AccessToken/properties/token_type/example'
        expires_in:
          $ref: '#/components/schemas/AccessToken/properties/expires_in/example'
        scope:
          $ref: '#/components/schemas/AccessToken/properties/scope/example'
        client_id:
          $ref: '#/components/schemas/AccessToken/properties/client_id/example'
        refresh_token:
          $ref: '#/components/schemas/AccessToken/properties/refresh_token/example'          

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions