Documentation
ReferenceJavaScript SDKNamespacesauth.users

auth.users

The signed-in user acting on their own profile and identities.

Everything here acts as the current user, on themselves. For acting on other people, that is auth.admin.users, which needs a secret key and lives on the server.

Five of these need a secret key

update, sendOTPToUpdateEmail, confirmEmailUpdate, sendOTPToUpdatePhone and confirmPhoneUpdate are reachable only with sk- — call them from a browser client and the request is refused before it reaches your code. Everything else here, including me and the whole identities surface, works with either key.

The rule behind it: an endpoint that does not declare its accepted key types defaults to secret-only. So the split is not arbitrary — replacing a primary credential is server-side, while adding an identity beside an existing one is not.

The distinction that runs through this page: a user is one person with one id, and an identity is one way of proving they are that person. A user with a password, a Google login and a work email has one id and three identities — which is why signing in with Google after signing up with email does not create a second account.

me

me() → User

The full profile of whoever is signed in.

const { data: user } = await nulvless.auth.users.me();
idstringRequired

Stable for the life of the account, across every identity added later.

emailstringOptional

The primary email. The full list is in identities.

phonePhoneOptional
usernamestringOptional
profileUserProfileOptional

name, biography, gender, avatar_uri, date_of_birth.

identitiesIdentity[]Optional

Every way this person can sign in.

metadataRecord<string, string | number>Optional

Your own data. Only an admin can set it.

is_confirmedbooleanOptional

With confirmed_at.

is_bannedbooleanOptional

With banned_at. A banned user cannot sign in, but the record and its history survive.

password_enabledbooleanOptional

Whether the user has a password, whatever sign-in methods the platform enables. Use it to offer "Set a password" or "Change password".

last_sign_in_atstringOptional

update

update(params) → User

Updates the signed-in user's own profile. Omitted fields are left alone.

const { data: user } = await nulvless.auth.users.update({
  profile: { name: 'Ada Lovelace', biography: 'Analytical engines.' },
});
usernamestringOptional

Subject to the same rules as sign-up, and still has to be unique.

profileUserProfileOptional

name, biography, gender, avatar_uri, date_of_birth.

Identities are deliberately absent. An email or phone is never edited in place — you add the new one and confirm it, which is what the rest of this page is about.


getIdentities

getIdentities(type?) → Identity[]

Everything linked to the account.

const { data: identities } = await nulvless.auth.users.getIdentities();
const { data: emails } = await nulvless.auth.users.getIdentities('email');
typeIdentityTypeOptional

Narrows the result.

emailEmail addresses.
phonePhone numbers.
oauthSocial providers, each with provider and provider_id.

Each identity carries an id — which is what updateIdentity and removeIdentity take — plus identifier (the address, number, or provider account) and created_at.

This is the "connected accounts and addresses" screen.


addEmailIdentity

addEmailIdentity({ email, nonce }) → Challenge

Starts adding a second email to the account. A code is sent to the new address.

const { data: challenge } = await nulvless.auth.users.addEmailIdentity({
  email: '[email protected]',
  nonce: code,
});
emailstringRequired

The address being added.

noncestringOptional

Sudo confirmation. See below.

Returns a Challenge — nothing is added yet. Finish with verifyIdentity.

Requires sudo

Protected under manage_identities. Adding an address you control to someone else's account is a takeover, so a stolen session must not be able to do it quietly.


addPhoneIdentity

addPhoneIdentity({ phone, nonce }) → Challenge

The same flow for a phone number.

await nulvless.auth.users.addPhoneIdentity({
  phone: { country_code: '+55', number: '11987654321' },
  nonce: code,
});
phonePhoneRequired

{ country_code, number }.

noncestringOptional

verifyIdentity

verifyIdentity({ token, code }) → User

Confirms an addition started by either of the two calls above.

const { data: user } = await nulvless.auth.users.verifyIdentity({
  token: challenge.token,
  code,
});
tokenstringRequired

From the challenge you got back.

codestringRequired

What the user received at the new address or number.

Returns the updated User, with the new identity present. Until this succeeds the address is not attached and cannot be used to sign in.


updateIdentity

updateIdentity({ id, primary, nonce }) → User

Changes which identity is primary.

await nulvless.auth.users.updateIdentity({ id, primary: true, nonce: code });
idstringRequired

From getIdentities().

primarybooleanRequired

The only property that can be changed today. An identifier is never edited in place — you add the new one and remove the old.

noncestringOptional

Requires sudo

manage_identities.


removeIdentity

removeIdentity({ id, nonce }) → Identity

Unlinks an identity. Returns the one that was removed.

const { data: removed } = await nulvless.auth.users.removeIdentity({
  id,
  nonce: code,
});
idstringRequired
noncestringOptional

Removing the last identity that can sign in locks the account out. The platform does not decide for you which combinations leave a usable account, so check before offering the button — a user with only a Google identity who unlinks it has nothing left.


sendOTPToUpdateEmail

sendOTPToUpdateEmail({ email, nonce }) → Challenge

Starts replacing the primary email, rather than adding another one.

const { data: challenge } = await nulvless.auth.users.sendOTPToUpdateEmail({
  email: '[email protected]',
  nonce: code,
});
emailstringRequired

The new address. The code goes there, which is what proves the user controls it.

noncestringOptional

Finish with confirmEmailUpdate.

Requires sudo

manage_credentials — a different group from adding an identity, and an environment can require one without the other.


confirmEmailUpdate

confirmEmailUpdate({ token, code }) → User

Completes the change. Returns the updated User.

const { data: user } = await nulvless.auth.users.confirmEmailUpdate({
  token: challenge.token,
  code,
});

The old address stops being the primary at this point. Whether it remains as a secondary identity depends on how it was attached in the first place — read identities from the response rather than assuming.


sendOTPToUpdatePhone

sendOTPToUpdatePhone({ phone, nonce }) → Challenge

The same replacement flow for the phone number.

await nulvless.auth.users.sendOTPToUpdatePhone({
  phone: { country_code: '+55', number: '11987654321' },
  nonce: code,
});

confirmPhoneUpdate

confirmPhoneUpdate({ token, code }) → User

Completes it, returning the updated User.

await nulvless.auth.users.confirmPhoneUpdate({ token: challenge.token, code });

Add or replace?

The two pairs look similar and are not interchangeable:

  • addEmailIdentity → verifyIdentity leaves the existing address in place and gives the user a second way in. Sudo group manage_identities, and callable with either key.
  • sendOTPToUpdateEmail → confirmEmailUpdate swaps the primary address. Sudo group manage_credentials, and secret key only — this pair cannot run from the browser, so the swap has to go through your own server.

Both send a code to the new address, and neither changes anything before that code comes back.

Next

On this page