# User accounts: profile, password, identities
Source: https://sovaria.in/docs/user-accounts

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.

## Update the signed-in user

JavaScript:
```js
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
```

HTTP:
```bash
curl -X PATCH "$PROJECT_URL/auth/v1/user" -H "Authorization: Bearer $ACCESS" -H "apikey: $ANON" \
  -d '{"data": {"theme": "dark"}}'          # PUT works too (Supabase clients)
```

- `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`.

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

## Anonymous users become permanent

JavaScript:
```js
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

JavaScript:
```js
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.

JavaScript:
```js
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.

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

Kotlin:
```kotlin
// Credential Manager / GoogleIdTokenCredential
sovaria.auth.signInWithIdToken("google", credential.idToken, nonce = rawNonce)
```

JavaScript:
```js
await sovaria.auth.signInWithIdToken({ provider: 'google', token: idToken, nonce })
```

HTTP:
```bash
curl -X POST "$PROJECT_URL/auth/v1/token?grant_type=id_token" -H "apikey: $ANON" \
  -d '{"provider": "apple", "id_token": "eyJ…", "nonce": "raw-nonce"}'
```

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.
