OpenAPITools / OpenAPITools/openapi-generator

[BUG][Java][Spring] Incorrect generated code when use application/x-www-form-urlencoded and oneOf

Open
#14,967 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

I am designing an API following the oauth2 protocol. In it, the encoding to use is application/x-wwww-form-urlencoded.

Using that encoding algorithm and oneOf feature is not generating the code correctly.

openapi-generator version

6.4.0

OpenAPI declaration file content or url

Minimal sample

openapi: '3.0.0'
info:
  title: AuthServer OAuth2
  description: This API contains the public paths for the Auth Server
  version: 0.0.6
security:
  - basicOauthClientAuth: [ ]
paths:
  /oauth2/token:
    post:
      description: Generate OAuth2/OIDC tokens
      operationId: token
      security:
        - basicOauthClientAuth: [ ]
      tags:
        - OAuth2
      requestBody:
        description: OAuth2 RO basics grant types
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              oneOf:
                - $ref: '#/components/schemas/PasswordGrantTypeForm'
                - $ref: '#/components/schemas/AnonymousGrantTypeForm'
              discriminator:
                propertyName: grant_type
                mapping:
                  password: '#/components/schemas/PasswordGrantTypeForm'
                  anonymous: '#/components/schemas/AnonymousGrantTypeForm'
      responses:
        '200':
          description: OAuth2/OIDC standard response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessTokenResponse'
 
components:
  securitySchemes:
    basicOauthClientAuth:
      description: Base64 of the clientId and clientSecret, the format is clientId:clientSecret
      type: http
      scheme: basic
  schemas:
    GrantType:
      type: object
      required:
        - grant_type
      properties:
        grant_type:
          type: string
          description: Grant type to obtain the token.
          example: "password"

    PasswordGrantTypeForm:
      title: PasswordGrantTypeForm
      description: Request body for access token with password grant type for ecommerce customers
      allOf:
        - $ref: '#/components/schemas/GrantType'
        - type: object
          properties:
            scope:
              description: List of scope for the token separated by an space
              type: string
              example: "session openid"
            username:
              type: string
              description: Account id of the user
              example: "4b4d91f2-17b9-46af-b5de-008956ee6f65"
            password:
              type: string
              description: Password of the user
              example: "dummyP4ssw0rd"
          required:
            - username
            - password

    AnonymousGrantTypeForm:
      title: AnonymousGrantTypeForm
      description: Request body for access token with anonymous grant type for ecommerce customers
      allOf:
        - $ref: '#/components/schemas/GrantType'
        - type: object
          properties:
            scope:
              description: List of scope for the token separated by an space
              type: string
              example: "session openid"
            anonymous:parameters:account_id:
              type: string
              description: Account id of the user
              example: "4b4d91f2-17b9-46af-b5de-008956ee6f65"
          required:
            - anonymous:parameters:account_id

    AccessTokenResponse:
      type: object
      required:
        - token_type
      properties:
        token_type:
          type: string
        id_token:
          type: string
          description: If the request includes openid scope this field contains a JWT token    
Generation Details

java -jar openapi-generator-cli.jar generate -i spec.yml -g spring -o out --global-property skipFormModel=false

Steps to reproduce
  1. Generate classes
  2. See OauthApi.java.
Output
  1. It has been created endpoint with all parameters
  2. Parameters username and password are created as required --> incorrect
  3. Discriminator (grandType) should be required --> incorrect
/**
     * POST /oauth2/token
     * Generate OAuth2/OIDC tokens
     *
     * @param username Account id of the user (required)
     * @param password Password of the user (required)
     * @param grantType Grant type to obtain the token. (optional)
     * @param scope List of scope for the token separated by an space (optional)
     * @param anonymousColonParametersColonAccountId Account id of the user (optional)
     * @return OAuth2/OIDC standard response (status code 200)
     */
    @Operation(
        operationId = "token",
        description = "Generate OAuth2/OIDC tokens",
        tags = { "OAuth2" },
        responses = {
            @ApiResponse(responseCode = "200", description = "OAuth2/OIDC standard response", content = {
                @Content(mediaType = "application/json", schema = @Schema(implementation = AccessTokenResponse.class))
            })

        },
        security = {
            @SecurityRequirement(name = "basicOauthClientAuth")
        }
    )
    @RequestMapping(
        method = RequestMethod.POST,
        value = "/oauth2/token",
        produces = { "application/json" },
        consumes = { "application/x-www-form-urlencoded" }
    )
    default ResponseEntity<AccessTokenResponse> token(
        @Parameter(name = "username", description = "Account id of the user", required = true) @Valid @RequestParam(value = "username", required = true) String username,
        @Parameter(name = "password", description = "Password of the user", required = true) @Valid @RequestParam(value = "password", required = true) String password,
        @Parameter(name = "grant_type", description = "Grant type to obtain the token.") @Valid @RequestParam(value = "grant_type", required = false) String grantType,
        @Parameter(name = "scope", description = "List of scope for the token separated by an space") @Valid @RequestParam(value = "scope", required = false) String scope,
        @Parameter(name = "anonymous:parameters:account_id", description = "Account id of the user") @Valid @RequestParam(value = "anonymous:parameters:account_id", required = false) String anonymousColonParametersColonAccountId
    )
Expected

I was expecting the input parameters to be like when the encoding is application/json.

/**
     * POST /oauth2/token
     * Generate OAuth2/OIDC tokens
     *
     * @param tokenRequest OAuth2 RO basics grant types (required)
     * @return OAuth2/OIDC standard response (status code 200)
     */
    @Operation(
        operationId = "token",
        description = "Generate OAuth2/OIDC tokens",
        tags = { "OAuth2" },
        responses = {
            @ApiResponse(responseCode = "200", description = "OAuth2/OIDC standard response", content = {
                @Content(mediaType = "application/json", schema = @Schema(implementation = AccessTokenResponse.class))
            })
        },
        security = {
            @SecurityRequirement(name = "basicOauthClientAuth")
        }
    )
    @RequestMapping(
        method = RequestMethod.POST,
        value = "/oauth2/token",
        produces = { "application/json" },
        consumes = { "application/x-www-form-urlencoded" }
    )
    default ResponseEntity<AccessTokenResponse> token(
        @Parameter(name = "TokenRequest", description = "OAuth2 RO basics grant types", required = true) @Valid @RequestBody TokenRequest tokenRequest
    )

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Run the issue's OpenAPI Generator command with the provided spec and inspect the generated OauthApi.java. Trace how the spring generator handles application/x-www-form-urlencoded request bodies containing oneOf and discriminator schemas. Done means the generated endpoint accepts the form model as one required request body, matching the expected signature rather than exposing individual parameters.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.