git.iko.soy/rowtype-yoga/purescript-yoga-http-api
on feat/custom-type-errors help sign in
fix: Exclude BearerToken from regular header parameters in OpenAPI

Authorization headers with BearerToken should only appear in the security section of OpenAPI specs, not as regular header parameters. This follows the OpenAPI specification where security schemes are handled separately. - Add RenderHeadersSchemaRL instances to skip BearerToken headers - Skip BearerToken wrapped in metadata (Description, Example, Deprecated) - Add comprehensive tests verifying BearerToken exclusion from parameters - Verify BearerToken still appears in security requirements

commit7a05f462f519ae1abea2a379e498e58b4772f84d authorMark <mark.eibes@gmail.com> dateSat 7 Feb 2026, 22:29 (8 months ago) parent05db924
3 files changed+65−1
src/Yoga/HTTP/API/Route/OpenAPI.purs+17−1 test/BearerAuthTest.Spec.purs+46 test/Test.Operators.purs+2
src/Yoga/HTTP/API/Route/OpenAPI.purs+17−1
@@ -171,8 +171,24 @@ class RenderHeadersSchemaRL (rl :: RowList Type) (headers :: Row Type) | rl -> h
171171instance RenderHeadersSchemaRL RL.Nil () where
172172 renderHeadersSchemaRL _ = []
173173
174+-- Skip BearerToken headers (they're handled as security schemes, not regular headers)
175+instance (RenderHeadersSchemaRL tail tailRow, Row.Cons name BearerToken tailRow headers, Row.Lacks name tailRow) => RenderHeadersSchemaRL (RL.Cons name BearerToken tail) headers where
176+ renderHeadersSchemaRL _ = renderHeadersSchemaRL (Proxy :: Proxy tail)
177+
178+-- Skip BearerToken wrapped in Description
179+else instance (RenderHeadersSchemaRL tail tailRow, Row.Cons name (Description desc BearerToken) tailRow headers, Row.Lacks name tailRow) => RenderHeadersSchemaRL (RL.Cons name (Description desc BearerToken) tail) headers where
180+ renderHeadersSchemaRL _ = renderHeadersSchemaRL (Proxy :: Proxy tail)
181+
182+-- Skip BearerToken wrapped in Example
183+else instance (RenderHeadersSchemaRL tail tailRow, Row.Cons name (Example ex BearerToken) tailRow headers, Row.Lacks name tailRow) => RenderHeadersSchemaRL (RL.Cons name (Example ex BearerToken) tail) headers where
184+ renderHeadersSchemaRL _ = renderHeadersSchemaRL (Proxy :: Proxy tail)
185+
186+-- Skip BearerToken wrapped in Deprecated
187+else instance (RenderHeadersSchemaRL tail tailRow, Row.Cons name (Deprecated BearerToken) tailRow headers, Row.Lacks name tailRow) => RenderHeadersSchemaRL (RL.Cons name (Deprecated BearerToken) tail) headers where
188+ renderHeadersSchemaRL _ = renderHeadersSchemaRL (Proxy :: Proxy tail)
189+
174190-- Required header (non-Maybe type)
175−instance
191+else instance
176192 ( IsSymbol name
177193 , HeaderValueType ty
178194 , HasDescription ty
test/BearerAuthTest.Spec.pursnew file+46
@@ -0,0 +1,46 @@
1+module BearerAuthTest.Spec where
2+
3+import Prelude
4+
5+import Data.String as String
6+import Effect (Effect)
7+import Yoga.HTTP.API.Path (Path, Lit)
8+import Yoga.HTTP.API.Route (GET, Route, Request, BearerToken, buildOpenAPISpec)
9+import Yoga.JSON (writeJSON)
10+import ViTest (ViTest, describe, test)
11+import ViTest.Expect (expectToBe)
12+
13+--------------------------------------------------------------------------------
14+-- Test API with BearerToken
15+--------------------------------------------------------------------------------
16+
17+type AuthAPI =
18+ { getProtected :: Route GET (Path (Lit "protected")) (Request { headers :: { authorization :: BearerToken } }) (ok :: { body :: { message :: String } })
19+ }
20+
21+--------------------------------------------------------------------------------
22+-- Tests
23+--------------------------------------------------------------------------------
24+
25+testBearerTokenNotInHeaders :: Effect ViTest
26+testBearerTokenNotInHeaders = describe "BearerToken in OpenAPI" do
27+ _ <- test "BearerToken does not appear as a regular header parameter" do
28+ let spec = buildOpenAPISpec @AuthAPI { title: "Auth API", version: "1.0.0" }
29+ let json = writeJSON spec
30+ -- Authorization should NOT appear in parameters array
31+ let hasAuthParam = String.contains (String.Pattern "\"name\":\"authorization\"") json
32+ expectToBe false hasAuthParam
33+
34+ _ <- test "BearerToken appears in security requirements" do
35+ let spec = buildOpenAPISpec @AuthAPI { title: "Auth API", version: "1.0.0" }
36+ let json = writeJSON spec
37+ -- Should contain bearerAuth security reference
38+ let hasBearerAuth = String.contains (String.Pattern "bearerAuth") json
39+ expectToBe true hasBearerAuth
40+
41+ test "OpenAPI spec contains securitySchemes component" do
42+ let spec = buildOpenAPISpec @AuthAPI { title: "Auth API", version: "1.0.0" }
43+ let json = writeJSON spec
44+ -- Should have bearerAuth defined in components.securitySchemes
45+ let hasSecuritySchemes = String.contains (String.Pattern "securitySchemes") json
46+ expectToBe true hasSecuritySchemes
test/Test.Operators.purs+2
@@ -9,6 +9,7 @@ import VariantResponseTest.Spec as VariantResponseTest
99import MetadataValidationTest.Spec as MetadataValidationTest
1010import APIRecordTest.Spec as APIRecordTest
1111import SchemaComponentTest.Spec as SchemaComponentTest
12+import BearerAuthTest.Spec as BearerAuthTest
1213
1314spec :: Effect ViTest
1415spec = do
@@ -38,6 +39,7 @@ spec = do
3839 _ <- APIRecordTest.testOperationIds
3940 _ <- APIRecordTest.testBuildOpenAPISpec
4041 _ <- APIRecordTest.testAPIHandlers
42+ _ <- BearerAuthTest.testBearerTokenNotInHeaders
4143 SchemaComponentTest.testSchemaComponents
4244
4345main :: ViTest