From c1fbc1261a02ac57b334fafe90c909c5387a4eb9 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 19 Oct 2026 12:38:13 +0200 Subject: [PATCH] OAuth start: connect keeps admitting identity providers, as Bifrost uses it The first gate refused connect for a provider outside managedConnectionProviders, which broke Bifrost's existing Discord connect. sign_in and link still take identityProviders only, so a managed-only provider (Joystick) can never sign anyone in or link; connect takes either list. Co-Authored-By: Claude Opus 5.5 --- docs/app-profiles/streampixels.md | 2 +- src/app-profiles.ts | 10 ++++++---- tests/app.test.ts | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/app-profiles/streampixels.md b/docs/app-profiles/streampixels.md index 9d1e23d..cf7b602 100644 --- a/docs/app-profiles/streampixels.md +++ b/docs/app-profiles/streampixels.md @@ -42,7 +42,7 @@ In the service-first Heimdall version, the most likely reusable slice is: - provider OAuth authority - identity-linking authority -- managed Twitch/YouTube/Joystick credential custody for creator connections (Joystick is a managed connection only, not a viewer sign-in provider: `startOAuthFlow` admits `sign_in` and `link` only for `identityProviders` and `connect` only for `managedConnectionProviders`, and refuses anything else before any redirect) +- managed Twitch/YouTube/Joystick credential custody for creator connections (Joystick is a managed connection only, not a viewer sign-in provider: `startOAuthFlow` admits `sign_in` and `link` only for `identityProviders` and `connect` only for `managedConnectionProviders` or `identityProviders`, and refuses anything else before any redirect) - entitlement refresh - shared claim semantics diff --git a/src/app-profiles.ts b/src/app-profiles.ts index 8ac57dd..128b27a 100644 --- a/src/app-profiles.ts +++ b/src/app-profiles.ts @@ -225,13 +225,15 @@ export function serializeAppProfile(profile: AppProfile): Record { + it("sign_in and link take identityProviders only; connect takes managedConnectionProviders or identityProviders, for every profile", async () => { const app = await buildApp({ config: createTestConfig() }); try { for (const profile of Object.values(builtInAppProfiles)) { @@ -2034,7 +2034,10 @@ describe("OAuth start admits a provider by the profile's role for the mode", () url: `/v1/oauth/${provider}/start`, payload: { appSlug: profile.slug, mode, returnTo: `${profile.allowedReturnOrigins[0]}/x` }, }); - const roleList = mode === "connect" ? profile.managedConnectionProviders : profile.identityProviders; + const roleList = + mode === "connect" + ? [...profile.managedConnectionProviders, ...profile.identityProviders] + : profile.identityProviders; const refused = start.json().error === "provider_not_supported_for_app"; expect([profile.slug, provider, mode, refused]).toEqual([profile.slug, provider, mode, !roleList.includes(provider)]); }