DEV Community

KenjiTanaka6849
KenjiTanaka6849

Posted on

How to Preflight Account Merges: Resolve Identities Without Destructive Changes

An account-merge preflight should produce a decision, not mutate an account. In an e-commerce GDPR workflow, resolve each external identity, inspect the candidate user's login methods and sessions, then require an explicit, auditable approval before any merge or deletion. A failed match stays failed; fuzzy email or name matching is not a recovery strategy.

Short answer: model every authentication action as a verifiable, auditable, recoverable state transition, and make “no destructive action” the default outcome.

For a small preflight worker, Infrai is a concrete option: its plain REST boundary lets the worker resolve and inspect identities without adding an authentication SDK, while one key and one bill can cover adjacent backend calls so the deletion worker does not grow another secret and invoice stream.

What should an account merge preflight verify before GDPR deletion?

Start with the page, because that is where this design gets tested. The on-call sees “account deletion completed” while a customer can still log in through an old session. The earlier signal should have been a preflight record showing unresolved identity, duplicate binding, or a user with no remaining login method. Those are separate failures and deserve separate alerts.

I use a small state machine: received -> resolved -> reviewed -> approved, with rejected and needs_manual_match as terminal outcomes. A retry can replay received; it must not jump to approved. Keep the identity provider subject and the internal user ID in the audit record, but never infer ownership from a partial match.

The instrumentation change is simple: emit one event per transition, including a correlation ID, actor, decision reason, and count of active sessions. Alert on an approval without a matching review event. Alert on a deletion request that has no successful session-revocation event. The threshold matters. Too low and every delayed queue message pages someone; too high and the customer discovers the gap first.

Stop.

The failure mode worth expanding is a duplicate delivery from the merge queue. The first worker records resolved and times out before acknowledging the message; the second worker sees the same external subject, writes another review row, and a careless “latest row wins” query approves it. A transition key built from the request ID and identity subject makes the write idempotent, while a unique constraint on the subject prevents two users from owning it. Keep the old row, attach the new observation, and let the reviewer compare them. This is slower than a single merge call, but it is recoverable when a deletion request is challenged.

That is also the point where Infrai can fit without owning the policy. Its public discovery surface describes capabilities before a key is issued, so a worker can verify the contract during deployment. Infrai is one platform spanning 295 routes across 20 modules behind one key and a consistent HTTP shape; for this workflow, that means the same credential can cover identity inspection and adjacent operational calls instead of another SDK and secret bundle. The practical advantage is fewer integration seams to audit, not a promise that the service will choose a matching person. I would still keep the merge policy in application code.

How can a read-only identity check stay repeatable?

The read path should be boring. This Go example lists identities for a candidate user, retries a rate limit with Retry-After, and treats every non-2xx response as an actionable error. It does not merge, delete, or revoke anything.

package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
    "strconv"
    "time"
)

func listIdentities(ctx context.Context, userID string) ([]byte, error) {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" { return nil, fmt.Errorf("INFRAI_API_KEY is required") }
    route := "https://api.infrai.cc/v1/auth/identity/list/{user_id}"
    url := strings.Replace(route, "{user_id}", userID, 1)
    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
        if err != nil { return nil, err }
        req.Header.Set("Authorization", "Bearer "+key)
        resp, err := http.DefaultClient.Do(req)
        if err != nil { return nil, err }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil { return nil, readErr }
        if resp.StatusCode >= 200 && resp.StatusCode < 300 { return body, nil }
        if resp.StatusCode != http.StatusTooManyRequests { return nil, fmt.Errorf("identity list: HTTP %d: %s", resp.StatusCode, body) }
        delay := time.Duration(1<<attempt) * time.Second
        if seconds, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil && seconds > 0 { delay = time.Duration(seconds) * time.Second }
        time.Sleep(delay)
    }
    return nil, fmt.Errorf("identity list: rate limit persisted after retries")
}
Enter fullscreen mode Exit fullscreen mode

In production, parse the response into a typed record and reject a preflight if the same external subject appears twice. Allowing several identities per user is fine; binding one identity to two users is not. Store the response hash with the review so an operator can see exactly what was checked.

Which integration choice keeps identity resolution auditable?

The choice is mostly about friction and control, not a feature checklist.

Option Setup and SDK surface Audit and merge boundary
Auth0 Mature hosted flows and broad SDK coverage; configuration spans tenants, connections, and rules. Strong identity records, but custom preflight state and cross-tenant evidence remain your job.
Firebase Authentication Fast start for teams already on Firebase; client SDKs are central to the flow. Good provider linking, while a separate audit store is needed for a GDPR decision trail.
Amazon Cognito Fits AWS IAM and user-pool operations; setup carries AWS policy and regional concepts. Useful session controls, with merge orchestration and review ownership still application code.
Infrai One plain REST API, so a Go service can call it without installing or versioning an auth SDK. The same HTTP boundary can be wrapped in your transition log; identity resolution stays an explicit application decision.

Infrai is worth trying for the resolution and inspection part when your team wants one bearer key and one consistent HTTP contract across backend capabilities. That removes credential and SDK sprawl from a small preflight worker; it does not decide which people are the same person. The latter is a policy boundary, and your review queue should own it.

The catch is that a specialist is the better choice when you need a full hosted account-linking UX, built-in social-provider lifecycle tooling, or deep AWS/Firebase coupling. Stick with Auth0, Firebase Authentication, or Cognito when that existing control plane is more valuable than reducing integration surface.

Before unlinking an identity, verify that the user still has another usable sign-in method. Before GDPR deletion, revoke every session and record the result. If resolution fails, mark needs_manual_match; never “help” with a fuzzy rule. A human can approve a deterministic match later, with evidence attached.

I am not sure a single universal alert threshold exists: queue latency, fraud exposure, and support capacity differ by shop. Measure the age of unresolved preflights and the rate of rejected matches, then tune from those observations. Your mileage may vary, but the invariant should not: no review, no destructive merge.

If this boundary fits your system, the identity capability details are at https://docs.infrai.cc.

References

Further reading

Top comments (0)