import type { GraphQLClient } from '../client.js'; import type { AuthState } from '../auth-state.js'; /** * Authentication and account lifecycle — exposed as `client.auth`. * * Four ways to sign in: email + password ({@link register} / {@link login}), an * emailed magic link, or a federated social provider (OIDC). Every path returns * an identity SESSION token (management-plane), which is stored on the shared * session state automatically. Gameplay tokens are minted separately via * `client.portal`. * * This comment used to say Crowded Kingdoms was passwordless and that there was * no email+password login. That was never true of the server: `login` and * `register` have been first-class in the API throughout, and the C++ load * tester has used them all along. Only this SDK pretended otherwise, and the * gap sent integrators to the dev bypass — which no longer exists on any tier. * The same gap ran one method deeper until 2026-08-21: password MANAGEMENT * (reset, change, and adding a first password) was served by the API and * wrapped here by nothing, so a game shipping this SDK had no first-class way * to let a player set or change a password. * * Part of the management surface. * * **Public (no session):** {@link register}, {@link login}, * {@link requestLoginLink}, {@link completeLoginLink}, {@link socialLoginStart}, * {@link socialLoginComplete}, {@link availableLoginProviders}, * {@link checkAuthMethod}, {@link requestPasswordReset}, {@link resetPassword}. * **Require a session:** {@link logout}, {@link logoutAllDevices}, * {@link myIdentities}, {@link linkIdentity}, {@link unlinkIdentity}, * {@link changePassword}, {@link setInitialPassword}. * * **Which password method:** the four are distinguished by what the caller has * already proven, not by what they want to do. Signed in with a password → * {@link changePassword}. Signed in with none → {@link setInitialPassword}. * Not signed in, or signed in and cannot remember it → * {@link requestPasswordReset} then {@link resetPassword}. * {@link checkAuthMethod} answers `hasPassword` for an address before sign-in. */ export interface AuthUser { userId: string; email?: string | null; gamertag?: string | null; } export interface AuthResponse { /** Identity session token; stored on the session state automatically. */ token: string; gameTokenId: string; user: AuthUser; } export interface UserIdentity { identityId: string; provider: string; subject: string; email: string | null; emailVerified: boolean; createdAt: string; lastLoginAt: string | null; } /** * `register` refused because the address already has an account. * * `EMAIL_ALREADY_REGISTERED` from v1.60.0. Before that it arrived as * `INTERNAL_SERVER_ERROR`, so a caller keying on the code matched nothing and * treated a routine "this account exists" as a server fault; the wording branch * is what still works against those tiers. * * Note what this does NOT accept: a bare `CONFLICT`. From v1.60.0 that is the * code a generic 409 carries, and a generic code cannot identify a specific * condition — a predicate that accepted it would report any future conflict in * this mutation as "already registered". Only a code minted for this outcome * will do. */ export declare function isAlreadyRegisteredError(error: unknown): boolean; /** * `login` refused because the password is real but not yet confirmed, on an * account that has another verified sign-in method. The remedy is the emailed * confirmation link, not a different password — so this must not be reported to * the user as "wrong password". * * **The one refusal here with no code of its own, and the only wording-only * predicate left.** ck-api v1.60.0 gave the other four a dedicated * `extensions.code`; this one is still a plain `UnauthorizedException`, so it * arrives as `UNAUTHENTICATED` — the same code as an expired session, whose * remedy (sign in again) is the opposite of this one's. The message is * therefore the only discriminator, and the absence of a `codeOf` branch below * is deliberate rather than an oversight: there is no code to read. */ export declare function isPasswordUnconfirmedError(error: unknown): boolean; /** * {@link setInitialPassword} refused because the account already has a * password. The remedy is {@link changePassword}, which verifies the current * one. * * The refusal is deliberate and load-bearing: without it, `setInitialPassword` * would be `changePassword` with the current-password check deleted, which is * the check that stops a stolen session from silently locking the owner out. * So a caller must route to `changePassword` rather than retrying. * * `PASSWORD_ALREADY_SET` from ck-api v1.60.0. Before that the schema said this * "throws CONFLICT" and it reached clients as `INTERNAL_SERVER_ERROR`, so the * message was the only thing to match — which is why the wording branch is * still here. */ export declare function isPasswordAlreadySetError(error: unknown): boolean; /** * {@link changePassword} refused because there is no password to change — a * magic-link or social-only account. The remedy is {@link setInitialPassword} * while signed in, or {@link requestPasswordReset}. * * `PASSWORD_NOT_SET` from ck-api v1.60.0. Before that this and * {@link isInvalidCurrentPasswordError} both arrived as `UNAUTHENTICATED`, * which is ALSO what an expired session looks like — so a caller keying on the * code signed the user out when they had merely typed the wrong current * password, or offered a password field on an account that has none. That is * the defect the new codes exist to remove, and the wording branch is what * still separates them on a tier that has not deployed it. */ export declare function isNoPasswordSetError(error: unknown): boolean; /** * {@link changePassword} refused because the current password is wrong. The * remedy is to ask again — the session is fine. * * `INVALID_CURRENT_PASSWORD` (HTTP 403) from ck-api v1.60.0. See * {@link isNoPasswordSetError} for what it used to be and why the wording * branch stays. */ export declare function isInvalidCurrentPasswordError(error: unknown): boolean; export declare class AuthAPI { private readonly graphql; private readonly session; constructor(graphql: GraphQLClient, session: AuthState); /** The federated sign-in providers currently enabled (e.g. `['google']`). */ availableLoginProviders(): Promise; /** * Passwordless: email the address a one-time magic sign-in link (creating the * account on first sign-in). Always resolves `sent: true` (no enumeration). * * The token arrives only by email. There is no longer a `devToken` shortcut — * automated callers that need a session without an inbox should * {@link register} an account they own the password to. */ requestLoginLink(input: { email: string; redirectUri?: string; }): Promise<{ sent: boolean; }>; /** Complete a magic-link sign-in; stores the session token on success. */ completeLoginLink(token: string): Promise; /** * Begin a federated (social) sign-in. Returns an `authorizeUrl` to redirect the * user to and an opaque `state` to round-trip back to {@link socialLoginComplete}. */ socialLoginStart(provider: string, redirectUri: string): Promise<{ authorizeUrl: string; state: string; }>; /** Complete a federated sign-in from the provider callback; stores the token. */ socialLoginComplete(input: { provider: string; code: string; state: string; }): Promise; /** * Sign in with email + password; stores the session token on success. * * Throws when the credentials are wrong, and — separately — when the account * has another verified sign-in method and the password has not yet been * confirmed by email. {@link isPasswordUnconfirmedError} tells those apart, * because they need different things from the user. */ login(input: { email: string; password: string; }): Promise; /** * Create an email + password account; stores the session token on success. * * **A brand-new address gets a session immediately.** An address that already * has an account does NOT: the password is attached pending email * confirmation and the server throws instead of returning a token, so the * caller cannot treat "registered" and "signed in" as one outcome. Use * {@link isAlreadyRegisteredError} to detect it and fall back to * {@link login} or {@link requestLoginLink}. */ register(input: { email: string; password: string; gamertag?: string; }): Promise; /** * Email-first adaptive login: does this address have password sign-in enabled? * Public, and deliberately does not reveal whether the address is registered. */ checkAuthMethod(email: string): Promise<{ hasPassword: boolean; }>; /** * Email a password-reset link to the address. Public. * * Always resolves `true` whether or not the address has an account, so it * cannot be used to enumerate users — which also means a `true` here is not * evidence an email was sent. * * This is the ownership-proven way to add a password to an account that has * none, and the only one for a user who is not signed in. A user who IS * signed in should use {@link setInitialPassword} instead and skip the inbox. */ requestPasswordReset(email: string): Promise; /** * Complete a password reset with the token from the emailed link. Public — * the token is the authorization. * * Throws if the token is invalid or expired. **Existing sessions are not * revoked**, so a reset does not by itself evict anyone already signed in; * follow it with {@link logoutAllDevices} if that is what you want. */ resetPassword(input: { token: string; newPassword: string; }): Promise; /** * Change the signed-in user's password, verifying the current one. Requires a * session. * * **This is not the method for an account that has no password** — a * magic-link or social-only account, which cannot supply a current one. That * is {@link setInitialPassword}, and the two are kept apart deliberately: * the current-password check here is what stops a stolen session from * changing a credential the owner still knows. * * Three outcomes need telling apart and the error CODE cannot do it, because * a wrong current password, an account with no password, and an expired * session all arrive as `UNAUTHENTICATED`: * {@link isInvalidCurrentPasswordError} (ask again), * {@link isNoPasswordSetError} (send them to `setInitialPassword`), and * neither (the session is gone — sign in again). * * **Existing sessions are not revoked.** */ changePassword(input: { currentPassword: string; newPassword: string; }): Promise; /** * Add a password to the signed-in account when it does not have one yet. * Requires a session. * * For an account created by magic link or a social provider, which until this * existed had no in-product route to password sign-in at all — the only door * was {@link requestPasswordReset}, an email round trip to add a credential to * an account you are already signed in to. The session is the proof of account * control, so **the password works immediately**: there is no confirmation * email to wait for, and the password identity is written verified (an * unverified one would be refused by {@link login} while another verified * method exists, which is a dead end that looks like success). * * **Refuses when a password already exists** — {@link isPasswordAlreadySetError} * detects it — rather than replacing it. Without that refusal this would be * {@link changePassword} with the current-password check deleted. Route to * `changePassword`, or to `requestPasswordReset` if the user has forgotten it. * * **A security notification is emailed to the account address** whenever this * succeeds. That is the mitigation, and it is deliberately a notification * rather than a refusal: a stolen session can already attach durable * attacker-controlled access via {@link linkIdentity}, so refusing here would * remove the legitimate user's only door without closing the class. Do not * suppress or reword that email's role when you describe this to a user — * "we have emailed you about this change" is part of the feature. The * notification is best-effort on the server, so a `true` return is not proof * the email was delivered. * * **Existing sessions are not revoked.** */ setInitialPassword(newPassword: string): Promise; /** The signed-in user's linked sign-in identities. Requires a session. */ myIdentities(): Promise; /** Link an additional federated identity (from a social callback). */ linkIdentity(input: { provider: string; code: string; state: string; }): Promise; /** Unlink a federated identity (cannot remove the last sign-in method). */ unlinkIdentity(identityId: string): Promise; /** Single-device logout; clears the in-memory token on success. */ logout(): Promise; /** Revoke every active session for the user. Requires a session. */ logoutAllDevices(): Promise; /** Imperatively set the in-memory bearer token (e.g. rehydrate). */ setToken(token: string | null): void; /** Read the current in-memory bearer token. */ getToken(): string | null; } //# sourceMappingURL=auth.d.ts.map