Skip to content

Commit 171de20

Browse files
authored
Avoid unadvertised OAuth identity scopes (#1426)
1 parent 0c4e9b4 commit 171de20

3 files changed

Lines changed: 42 additions & 5 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@executor-js/plugin-openapi": patch
3+
---
4+
5+
Do not add unadvertised OpenID Connect identity scopes to OAuth authorization requests derived from OpenAPI specifications.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, expect, it } from "@effect/vitest";
2+
3+
import { resolvedOAuthScopes } from "./derive-auth";
4+
5+
describe("resolvedOAuthScopes", () => {
6+
it("does not synthesize OIDC scopes for a plain OAuth provider", () => {
7+
expect(resolvedOAuthScopes(["current_user:read", "files:read"], "auto")).toEqual([
8+
"current_user:read",
9+
"files:read",
10+
]);
11+
});
12+
13+
it("preserves advertised OIDC scopes in auto mode", () => {
14+
expect(resolvedOAuthScopes(["read", "openid", "profile"], "auto")).toEqual([
15+
"read",
16+
"openid",
17+
"profile",
18+
]);
19+
});
20+
21+
it("merges explicitly configured identity scopes", () => {
22+
expect(resolvedOAuthScopes(["read", "email"], ["openid", "email"])).toEqual([
23+
"read",
24+
"email",
25+
"openid",
26+
]);
27+
});
28+
});

packages/plugins/openapi/src/sdk/derive-auth.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,20 @@ const standardOidcIdentityScopes = ["openid", "email", "profile"] as const;
4141

4242
const identityScopesForPreset = (
4343
identityScopes: OAuth2Preset["identityScopes"],
44+
advertisedScopes: ReadonlySet<string>,
4445
): readonly string[] => {
4546
if (identityScopes === false) return [];
46-
return identityScopes === "auto" ? standardOidcIdentityScopes : identityScopes;
47+
return identityScopes === "auto"
48+
? standardOidcIdentityScopes.filter((scope) => advertisedScopes.has(scope))
49+
: identityScopes;
4750
};
4851

4952
export const resolvedOAuthScopes = (
5053
apiScopes: Iterable<string>,
5154
identityScopes: OAuth2Preset["identityScopes"],
5255
): string[] => {
5356
const merged = new Set(apiScopes);
54-
for (const scope of identityScopesForPreset(identityScopes)) merged.add(scope);
57+
for (const scope of identityScopesForPreset(identityScopes, merged)) merged.add(scope);
5558
return [...merged];
5659
};
5760

@@ -141,9 +144,10 @@ const oauthTemplateFromPreset = (
141144
// ---------------------------------------------------------------------------
142145
// All spec-detected auth methods → the union of stored `Authentication`
143146
// templates. Header presets become apiKey templates; each oauth2 preset becomes
144-
// an oauth template (with its declared API scopes plus, for auth-code flows,
145-
// the standard identity scopes). Slugs stay deterministic per method so the
146-
// stored template is stable across previews of the same spec.
147+
// an oauth template with its declared scopes. Auto identity detection only
148+
// preserves standard OIDC scopes the provider advertised; it never invents
149+
// scopes unsupported by a plain OAuth provider. Slugs stay deterministic per
150+
// method so the stored template is stable across previews of the same spec.
147151
// ---------------------------------------------------------------------------
148152

149153
export const detectedAuthenticationTemplates = (

0 commit comments

Comments
 (0)