Skip to content

Move users off your existing login

Moving to EdgeKit ID is three steps, one id mode each, and each site takes them on its own schedule. Your users keep one user id through all three, with the grants and memberships attached to it. EdgeKit makes each step for you as a deploy of your configuration; see Choose who signs users in.

Step Mode Users sign in with EdgeKit holds a session
1 external_only Your login No
2 dual Your login Yes, made from your login’s credential
3 edgekit_only EdgeKit ID Yes

Before step 1, connect your login as an identity provider. If your login signs tokens, have it put each site’s own aud value, which EdgeKit gives you, in them. See Connect your existing login.

Your login stays the only way in. On each request EdgeKit verifies your login’s credential and knows the user from it; the first time it sees someone, it finds or creates their user. EdgeKit sign-in, /v1/id/user-details and /v1/org/* answer 404.

Your rules see two kinds of access at such a site: the grants the user holds in EdgeKit, as grants.*, and what your login says about them, as the external.* facts you map. The two are never merged, so a rule that should let your login’s paying users in reads external.*. See Claims from your login are facts, never grants.

Your login still signs users in, and EdgeKit turns its credential into an EdgeKit session the first time it sees it. From then on the user reads with an EdgeKit session, even after your login’s credential expires.

Before this step:

  • Move your rules off external.*. external.* is empty at a dual site, and a deploy is refused while any feature reading it covers the site. Your paying users’ access must be grants in EdgeKit by then; see Subscriptions.
  • Give EdgeKit your login’s sign-out page. A deploy is refused without it. See Sign-out in dual.

EdgeKit sign-in stays closed at a dual site unless EdgeKit turns it on for that site. It is off by default because your login may ask for more than an email, such as a second factor, and EdgeKit’s sign-in never does.

GET /v1/id/get-session makes the exchange by itself. When the request carries no EdgeKit session, EdgeKit reads your login’s cookie and:

  1. verifies the credential, and finds or creates the user, as in step 1;
  2. creates an EdgeKit session and answers with it, marked "exchanged": true on this one response;
  3. expires your login’s cookies, under the names you gave EdgeKit, on the host the request reached. A cookie your login set on a parent domain, such as example.com for a request to www.example.com, cannot be expired this way.

When there is no credential, or it does not verify, the answer is 200 with two nulls, as for anyone signed out, and that browser is not tried again for 5 minutes. Retrying at once would cost your login a check per page view.

GET /v1/id/get-session answers no errors for a failed exchange. To see why one fails, call POST /v1/id/session/exchange, which answers only in dual and 404 everywhere else. A browser sends it with the cookie; it ignores the 5-minute pause.

Status Code Message Retry
200 Each call creates a new session.
401 external_credential_missing That credential was not accepted. No
401 external_credential_expired That credential was not accepted. No: get a fresh credential
401 external_credential_invalid That credential was not accepted. No
503 identity_provider_unreachable The identity provider could not be reached. Yes, after Retry-After
503 identity_provider_rate_limited The identity provider could not be reached. Yes, after Retry-After

external_credential_invalid never says what was wrong with the credential, so that a forged one learns nothing.

A native app has no cookie for EdgeKit to read, so it sends the credential in the body:

Terminal window
curl -i -X POST https://your-tenant-host/v1/id/session/exchange \
-H 'x-edgekit-site-id: site_example' \
-H 'content-type: application/json' \
-d '{"subject_token": "<your login credential>"}'

The EdgeKit session token is in the set-auth-token response header; see Native apps. subject_token takes up to 8192 characters, and subject_token_type is accepted and optional. EdgeKit cannot delete the credential from your app’s storage: the app discards it.

The user holds two sessions, yours and EdgeKit’s, and neither ends the other. POST /v1/id/sign-out ends EdgeKit’s and answers your sign-out page as logoutUrl. Send the browser there to end yours.

{ "success": true, "logoutUrl": "https://login.example.com/logout" }

EdgeKit signs users in, and your login’s credential is no longer read. EdgeKit sessions made in step 2 keep working. A user who did not come back during step 2 signs in with EdgeKit ID by email, and gets the user they already had, because that user holds their address.

Users change their own address with EdgeKit again; see Change the user’s email address.

While your login owns your users, in external_only and dual, your server tells EdgeKit when it changes a person. Four routes take the person’s sub in your login as subject. They need an API key, answer on your tenant host only, never under /edgekit/, and a key pinned to any of your sites acts on any of your users.

Route Body What EdgeKit does
POST /v1/id/upstream/revoke-sessions subject, cause: lock or password_reset Ends every EdgeKit session the user holds.
POST /v1/id/upstream/change-identifier subject, email, changedAt, optional emailVerified Moves the user’s address. Sessions stay.
POST /v1/id/upstream/change-subject subject, newSubject Links the user to a new id in your login, such as after an account merge.
POST /v1/id/upstream/erase subject Erases the user. See Erased users.
const response = await fetch(
'https://your-tenant-host/v1/id/upstream/revoke-sessions',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.EDGEKIT_API_KEY}`,
'x-edgekit-site-id': 'site_example',
'content-type': 'application/json',
},
body: JSON.stringify({ subject: 'user-6475b8', cause: 'lock' }),
},
);
const { userId, applied } = await response.json();

Each answers 200 with { "success": true, "userId": "…", "applied": true }. applied: false means nothing changed, for example no session to end, or an address change no newer than the last one, so retrying is safe.

  • Revoke sessions reaches every browser within the delays in How quickly an ended session stops working. In dual, a credential that is still valid makes a new EdgeKit session on its next request, so stop issuing it too.
  • Change identifier ignores a change whose changedAt is not newer than the last one EdgeKit recorded, so an old retry cannot undo a newer change. Without emailVerified: true the new address is stored unverified. It does not end sessions: send a revoke as well if the change was a takeover.
  • Change subject cannot be retried: afterwards the old subject names nobody.
Status Code Message Cause
404 not_found No user is bound to that subject. EdgeKit has never seen that subject.
409 conflict That address is already held by another user. Change identifier: details.collidingUserId names that user.
409 conflict That subject is already bound to another user. Change subject: details.collidingUserId names that user.
503 service_unavailable This tenant has no identity provider. Your organization has no identity provider connected.

A missing, revoked or wrongly pinned key is refused as on every key route; see Issue and revoke API keys.