Skip to content

fix(openapi3): don't emit auth scheme models under components.schemas#10677

Open
dschmidt wants to merge 3 commits into
microsoft:mainfrom
dschmidt:fix/openapi3-skip-auth-models-in-schemas
Open

fix(openapi3): don't emit auth scheme models under components.schemas#10677
dschmidt wants to merge 3 commits into
microsoft:mainfrom
dschmidt:fix/openapi3-skip-auth-models-in-schemas

Conversation

@dschmidt
Copy link
Copy Markdown

Issue

A custom auth scheme model declared inside the service namespace is emitted into both components.securitySchemes (correct) and components.schemas (spurious):

@useAuth(customBearer)
@service
namespace MyService;

model customBearer {
  type: AuthType.http;
  scheme: "bearer";
}

Emitted output today:

components:
  schemas:
    customBearer:                # <-- spurious
      type: object
      required: [type, scheme]
      properties: { ... }
  securitySchemes:
    customBearer:                # <-- correct
      type: http
      scheme: bearer

The spurious components.schemas.<name> entry comes from processUnreferencedSchemas, which walks every model in the service namespace and emits any model that is "unreachable" (i.e. not referenced from any payload). Custom auth scheme models always match that criterion but are already covered by components.securitySchemes.

Visible symptom (how I found this): @extension(...) decorators applied to the auth model propagate to both emit locations. Extensions valid under components.securitySchemes.<name> (for example bearerFormat: plain on a bearer auth scheme) are unknown under components.schemas.<name>, and downstream tools — openapi-generator-cli's spec validator in my case — reject the document:

- Error count: 1, Warning count: 4
Errors:
  -attribute components.schemas.customBearer.bearerFormat is unexpected

The bug only triggers when the auth model is declared inside the service namespace. Declared at global scope it doesn't reproduce, because processUnreferencedSchemas only walks the service namespace.

Fix

Filter auth scheme model types out of processUnreferencedSchemas. serviceAuth.schemes already carries each scheme's originating model (HttpAuth.model) by the time emitSchemas runs.

const authSchemeModels = new Set<Type>(serviceAuth.schemes.map((s) => s.model));
const addSchema = (type: Type) => {
  if (isOrExtendsHttpFile(program, type)) return;
  if (authSchemeModels.has(type)) return;  // skip auth scheme models
  if (visibilityUsage.isUnreachable(type) && !paramModels.has(type) && !shouldInline(program, type)) {
    callSchemaEmitter(type, Visibility.All);
  }
};

Auth models continue to be emitted via the separate securitySchemes path in initializeEmitter.

Test

Added a test case in packages/openapi3/test/security.test.ts that asserts res.components.schemas?.customBearer is undefined while res.components.securitySchemes.customBearer is set correctly. All 60 existing security tests still pass.

Backward compatibility

I think this should be safe: depending on auth scheme models to appear under components.schemas would be a misuse — they're not payload schemas. The change only affects the emit location, the model still parses and resolves identically inside @useAuth(...).

A custom auth scheme model declared inside the service namespace
(`model X { type: AuthType.http; scheme: "..."; }`) was emitted both
under `components.securitySchemes.X` (correct) and under
`components.schemas.X` (spurious). The second emit is the side effect
of `processUnreferencedSchemas`, which walks every model in the
service namespace and emits any whose visibility is "unreachable from
payloads". Custom auth models always match that criterion but are
already represented under `securitySchemes`.

The visible symptom was that any `@extension(...)` on the auth model
propagated to both emit locations. Extensions valid at
`components.securitySchemes.X` (e.g. `bearerFormat`) are unknown at
`components.schemas.X`, and downstream OpenAPI validators (such as
openapi-generator-cli) reject the document.

Filter the auth scheme models out of `processUnreferencedSchemas` so
they only land in `components.securitySchemes`.
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:openapi3 Issues for @typespec/openapi3 emitter label May 13, 2026
dschmidt added a commit to dschmidt/libre-graph-api that referenced this pull request May 13, 2026
…espace

Workaround for microsoft/typespec#10677: when auth scheme models are
declared inside the service namespace, the openapi3 emitter leaks them
into `components.schemas` (in addition to the correct
`components.securitySchemes`). Any `@extension` on the model
propagates to both copies, and downstream validators (openapi-generator-cli)
reject extensions like `bearerFormat: plain` at the `schemas` location.

Move the three schemes (basicAuth, bearerAuth, openId) into a new
`spec/auth.tsp` file that has no `namespace LibreGraph;` declaration,
so they live in the global namespace and `processUnreferencedSchemas`
in the openapi3 emitter doesn't visit them.

With the models at global namespace the `@extension("bearerFormat",
"plain")` line is back on `bearerAuth`, and the emitted YAML matches
the original libre-graph-api v1 spec on that detail (`bearerFormat:
plain` under `components.securitySchemes.bearerAuth`).

Re-merge the auth models back into main.tsp once microsoft/typespec#10677
ships.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 13, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/openapi3@10677

commit: 2c2c513

Comment thread packages/openapi3/test/security.test.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 13, 2026

All changed packages have been documented.

  • @typespec/openapi3
Show changes

@typespec/openapi3 - fix ✏️

Fix custom auth scheme models leaking into components.schemas when declared inside the service namespace. They are now emitted only under components.securitySchemes as expected.

@dschmidt dschmidt requested a review from timotheeguerin May 14, 2026 13:43
@timotheeguerin timotheeguerin enabled auto-merge May 14, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:openapi3 Issues for @typespec/openapi3 emitter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants