MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / decideDcrClientReuse

Function decideDcrClientReuse

packages/core/sdk/src/oauth-service.ts:1346–1416  ·  view source on GitHub ↗
(
    input: RegisterDynamicClientInput,
    issuer: string | null,
    flowRedirectUri: string | null,
  )

Source from the content-addressed store, hash-verified

1344 );
1345
1346 const decideDcrClientReuse = (
1347 input: RegisterDynamicClientInput,
1348 issuer: string | null,
1349 flowRedirectUri: string | null,
1350 ): Effect.Effect<
1351 {
1352 readonly existingSlug: OAuthClientSlug | null;
1353 readonly registrationSlug: OAuthClientSlug;
1354 },
1355 StorageFailure
1356 > =>
1357 Effect.gen(function* () {
1358 const candidates = yield* dcrCandidatesForIssuer(input.owner, issuer);
1359 const resource = input.resource ?? null;
1360 // A caller-supplied redirect is authoritative: only a client registered
1361 // with that exact callback can be reused. In particular, a legacy row
1362 // with no recorded redirect is not proof of a match. When the caller
1363 // relies on the executor's configured default, retain the legacy-null
1364 // compatibility behavior so upgrades do not re-register every client.
1365 const hasExplicitRedirectUri = input.redirectUri != null;
1366 const redirectMatches = (candidate: DcrReuseCandidate): boolean => {
1367 if (candidate.redirectUri === flowRedirectUri) return true;
1368 if (hasExplicitRedirectUri) return false;
1369 return candidate.redirectUri === null || flowRedirectUri === null;
1370 };
1371 // A fresh registration must never take a slug an existing candidate
1372 // holds: `createClient` deletes any colliding (owner, slug) row first,
1373 // which would clobber a client that live connections still refresh
1374 // through (a redirect-mismatched client stays valid for refresh — the
1375 // token grant doesn't involve the redirect URI).
1376 const takenSlugs = new Set(candidates.map((client) => String(client.slug)));
1377 if (resource !== null) {
1378 // Prefer a candidate matching resource AND the current redirect across
1379 // ALL candidates (mirroring the resource-less branch below). Candidates
1380 // are oldest-first, so after an origin drift the oldest matching-
1381 // resource row is the STRANDED one — but the first drift recovery
1382 // already minted a client bound to the CURRENT callback, and later
1383 // reconnects must reuse that instead of registering another duplicate
1384 // each time.
1385 const reusable = candidates.find(
1386 (client) => client.resource === resource && redirectMatches(client),
1387 );
1388 if (reusable) {
1389 return { existingSlug: reusable.slug, registrationSlug: reusable.slug };
1390 }
1391 const slug = uniqueDcrSlug(
1392 dcrClientSlug(issuer, candidates.length > 0 ? resource : null, input.slug),
1393 takenSlugs,
1394 );
1395 return {
1396 existingSlug: null,
1397 registrationSlug: slug,
1398 };
1399 }
1400
1401 // Resource-less request: only reuse a resource-LESS candidate. A client
1402 // minted for a specific RFC 8707 resource must NOT be reused for a
1403 // resource-less flow (its tokens are bound to that resource), so when only

Callers 1

registerDynamicClientFunction · 0.85

Calls 4

dcrCandidatesForIssuerFunction · 0.85
redirectMatchesFunction · 0.85
uniqueDcrSlugFunction · 0.85
dcrClientSlugFunction · 0.85

Tested by

no test coverage detected