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

Function decideDcrClientReuse

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

Source from the content-addressed store, hash-verified

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