Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Required HTTP headers set via huma.Param are neither enforced nor validated #495

Open
johker01 opened this issue Jul 2, 2024 · 1 comment
Labels
question Further information is requested

Comments

@johker01
Copy link

johker01 commented Jul 2, 2024

Summary

My goal is to version an API by setting a mandatory API-Version HTTP header which is required for each path the API offers. In order to do that, I set the header globally in config.Components and reference it in a specific path. However, when doing that I'm still able to call the path without the header - and if it's set, it's not being validated.
When setting the header during huma.Register in the input struct, the required HTTP header is enforced and validated.

Is there a way to fix this behavior, i.e. specify a header globally, then reference it in a path and when calling the path without the header (or with a value which doesn't adhere to the defined schema) it results in HTTP 422?

Details

I created a Go Playground which reproduces the problem I described above: https://go.dev/play/p/UbjH1SPPn_2

Notable steps in the repro case

Globally define a required header with schema information (for validation) which can be referenced in individual paths:

config.Components = &huma.Components{
		Headers: map[string]*huma.Param{
			"APIVersion": {
				In:          "header",
				Name:        "API-Version",
				Required:    true,
				Description: "Pass the current date as a version date during development and a fixed value for testing and production release. Format: YYYY-MM-DD.",
				Example:     "2024-07-01",
				Schema: &huma.Schema{
					Type:   "string",
					Format: "date",
				},
			},
		},
	}

Reference the header in a path definition:

huma.Register(api, huma.Operation{
...
		Parameters: []*huma.Param{
			{
				// This should enforce the `API-Version` header and make sure that it's being validated, but it doesn't work
				Ref: "#/components/headers/APIVersion",
			},
		},

This step doesn't work. The referenced header is correctly documented in /openapi, however, it is not enforced, nor validated - as specified in the global definition of the header:
image

Setting the header in the input struct during huma.Register works:

huma.Register(api, huma.Operation{
...
	}, func(ctx context.Context, input *struct {
		// This works, i.e. required header is enforced and validated
		APIVersion string `header:"API-Version" required:"true" format:"date"`
	}) (*DemoResponse, error) {

OpenAPI 3.1 document

components:
  headers:
    APIVersion:
      description: "Pass the current date as a version date during development and a fixed value for testing and production release. Format: YYYY-MM-DD."
      example: "2024-07-01"
      in: header
      name: API-Version
      required: true
      schema:
        format: date
        type: string
  schemas:
    DemoResponseBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/DemoResponseBody.json
          format: uri
          readOnly: true
          type: string
        message:
          type: string
      required:
        - message
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
    ErrorModel:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ErrorModel.json
          format: uri
          readOnly: true
          type: string
        detail:
          description: A human-readable explanation specific to this occurrence of the problem.
          examples:
            - Property foo is required but is missing.
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: "#/components/schemas/ErrorDetail"
          type: array
        instance:
          description: A URI reference that identifies the specific occurrence of the problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: A short, human-readable summary of the problem type. This value should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
      type: object
info:
  title: Demo
  version: 1.0.0
openapi: 3.1.0
paths:
  /demo:
    get:
      description: Prints a hello world message
      operationId: demo
      parameters:
        - $ref: "#/components/headers/APIVersion"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DemoResponseBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Hello, world!
      tags:
        - Demo
  /demo2:
    get:
      description: Prints a hello world message
      operationId: demo2
      parameters:
        - in: header
          name: API-Version
          required: true
          schema:
            format: date
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DemoResponseBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Hello, world!
      tags:
        - Demo
@danielgtaylor
Copy link
Owner

@johker01 ah, params which use $ref are not resolved or processed by Huma. You would need to do your own validation wherever you plan to access the value. Manually adding these into the OpenAPI is a way for you to document them to your user, but Huma assumes you are managing everything yourself.

Open to ideas on how this should work differently, but in general if Huma isn't loading a value into a struct/field it won't handle validation for it.

@danielgtaylor danielgtaylor added the question Further information is requested label Jul 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants