Docs

User accounts: profile, password, identities

What a signed-in user can change themselves (metadata, email, password), confirming sensitive changes, turning anonymous users into permanent ones, linking sign-in methods, passwordless sign-up and native Apple / Google sign-in.

Updated Oct 26, 2026

#Update the signed-in user

await sovaria.auth.updateUser({ data: { theme: 'dark', locale: 'hi' } })   // user_metadata; null removes a key
await sovaria.auth.updateUser({ email: 'new@example.com' })                 // confirmed from the old and new address
await sovaria.auth.updateUser({ password: newPassword, currentPassword })    // or a recent sign-in, or a nonce
  • user_metadata (up to 16 KB) is the user's own; app_metadata and custom claims are set only by your team or server (PATCH /api/v1/projects/{id}/auth/users/{user}, sov auth-product update). Both are in tokens as user_metadata / app_metadata.
  • A new password follows the project's password rules and the leaked-password check, signs the user out of every other device and is emailed to them.

#Confirm sensitive changes

Changing the password (without the current one), adding or removing a passkey and unlinking a sign-in method need a sign-in in the last 10 minutes or a one-time code. With MFA on, they need an aal2 session. Otherwise the answer is 401 REAUTHENTICATION_REQUIRED.

await sovaria.auth.reauthenticate()                  // emails an OTP
await sovaria.auth.updateUser({ password, nonce: otp })

#Anonymous users become permanent

await sovaria.auth.signInAnonymously({ captchaToken })   // when Anonymous sign-ins are on
// … later, keep everything they created:
await sovaria.auth.updateUser({ email, password })      // permanent once the email is confirmed
// or
await sovaria.auth.linkIdentity('google')               // permanent at once (a verified Google email)

Anonymous users who never become permanent are deleted after Authentication › Attack protection › Anonymous users days (30 by default), and on a plan with a hard monthly-active-user limit they can take at most a share of it (50 % by default).

#Link and unlink sign-in methods

const { identities } = await sovaria.auth.getUserIdentities()   // email, google, saml:…, passkey
await sovaria.auth.linkIdentity('github', { redirectTo: 'https://app.example.com/settings' })
await sovaria.auth.unlinkIdentity('github')   // never the last way to sign in

An identity already linked to another user is refused (error=identity_already_exists on the redirect). When a provider confirms an email that an unconfirmed account already uses, Sovaria links them only after removing that account's password, factors, passkeys and sessions, so whoever registered the address can't keep a way in.

#Passwordless sign-up

Magic links and SMS codes sign existing users in. With createUser, an unknown address or number is signed up first (when Allow new users to sign up is on; your before-create hook runs). With sign-ups off, the answer is the same as for an unknown address.

await sovaria.auth.sendMagicLink(email, { createUser: true, data: { referrer: 'ads' } })
await sovaria.auth.sendOtp(phone, { createUser: true })

#Native Sign in with Apple and Google

On iOS and Android, sign in with the platform's own sheet and hand Sovaria the ID token. Add your bundle ID / Android client IDs as native client IDs on the provider (Sign in / Providers); a secret isn't needed for native-only use.

// ASAuthorizationAppleIDCredential: identityToken, with SHA-256(rawNonce) as the request nonce
try await sovaria.auth.signInWithIdToken(provider: "apple", idToken: idToken, nonce: rawNonce)

Sovaria checks the token against the provider's keys, your client IDs as the audience, the expiry and the nonce (raw or SHA-256); each token works once.