# Link your accounts to a keyring After ordinary Mementom sign-in, your server can attach your authenticated account directly to that session's personal keyring. One SDK call does the link; there is no invitation to copy, second keyring to choose, or manager tab to visit. Invitations remain a supported way for a brand, vendor or partner to offer new rights to a keyring, including account permission. Use session-bound linking when your server has already verified the account and the current personal session. ## Server setup Use [the generated server setup](https://developer.mementom.net/developers/guides/sdk-setup.md). Save your website and return address in Integration, then download setup. It includes the application ID and saved addresses; the API registers the public key and selects the challenges. Keep the company credential and private RSA key on your server. Retrieve a snapshot at the beginning of each flow: ```js import { Mementom, createAccountReference } from './mementom-sdk.mjs'; const mementom = await Mementom.connect({ applicationId, credential, privateKey }); const { client, configuration } = await mementom.forAttempt(); const accountAction = configuration.challenges.accountAccess; if (!accountAction) throw new Error('Enable User account access in Integration.'); const domain = configuration.origin; // Keep this configuration with the pending server-side attempt. ``` ## Enable in the console In Integration, open **Enable user account access**, choose **On**, and save. This enables account rights, preparing Login only if needed. It creates no account challenge. There is no challenge name, proof, duration or expiry to configure for account access. Login already authenticates the keyring. Enabling account access does not grant anyone a right or register a server key. The sign-in button names the requesting app, for example **Log in to SHUI**. The company name does not replace the app name on that button. **Create an invitation** remains available for optional invitation-based access. It opens the existing authorization QR form with Account access selected. **Account reference** is the opaque reference saved by your server for that one account. **Account label** lets its holder recognize the account without sharing private details. This form accepts one claim per invitation. Your server can issue the same invitation through the API below; normal integration does not require copying references through this console. ## Contract The vendor creates and privately stores a random `accountRef` beside its account. Use `createAccountReference()` from the server SDK (32 random bytes, base64url), or an equivalent cryptographically random identifier of 32–128 URL-safe characters. Do not use a name, email, internal account number, hash of a predictable account number, keyring ID or a credential. The reference is a resource locator; knowing it never authenticates a person or grants access. Generate distinct references for separate applications, even within the same company. The selected account scope (usually `account-access`) identifies the right. Its scope is independent of challenge definitions. Keep the existing Login definition and admission policy. Issuing access never changes policy. ### Link the account to the current session Authenticate your existing account using your normal login or recovery. Bind that proof to the same server-side Mementom session from the beginning of the flow. Apply your normal CSRF protection and keep the session in an HttpOnly cookie or your server session. Do not accept an account or keyring identity from the browser as authorization. ```js // Your server has verified this account and the current Mementom session. // Create the random accountRef once and store it beside your account. const scope = { applicationId, sessionId, domain, action: accountAction.actionId }; await client.linkAccountAccess({ ...scope, accountRef, resourceLabel: 'Your workspace' }); // { linked: true, accountRef, resourceDigest } // Then use the account right as shown below. No QR code is needed. ``` `POST /api/v1/access-rights/accounts/link` accepts exactly the six fields above. Mementom derives the keyring from the current authenticated application session. No keyring, subject, connection or right identifier is accepted or returned. Expired sessions, revoked Login, shared keys and unprotected continuity keyrings cannot link an account. No private image, phrase or account data is sent. Retry the same call after an uncertain network response. Concurrent and repeated calls create only one right, without resetting its limits or uses. A pre-existing paused, revoked, expired, scheduled, exhausted or capability-restricted right is not replaced: manage that right in Mementom. A successful link grants the account right. Use that right before opening the account. This requires no new challenge. The account right persists beyond the session that created it. A fresh Login session can discover the permitted account with `listAccountAccess`; never store the old `sessionId` as a permanent identity. Session-bound describes the authority for creating the link, not the lifetime of the account right. ### Request fields and recovery These six request fields do not add six setup fields or secrets: | Field | Source and purpose | | --- | --- | | `applicationId` | Generated server setup; scopes the account to your app. | | `sessionId` | Current server-side Login result; Mementom resolves its keyring. | | `domain` | SDK configuration snapshot; binds access to the registered origin. | | `action` | Discovered `challenges.accountAccess.actionId`; selects the account right. | | `accountRef` | Generated once by your server and stored with the account; identifies only that app-owned resource. | | `resourceLabel` | A short, non-private account label your app supplies for the holder. | The browser enters none of these values manually. Only the company credential and response private key are integration secrets. | Result | Server behavior | | --- | --- | | `200`, `linked: true` | Use the exact account right under the current session. | | `401`, `company_session_required` | Obtain a fresh personal Login session and rebind the verified account flow. Do not reuse an expired session. | | `401`, `vendor_authentication_required` | Correct the server credential; do not send the person through another login. | | `400` or `409`, `invalid_account_access_scope` | Refresh app configuration and start a new attempt. If the role is missing/off, enable it in Integration. | | `409`, `access_right_unavailable` | Keep the existing restrictions; use Mementom permission management. Do not generate a replacement reference to bypass them. | | Other `400`/`403` | Correct the request or use an eligible personal keyring. Keep the app account closed. | | Network failure | Retry with the same session and saved account reference while the session is current. | For new accounts, create the account and its reference during your onboarding, then follow this same session-bound flow. Existing data stays in your application. The ordinary Login admission policy remains unchanged. ### Open the account after sign-in `configuration.challenges.accountAccess` is `null` when account rights are Off. Otherwise it provides the account-right scope and `challengeRequired: false`. The `challenges.accountAccess` property name remains for SDK compatibility; it does not mean an account challenge is created or required. Existing integrations that deliberately selected a published protected action retain its policy. For those integrations only, `challengeRequired: true` (or an absent value from an older service) retains the existing challenge flow. New account setup has no extra challenge setting. ```js if (accountAction.challengeRequired === false) { const { allowed } = await client.useAccountAccess({ ...scope, accountRef }); if (!allowed) return denyAccountAccess(); // Resolve your saved reference to your existing account on your server. return openYourAccount(accountRef); } // Legacy published account policy (or an older service): retain your existing // account challenge, exact-resource decision verification and consumption. ``` `POST /api/v1/access-rights/accounts/use` accepts only `applicationId`, `sessionId`, `domain`, `action`, and `accountRef`. It returns `{allowed: true|false}`. No browser window, image, phrase, QR, invitation or continuation token is involved. It checks the current session and exact right, and spends one use per session/account. Concurrent calls and response-loss retries are safe with the same session and account reference. They do not spend twice. A new session spends another use. Within an admitted session, `checkAccountAccess` and `listAccountAccess` still recognize the account after its last allowed use. Revocation, expiry, capability restrictions and session invalidation remain effective, including on retries. `409 account_challenge_required` means the current setting requires additional proof: refresh configuration and use its challenge flow. `allowed:false` does not permit opening the account or creating a replacement account automatically. Your private mapping may index a hash of the random reference. Keep the mapping to the real account ID on your server. The app-scoped `resourceDigest` returned by Mementom binds a challenge to that reference; it is not a hash of your real account ID. Preserve existing references and mappings when adopting this flow. ### Grant rights by invitation Brands, vendors and partners can continue issuing invitations to give a keyring new rights. The recipient may be an existing holder or a new one; this mechanism is not limited to account linking. Account-specific invitations remain private and single-claim. Other rights retain their existing one-use or reusable QR/link options, limits, capability rules and revocation. For an account invitation, keep it private, cancel abandoned invitations, and check the exact account under the resulting authenticated session after claiming. Issuing an invitation alone is not proof of association. ## Optional invitation QR/link `POST https://auth.mementom.app/api/v1/authorization-invitations` uses the existing vendor bearer authentication. Add `accountRef` and a meaningful `resourceLabel`. `claimLimit` must be 1 (the default); account invitations cannot be public reusable grants. Other invitation time and right limits are unchanged. The service derives `resourceDigest` as SHA-256 of the UTF-8 JSON array `["mementom-vendor-account-v1", applicationId, accountRef]`, prefixed `sha256:`. If supplied, `resourceDigest` must match. References and account scopes are immutable after issuance; revoke the old right and issue a new one to replace it. ```js // Server-only. Persist this mapping in the vendor's account transaction. const accountRef = createAccountReference(); const invitation = await client.createAccountAccessInvitation({ applicationId, challengeActionId: accountAction.actionId, accountRef, resourceLabel: 'Your workspace', // Avoid personal identity in the label. claimMinutes: 10, }); // Existing output: invitationUrl, qrSvg, token and limits. // Additional context: accountRef and derived resourceDigest. ``` The claim screen already displays the resourceLabel and current limits. This reuses its standalone authentication, acceptance, cancellation and duplicate-claim handling. No credential entry is added to the vendor application. Claiming creates a right, not a login session or a new image key. ## Discover accessible accounts after ordinary login `POST /api/v1/access-rights/accounts` requires the vendor credential plus `applicationId`, the current `sessionId`, registered `domain`, and published action. Optional `limit` is 1–100 (default 50), and `after` is the `nextAfter` from the previous response. Each page rechecks live rights. ```js const scope = { applicationId, sessionId, domain, action: accountAction.actionId }; const page = await client.listAccountAccess(scope); // { accounts: [{ accountRef, resourceDigest, resourceLabel }], nextAfter: null } ``` This resolves the new-device account-selection gap: the vendor maps the returned references to its own accounts and offers a chooser if there is more than one. It returns no keyring/subject/connection/grant ID or cross-app identity. An empty array reveals no accounts from other holders or applications. The existing `listAccessRights` is administrative inventory; it is NOT this holder lookup. Neither discovery nor checking spends a use or extends authentication lifetime. ## Check one exact account The existing `POST /api/v1/access-rights/check` accepts `accountRef`. Account checks additionally require `applicationId` and registered `domain` and use strict account matching. A generic action/Login grant or open policy cannot satisfy it. ```js const { allowed } = await client.checkAccountAccess({ ...scope, accountRef }); if (!allowed) denyAccountAccess(); // Read-only permission check. For ordinary account entry, // call useAccountAccess; a check alone does not spend a limited use. ``` Successful checks return only `{ allowed: true|false }`. Missing, expired, ended, mismatched or stale sessions return 401. Invalid scope/reference input returns 400. Current Login revocation also blocks account discovery and checks. Account revocation, time limits, remaining uses, domain, delegated-parent rights, and capability restrictions remain enforced. Reauthentication changes the session; it does not regrant or reset account rights. Never cache a successful check as a permanent approval, treat a timeout as permission, or accept a browser claim that access was approved. Empty results/denials must not create another account automatically. This endpoint checks the right under the authenticated session; it does not complete the account-action challenge or issue a signed action decision. If the published action requires an additional ceremony, use its existing challenge, exact returned resourceDigest, verified/consumed decision, and current account check before executing. The account check cannot be replaced with a generic resource-only check: accountRef is the explicit strict-account selector. For ordinary continuing account access, use an unlimited-use right and appropriate expiry/revocation. Finite-use rights are spent by `useAccountAccess`, or by decision consumption for an existing published protected-action policy. A separate destructive action still needs its own exact authorization.