DEV Community

WadeSterling3125
WadeSterling3125

Posted on

Provider Discovery and Identity Resolution for Safer Marketplace Contributor Sign-In

A marketplace that accepts community contributions should use provider discovery to route a contributor to an allowed sign-in method, then use identity resolution to attach the verified login to one internal contributor record. Do not merge accounts from an email string alone. The proof should come from a completed authentication flow, and account linking should require control of both identities.

Choice Abuse resistance Contributor friction Operational load Best fit
Provider discovery plus explicit identity resolution Strong when discovery is non-enumerating and linking requires fresh proof Low after the first link Medium A marketplace with returning contributors and several permitted sign-in methods
Email and password only Easier to reason about, but every password and recovery flow stays in scope Familiar, with another password to keep Low to medium A small community with one account source
Manual identity review High confidence for exceptional cases High High Maintainer, payout, or disputed-account recovery

Decision: use discovery and resolution for routine contributor access, keep email-and-password sign-in available, and reserve manual review for recovery or privileged changes. This spends engineering time on the boundary where abuse matters instead of making every contributor wait for a maintainer.

It also matches a one-person SaaS constraint: ship weekly, outsource undifferentiated authentication work where possible, and measure custom logic by revenue per engineering hour. The custom part worth owning is the identity policy. That policy determines who can publish, edit a listing, or inherit an existing contributor history.

How should provider discovery and identity resolution protect contributor sign-in?

Provider discovery answers a narrow question: which sign-in path may this browser attempt? Identity resolution answers the harder one: which local contributor does a successfully authenticated identity represent? Combining the two can reduce duplicate accounts without treating a typed email address as proof.

Discovery must not become an account directory. If one response says an address exists and another says it does not, an attacker can test marketplace contributors one address at a time. OWASP recommends generic authentication responses so the visible result does not reveal whether an account exists, is disabled, or used the wrong credential. Apply that idea to discovery too: keep status codes, response shapes, and user-facing timing as uniform as practical. A valid next step can still be returned, but it should not confirm the state of a local contributor record.

Keep it boring.

Resolution begins only after authentication succeeds. Store a stable internal contributor ID separately from each login identity, and map an authenticated issuer-and-subject pair to that ID. A verified email can help suggest that two identities may belong to the same person, but it is not sufficient authorization to merge them. Addresses get reassigned, domains change hands, and different identity systems do not promise that equal-looking profile fields describe the same account. The safe merge path asks the contributor to authenticate the existing identity and the new identity in fresh sessions, then records the link as a security event.

This split creates one useful invariant: discovery chooses a route; only verified authentication and explicit linking change ownership.

The two criteria that decide the architecture

The first criterion is resistance to enumeration and automated abuse. Generic errors matter, but they are only one layer. OWASP also calls for login throttling and notes that counters should be associated with the account rather than only the source IP. That distinction matters for a contributor marketplace because distributed attempts can rotate addresses while targeting the same account. Use layered controls: account-aware throttling, cautious IP and device signals, monitoring, and stronger verification when risk rises. A hard global rule can punish a whole office or conference network, so risk signals should inform the decision rather than act as identity proof.

The second criterion is resolution integrity. Ask what evidence can create, link, unlink, or recover an identity. Each transition needs an explicit rule. Creating a new contributor after a verified sign-in is different from attaching a new login to a contributor who already controls packages, reviews, or payouts. The latter deserves reauthentication, a notification through an already trusted channel, and an audit record. OWASP recommends reauthentication after high-risk events and credential changes; linking a new way to enter a valuable contributor account belongs in the same threat model.

There is a less obvious operational cost here. Once multiple identities can point to one contributor, support requests stop being simple password resets. A person may lose one provider, retain another, and claim a third account with similar profile data. Write the recovery policy before launch: what evidence support can accept, which actions require a delay or manual review, and which ownership claims cannot be recovered automatically. I'm not sure any universal threshold is honest; the right threshold depends on the value of publishing rights and on abuse observed in your own logs. Start conservative, instrument decisions, and revise from evidence.

For a solo operator, this is the revenue-per-hour test. A polished provider chooser has little value if the merge rules can transfer a popular listing to the wrong person. Spend the week on state transitions, auditability, and recovery. The button styling can wait.

A small TypeScript boundary for discovery and linking

The application boundary should make the security distinction visible. This example returns the same discovery shape for every syntactically valid identifier, then requires two independently authenticated sessions before linking. The placeholder functions represent your authentication adapter and persistence layer; the policy stays in application code.

type SignInMethod = "password" | "external";

type DiscoveryResult = {
  methods: SignInMethod[];
  challengeId: string;
};

type AuthenticatedIdentity = {
  issuer: string;
  subject: string;
  authenticatedAt: Date;
};

type Contributor = {
  id: string;
};

const STANDARD_METHODS: SignInMethod[] = ["password", "external"];
const FRESH_SESSION_MS = 10 * 60 * 1000;

export async function discoverSignIn(
  rawIdentifier: string,
): Promise<DiscoveryResult> {
  await applyAbuseControls(rawIdentifier);

  return {
    methods: STANDARD_METHODS,
    challengeId: await issueOpaqueChallenge(),
  };
}

export async function linkIdentity(
  currentSession: string,
  candidateSession: string,
): Promise<Contributor> {
  const current = await requireAuthenticatedIdentity(currentSession);
  const candidate = await requireAuthenticatedIdentity(candidateSession);

  requireFreshAuthentication(current, FRESH_SESSION_MS);
  requireFreshAuthentication(candidate, FRESH_SESSION_MS);
  requireDifferentIdentity(current, candidate);

  return attachIdentityAtomically(current, candidate);
}
Enter fullscreen mode Exit fullscreen mode

The ten-minute freshness window is an example policy value, not a standard. Tune it to the consequence of a bad link and document the reason. More important, attachIdentityAtomically should preserve uniqueness for the issuer-and-subject pair. Two simultaneous requests must not attach one external identity to two contributors. Treat the audit write and the identity link as one transaction where the data store permits it; otherwise design an outbox so the security event cannot quietly disappear.

Test the failures, not just the happy path. Send concurrent link attempts. Replay an expired session. Try to link an identity already owned elsewhere. Compare discovery responses for known and unknown addresses. Verify that logs contain internal decision identifiers but exclude passwords, session tokens, and raw recovery secrets. I don't need a giant test suite before shipping; I do need coverage of every transition that changes account ownership.

When is the simpler runner-up better?

The catch is operational complexity. Provider discovery plus identity resolution is not suitable when every contributor uses the same email-and-password path, account duplication is rare, and the project has no capacity to operate linking or recovery. Stick with email and password in that case. Follow established password storage and recovery guidance, return generic authentication errors, throttle attempts, and add the resolution layer only when real duplicate identities appear.

Manual review is the better runner-up when the account can publish trusted releases, redirect payouts, or control a widely installed marketplace package. It costs maintainer time and does not scale well. Still, an explicit queue with recorded evidence is preferable to an automatic merge based on matching names or email text.

Provider discovery also loses much of its value if policy always returns one method. Do less. A fixed sign-in page has fewer states to test and fewer support paths to explain. Conversely, if the community already arrives through multiple trusted identity systems and duplicate contributor records are common, the added resolution machinery earns its keep because it protects continuity without making a profile field the source of truth.

The conclusion is a decision rule, not a product choice: use discovery when it routes among genuinely different permitted methods, and use identity resolution only when ownership can be proven on both sides. Keep privileged recovery manual. That boundary is small enough to test and strong enough to resist the most damaging shortcut: silently merging contributors who merely look alike.

References

Top comments (0)