oapi-codegen icon indicating copy to clipboard operation
oapi-codegen copied to clipboard

Generate types from components/securitySchemes to be used as a key in context.WithValue

Open nek023 opened this issue 2 years ago • 2 comments

Summary

Generate types from components/securitySchemes to be used as a key in context.WithValue.

Background

For example, prepare the following API schema and configuration.

schemas.yaml

paths:
  /example:
    get:
      summary: Example
      operationId: example
      security:
        - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

config.yaml

package: schemas
generate:
  chi-server: true
  models: true
output: schemas.gen.go
output-options:
  skip-prune: true

If you run oapi-codegen, the following code is generated.

const (
	BearerAuthScopes = "BearerAuth.Scopes"
)

func (siw *ServerInterfaceWrapper) Example(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	ctx = context.WithValue(ctx, BearerAuthScopes, []string{})

	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		siw.Handler.Example(w, r)
	}))

	for _, middleware := range siw.HandlerMiddlewares {
		handler = middleware(handler)
	}

	handler.ServeHTTP(w, r.WithContext(ctx))
}

The constant BearerAuthScopes is generated as a key for context.WithValue, but its type is a string and there is a risk of collision between packages.

The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys.

https://pkg.go.dev/context#WithValue

If you use staticcheck, it will also warn this.

https://staticcheck.dev/docs/checks/#SA1029

This can be avoided by defining a type as shown below.

type bearerAuthContextKey string

const (
	BearerAuthScopes bearerAuthContextKey = "BearerAuth.Scopes"
)

In this PR, I have implemented to generate types from components/securitySchemes to be used as keys for context.WithValue, as shown above.

Considerations

While the implementation has been done to avoid breaking the existing code as much as possible, if users are directly using the underlying string (in this case, "BearerAuth.Scopes), they will no longer be able to extract values from the context.

However, this is considered to be a rare case.

type contextKey string

const exampleContextKey contextKey = "example"

func main() {
	ctx := context.Background()
	ctx = context.WithValue(ctx, exampleContextKey, "hello")

	val := ctx.Value(exampleContextKey)
	fmt.Println(val) // => hello

	val = ctx.Value("example")
	fmt.Println(val) // => nil
}

nek023 avatar Aug 08 '23 14:08 nek023

@jamietanna any updates on this? I would really love seeing this merged. Not like I'm the one who makes decision here, yet I think it's good to go.

GRbit avatar Dec 27 '24 12:12 GRbit

Will need to double check if https://github.com/oapi-codegen/oapi-codegen/issues/1200 and https://github.com/oapi-codegen/oapi-codegen/issues/836 are related here / can be closed after this

jamietanna avatar Apr 24 '25 08:04 jamietanna