| @@ -76,6 +76,19 @@ export const registrationScopes = ( · 75 unmodified lines |
| 76 | 76 | discoveredScopes: readonly string[], |
| 77 | 77 | ): readonly string[] => (declaredScopes.length > 0 ? declaredScopes : discoveredScopes); |
| 78 | 78 | |
| 79 | +/** The `originIntegration` to send with a `createClient` payload. When the |
| 80 | + * caller passes an explicit `intentIntegration` (edit flow: the app's |
| 81 | + * preserved recorded intent, which may legitimately be `null`), it wins |
| 82 | + * verbatim — editing must never silently overwrite a stamp. Otherwise this is |
| 83 | + * a fresh registration from an integration's dialog, which stamps the current |
| 84 | + * integration as recorded intent (or `null` outside any integration |
| 85 | + * context). */ |
| 86 | +export const resolveOriginIntegration = ( |
| 87 | + intentIntegration: IntegrationSlug | null | undefined, |
| 88 | + integrationSlug: IntegrationSlug | undefined, |
| 89 | +): IntegrationSlug | null => |
| 90 | + intentIntegration !== undefined ? intentIntegration : (integrationSlug ?? null); |
| 91 | + |
| 79 | 92 | export const canSubmitOAuthClientForm = (input: { |
| 80 | 93 | readonly submitting: boolean; |
| 81 | 94 | readonly name: string; |
| @@ -101,6 +114,13 @@ export function OAuthClientForm(props: { · 19 unmodified lines |
| 101 | 114 | * when editing (an existing app's origin is fixed) or when there is no single |
| 102 | 115 | * integration context. */ |
| 103 | 116 | readonly integrationSlug?: IntegrationSlug; |
| 117 | + /** Explicit recorded-intent stamp to send verbatim, overriding the default |
| 118 | + * derivation from `integrationSlug`. Set this when editing an existing app |
| 119 | + * so its ALREADY-recorded origin (which may be `null`) is preserved rather |
| 120 | + * than re-derived from the current dialog's integration context. Omit for a |
| 121 | + * fresh registration, where the default (stamp `integrationSlug`, or `null` |
| 122 | + * outside an integration context) is correct. */ |
| 123 | + readonly intentIntegration?: IntegrationSlug | null; |
| 104 | 124 | /** Existing client slugs, so the generated slug stays unique across apps. */ |
| 105 | 125 | readonly existingSlugs: readonly string[]; |
| 106 | 126 | /** Endpoints/scopes declared by the integration's OAuth method. */ |
| @@ -125,6 +145,7 @@ export function OAuthClientForm(props: { · 18 unmodified lines |
| 125 | 145 | const { |
| 126 | 146 | integrationName, |
| 127 | 147 | integrationSlug, |
| 148 | + intentIntegration, |
| 128 | 149 | existingSlugs, |
| 129 | 150 | prefill, |
| 130 | 151 | fixedSlug, |
| @@ -307,9 +328,10 @@ export function OAuthClientForm(props: { · 176 unmodified lines |
| 307 | 328 | clientId: clientId.trim(), |
| 308 | 329 | clientSecret: clientSecret.trim(), |
| 309 | 330 | resource, |
| 310 | | − // Editing keeps the app's original origin; only a fresh registration |
| 311 | | − // from an integration's dialog stamps recorded intent. |
| 312 | | − originIntegration: fixedSlug ? null : (integrationSlug ?? null), |
| 331 | + // Editing preserves the app's already-recorded origin (via |
| 332 | + // `intentIntegration`, passed verbatim by the caller); a fresh |
| 333 | + // registration from an integration's dialog stamps recorded intent. |
| 334 | + originIntegration: resolveOriginIntegration(intentIntegration, integrationSlug), |
| 313 | 335 | }, |
| 314 | 336 | reactivityKeys: oauthClientWriteKeys, |
| 315 | 337 | }); |