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

Function decideDcrClientReuse

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

Source from the content-addressed store, hash-verified

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