Has anyone tried adding middleware layers to the code generated by the openapi generator cli tool?

TL;DR: I need an example from someone who knows the Rust/Swagger generated server code on how to write a middleware layer for authentication.

I've been working on a REST API built from an OpenApi spec (Swagger-file) with
Rust, and I want to implement some authentication with a middleware layer.

I have spec file that lists a number of services, that are all secured with an
API key. Whenever a service is called, I would like to peek at the HTTP request,
before running the actual service.

I want to validate the API key, and possibly do some back-channel server calls.
If the API key checks out, I want to run the actual service code, but if it doesn't,
I want to return a custom HTTP response.

The Rust generator uses a Hyper server, and generates some middleware in the form
of Tower services. I thought it would be a picnic to replace the "AllowAll"
middleware with one of my own making, but so far, I have had no success.

I think I understand how Tower services are supposed to work, but the ones generated
by the Swagger generator are a little more complex, to say the least, and they take
the form of a two step process, where one middleware generates another service and
wraps that, which then in turn wraps the preciously added middleware.

Long story short, I need some help from more experienced Rustaceans :slight_smile:

For the sake of simplicity, let's say I have a spec, middleware.yaml like this:

openapi: 3.0.3
info:
  title: Middleware Test
  version: 0.0.1
paths:
  /test:
    get:
      operationId: test
      responses:
        "200":
          description: Ok
      security:
        - api_key: []
components:
  securitySchemes:
    api_key:
      type: apiKey
      name: api_key
      in: header

I generate a server project using the openapi-generator-cli tool:

npx --yes @openapitools/openapi-generator-cli generate \
    --generate-alias-as-model \
    --api-package middleware \
    --package-name middleware \
    -g rust-server \
    -i middleware.yaml

This generates a library crate that I can then use in my own code. For the sake
of simplicity, we can look at the example server, generated by the tool
(in examples/server/server.rs). The interesting part is the function that
creates the Hyper server:

pub async fn create(addr: &str) {
    let addr = addr.parse().expect("Failed to parse bind address");

    let server = Server::new();
    let service = middleware::server::MakeService::new(server);
    let service = swagger::auth::MakeAllowAllAuthenticator::new(service, "cosmo");
    let service = middleware::server::context::MakeAddContext::<_, EmptyContext>::new(service);
    
    hyper::server::Server::bind(&addr).serve(service).await.unwrap()
}

It creates an implementation of the API (Server::new()), and wraps it in three services.

My problem is, that the combination of two-tiered wrappers (i.e. MakeAllowAllAuthenticator
is a wrapper for an internal AllowAllAuthenticator which in turns wraps the previous
MakeService that wraps a Service) and the Rust-Swagger concept of the context which is
not a plain struct but a set of trait bounds that hides a seemingly dynamic implementation.

This creates a very complex set of boundary requirements.

So, I'm hoping I'm not the only one using the Rust/Swagger generator, and that someone can
point me at an example of a middleware implementation, in the expected two service format
(MakeFoo/Foo) that inspects the raw HTTP body, and possibly returns an early result,
instead of proceeding with the chain.

Like I said, the short term purpose is to plug in my authentication code, but in the long term
I would like to clean up my code by moving some things I do in every service method into the
context that is built by the create method.

I also posted this question to SO, and I apologize for reposting it here.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.