AuthenticationMiddleware only knows how to extract Bearer <token> from the Authorization header. For anything else — an API key in X-Api-Key, a signed cookie, mTLS client cert — you have two options:
- Keep
AuthenticationMiddlewareand implement a customAuthenticatorInterfacethat is tolerant of your token format (fine if clients still sendAuthorization: Bearer <key>). - Replace
AuthenticationMiddlewarewith a custom middleware that pulls the credential from wherever it lives, calls your authenticator, and sets the identity on the request.
X-Api-Key header isn’t a bearer token.
1. Define an identity class
You can reuseJwtAuthIdentity if you don’t mind the misleading name. For clarity, define a dedicated one:
2. Implement the authenticator
Keep credential validation isolated from HTTP plumbing. The authenticator takes a raw string and returns an identity ornull.
3. Write the middleware
This replacesAuthenticationMiddleware (don’t add both; you’d double up on work).
- Passive, like
AuthenticationMiddleware. Invalid or missing keys don’t short-circuit — we letSecurityMiddlewaredecide based on the route’sRouteAuthorization. This keeps public routes public. - Header name variants.
ParameterBag::has()is case-sensitive. If your web server normalises headers you may only need one variant, but covering three common casings is cheap insurance.
4. Register it in place of AuthenticationMiddleware
5. Declare the security scheme for OpenAPI
Tell the OpenAPI generator that this API is secured via anapiKey in the X-Api-Key header. The name (ApiKeyAuth below) is what your routes reference.
6. Protect routes
Supporting both JWT and API keys
Run both middlewares. Whichever header the client sent populates the identity;SecurityMiddleware doesn’t care which authenticator was used, only whether the identity satisfies the route policy.
RouteAuthorization decides whether that’s allowed.
In your ComponentsObject, register both schemes (BearerAuth and ApiKeyAuth). Different routes can require different schemes; the SecuritySchemeObject.name passed to RouteAuthorization picks which one OpenAPI displays.