import type { KeyObject } from 'node:crypto'; import type { Client as PlcClient } from '@did-plc/lib'; import type { Keypair } from '@atproto/crypto'; import type { IdResolver } from '@atproto/identity'; import type { Cid } from '@atproto/lex-data'; import { type AtIdentifierString, type DidString, type HandleString } from '@atproto/lex-schema'; import { AuthRequiredError } from '@atproto/xrpc-server'; import type { ActorStore } from '../actor-store/actor-store.js'; import type { ServerConfig } from '../config/config.js'; import type { com } from '../lexicons/index.js'; import type { ServerMailer } from '../mailer/index.js'; import type { Sequencer } from '../sequencer/index.js'; import { type AccountDb, type EmailTokenPurpose } from './db/index.js'; import * as accountHelpers from './helpers/account.js'; import { AccountStatus, type ActorAccount } from './helpers/account.js'; import * as password from './helpers/password.js'; export { AccountStatus, formatAccountStatus } from './helpers/account.js'; export type UpdateAccountEmailOptions = { sendConfirmationEmail?: boolean; locale?: string; }; /** * Thrown by {@link AccountManager.login} when the identifier resolved to a * known account but the supplied credentials (account password / app * password) did not match. The matched `did` is attached so downstream * callers can distinguish "identifier known, credentials wrong" from * "identifier unknown" (which continues to throw a plain * {@link AuthRequiredError}). * * Callers should take care that remote clients *cannot* distinguish the above, * to prevent enumeration attacks. (Tested for in * packages/pds/tests/auth.test.ts) */ export declare class InvalidPasswordError extends AuthRequiredError { readonly did: DidString; constructor(did: DidString, errorMessage?: string); } /** * Thrown by {@link AccountManager.login} when the credentials were valid but * the account requires a second authentication factor. A one-time code has * already been dispatched by the time this is thrown. * * XRPC-native like every other error this class raises: `AuthFactorTokenRequired` * is the error name `com.atproto.server.createSession` declares, so that path * needs no translation. The OAuth boundary ({@link OAuthStore.authenticateAccount}) * converts it to `SecondAuthenticationFactorRequiredError` — which is why * `factor` and `hint` are carried as fields rather than baked into the message. * * @NOTE Subclasses {@link AuthRequiredError}, so any `instanceof` check against * the base class must come *after* this one — see the ordering note in * `OAuthStore.authenticateAccount`. */ export declare class AuthFactorRequiredError extends AuthRequiredError { readonly factor: 'emailOtp'; readonly hint: string; constructor(factor: 'emailOtp', hint: string, errorMessage?: string); } export type AccountManagerDbConfig = { accountDbLoc: string; disableWalAutoCheckpoint: boolean; }; export declare class AccountManager { readonly cfg: ServerConfig; readonly actorStore: ActorStore; readonly idResolver: IdResolver; readonly jwtKey: KeyObject; readonly mailer: ServerMailer; readonly sequencer: Sequencer; readonly plcClient: PlcClient; readonly plcRotationKey: Keypair; readonly db: AccountDb; constructor(cfg: ServerConfig, actorStore: ActorStore, idResolver: IdResolver, jwtKey: KeyObject, mailer: ServerMailer, sequencer: Sequencer, plcClient: PlcClient, plcRotationKey: Keypair); get serviceDid(): DidString; get serviceHandleDomains(): string[]; migrateOrThrow(): Promise; close(): Promise; getAccount(handleOrDid: AtIdentifierString, flags?: accountHelpers.AvailabilityFlags): Promise; getAccounts(dids: DidString[], flags?: accountHelpers.AvailabilityFlags): Promise>; getAccountByEmail(email: string, flags?: accountHelpers.AvailabilityFlags): Promise; isAccountActivated(did: DidString): Promise; getDidForActor(handleOrDid: AtIdentifierString, flags?: accountHelpers.AvailabilityFlags): Promise; getAccountStatus(handleOrDid: AtIdentifierString): Promise<{ status: AccountStatus.Deleted; account: null; } | { status: AccountStatus.Takendown; account: ActorAccount; } | { status: AccountStatus.Deactivated; account: ActorAccount; } | { status: AccountStatus.Active; account: ActorAccount; }>; normalizeAndValidateHandle(handle: string, { did, allowAnyValid, }?: { did?: string; allowAnyValid?: boolean; }): Promise; createAccount({ did, handle, email, password, repoCid, repoRev, inviteCode, deactivated, refreshJwt, }: { did: DidString; handle: HandleString; email?: string; password?: string; repoCid: Cid; repoRev: string; inviteCode?: string; deactivated?: boolean; refreshJwt?: string; }): Promise; createAccountAndSession(opts: { did: DidString; handle: HandleString; email?: string; password?: string; repoCid: Cid; repoRev: string; inviteCode?: string; deactivated?: boolean; }): Promise<{ accessJwt: string; refreshJwt: string; }>; /** * Validates the requested handle, updates the PLC document if needed, persists * the new handle locally, and emits an identity event. * * @throws {InvalidRequestError} when the handle is invalid, taken by another * account, or cannot be resolved for non-service domains. * * @see {@link AccountManager.updateAccountHandle} for behavior when the PLC update fails. */ updateHandle(did: DidString, newHandle: string, options?: { allowAnyValid?: boolean; }): Promise; validateHandleUpdate(did: DidString, newHandle: string, options?: { allowAnyValid?: boolean; }): Promise<{ did: DidString; handle: HandleString; account: ActorAccount; }>; /** * @note Failure to emit the identity event will silently be ignored. Users * can emit the event again by updating their handle to the same value. */ updateAccountHandle(did: DidString, handle: HandleString): Promise; deleteAccount(did: DidString): Promise; takedownAccount(did: DidString, takedown: com.atproto.admin.defs.StatusAttr): Promise; getAccountAdminStatus(did: DidString): Promise<{ takedown: com.atproto.admin.defs.StatusAttr; deactivated: com.atproto.admin.defs.StatusAttr; } | null>; updateRepoRoot(did: DidString, cid: Cid, rev: string): Promise; deactivateAccount(did: DidString, options?: { deleteCredentials?: boolean; deleteAfter?: string | null; }): Promise<{ status: AccountStatus.Takendown; account: ActorAccount; } | { status: AccountStatus.Deactivated; account: ActorAccount; } | { status: AccountStatus.Active; account: ActorAccount; }>; activateAccount(did: DidString): Promise<{ status: AccountStatus.Takendown; account: ActorAccount; } | { status: AccountStatus.Deactivated; account: ActorAccount; } | { status: AccountStatus.Active; account: ActorAccount; }>; sequenceAccountStatus(did: DidString): Promise; createSession(did: DidString, appPassword: password.AppPassDescript | null, isSoftDeleted?: boolean): Promise<{ accessJwt: string; refreshJwt: string; }>; rotateRefreshToken(id: string): any; revokeRefreshToken(id: string): Promise; login({ identifier, password, authFactorToken, locale, allowOtpBypassFor, }: { identifier: string; password: string; authFactorToken?: string | undefined; locale?: string | undefined; allowOtpBypassFor?: readonly DidString[]; }): Promise<{ user: ActorAccount; appPassword: password.AppPassDescript | null; isSoftDeleted: boolean; }>; createAppPassword(did: DidString, name: string, privileged: boolean): Promise; listAppPasswords(did: DidString): Promise<{ name: string; createdAt: import("@atproto/lex-schema").DatetimeString; privileged: boolean; }[]>; verifyAccountPassword(did: DidString, passwordStr: string): Promise; verifyAppPassword(did: DidString, passwordStr: string): Promise; revokeAppPassword(did: DidString, name: string): Promise; ensureInviteIsAvailable(code: string): Promise; createInviteCodes(toCreate: { account: string; codes: string[]; }[], useCount: number): Promise; createAccountInviteCodes(forAccount: string, codes: string[], expectedTotal: number, disabled: 0 | 1): Promise; getAccountInvitesCodes(did: DidString): Promise; getAccountsInvitesCodes(dids: DidString[]): Promise>; getInvitedByForAccounts(dids: DidString[]): Promise>; getInviteCodesUses(codes: string[]): Promise>; setAccountInvitesDisabled(did: DidString, disabled: boolean): Promise; disableInviteCodes(opts: { codes: string[]; accounts: string[]; }): Promise; createEmailToken(did: DidString, purpose: EmailTokenPurpose): Promise; assertValidEmailToken(did: DidString, purpose: EmailTokenPurpose, token: string): Promise; assertValidEmailTokenAndCleanup(did: DidString, purpose: EmailTokenPurpose, token: string): Promise; requestEmailConfirmation(did: DidString, opts?: { locale?: string; }): Promise; confirmEmail(did: DidString, email: string, token: string): Promise; requestEmailUpdate(did: DidString, opts?: { locale?: string; }): Promise<{ tokenRequired: boolean; }>; checkUpdateEmail(did: DidString, email: string, token?: string): Promise; /** * @throws UserAlreadyExistsError if the new email is already in use by another account */ updateEmail(did: DidString, email: string, token?: string, opts?: UpdateAccountEmailOptions): Promise; updateAccountEmail(did: DidString, email: string, opts?: UpdateAccountEmailOptions): Promise; /** * Enable email-based two-factor authentication. Enabling is immediate: the * account must already have a confirmed email matching `email`, since the * factor delivers one-time codes to that address. Returns * `{ account, tokenRequired }` for symmetry with future factors that require * a confirmation step; email-based 2FA never does, so `tokenRequired` is * always `false`. * * @throws InvalidRequestError if the account has no confirmed, matching email */ enableEmailAuthFactor(opts: { did: DidString; }): Promise; /** * Disable email-based two-factor authentication. This supports a two-phase * flow, if a token isn't initially supplied for proving control of the inbox * which prevents a hijacked session from silently turning off the factor. The * second call (with a valid `token`) actually disables it. Disabling an * already-disabled factor is an idempotent no-op. * * @throws InvalidRequestError if the account has no matching email, or the * token is invalid * @throws InvalidRequestError with `TokenRequired` error code if a * confirmation token is required but not provided */ disableEmailAuthFactor(opts: { did: DidString; token?: string; locale?: string; }): Promise; resetPassword(opts: { password: string; token: string; }): Promise<`did:${string}:${string}`>; updateAccountPassword(opts: { did: DidString; password: string; }): Promise; } //# sourceMappingURL=account-manager.d.ts.map