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

Function decideDcrClientReuse

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

Source from the content-addressed store, hash-verified

802 );
803
804 const decideDcrClientReuse = (
805 input: RegisterDynamicClientInput,
806 issuer: string | null,
807 flowRedirectUri: string | null,
808 ): Effect.Effect<
809 {
810 readonly existingSlug: OAuthClientSlug | null;
811 readonly registrationSlug: OAuthClientSlug;
812 },
813 StorageFailure
814 > =>
815 Effect.gen(function* () {
816 const candidates = yield* dcrCandidatesForIssuer(input.owner, issuer);
817 const resource = input.resource ?? null;
818 // A candidate is reusable only when the callback it registered with the
819 // AS still matches the current flow's callback — strict servers reject an
820 // authorize request whose redirect_uri differs from the registration
821 // (e.g. the callback origin changed after a sandbox was recreated while
822 // the persisted client survived). A null stored redirect is a legacy row
823 // predating the column: treated as matching so an upgrade doesn't
824 // re-register every client whose callback never changed. A null FLOW
825 // redirect has nothing to compare against, so it also reuses — the only
826 // alternative is a fresh registration, which the missing-redirectUri
827 // guard would fail.
828 const redirectMatches = (candidate: DcrReuseCandidate): boolean =>
829 candidate.redirectUri === null ||
830 flowRedirectUri === null ||
831 candidate.redirectUri === flowRedirectUri;
832 // A fresh registration must never take a slug an existing candidate
833 // holds: `createClient` deletes any colliding (owner, slug) row first,
834 // which would clobber a client that live connections still refresh
835 // through (a redirect-mismatched client stays valid for refresh — the
836 // token grant doesn't involve the redirect URI).
837 const takenSlugs = new Set(candidates.map((client) => String(client.slug)));
838 if (resource !== null) {
839 const matchingResource = candidates.find((client) => client.resource === resource);
840 if (matchingResource && redirectMatches(matchingResource)) {
841 return { existingSlug: matchingResource.slug, registrationSlug: matchingResource.slug };
842 }
843 const slug = uniqueDcrSlug(
844 dcrClientSlug(issuer, candidates.length > 0 ? resource : null, input.slug),
845 takenSlugs,
846 );
847 return {
848 existingSlug: null,
849 registrationSlug: slug,
850 };
851 }
852
853 // Resource-less request: only reuse a resource-LESS candidate. A client
854 // minted for a specific RFC 8707 resource must NOT be reused for a
855 // resource-less flow (its tokens are bound to that resource), so when only
856 // resource-scoped candidates exist we register a fresh resource-less client
857 // rather than silently borrowing one (the old `?? candidates[0]` bug).
858 const reusable = candidates.find(
859 (client) => client.resource === null && redirectMatches(client),
860 );
861 if (reusable) return { existingSlug: reusable.slug, registrationSlug: reusable.slug };

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