Abrova
☰ Docs menu · Web SDK

Web SDK

Abrova Auth for the browser (@abrova/auth-web). Anonymous, Apple, Google, e-mail and password, magic link and Telegram sign-in with only your public key; access token in memory, optional persistence, and a token provider for every Abrova SDK.

Web 0.1.1Last updated: 27 Oct 2026
Client-Safe

Uses the public API key only. Safe in a browser bundle

Any Framework

Plain TypeScript: React, Vue, Svelte, Next.js client code, or none

Memory by Default

Nothing is stored unless you opt in

Token Provider

tokenProvider and getToken() for the other Abrova SDKs

Nonce Helper

createNonce() for Apple JS and Google Identity Services

Upgrade Guests

linkApple, linkGoogle, linkPassword keep the user id

Abrova Auth Web SDK v0.1.1.

This page describes @abrova/auth-web 0.1.1.

Requirements.

A browser with fetch and crypto.getRandomValues (every current browser). Runs in client code only; Abrova Auth has no server SDK.

Using an AI assistant? Give it this page with Copy page for AI, or all the docs as plain text from /docs/llms.txt. See AI assistants.

Installation#

The package comes from Abrova Center. First point the @abrova scope at the Abrova registry in your project's .npmrc:

.npmrcINI
@abrova:registry=https://center.abrova.ir/api/packages/abrova/npm/

Then install:

bash
npm install @abrova/auth-web

Or in package.json:

json
{
  "dependencies": {
    "@abrova/auth-web": "^0.1.1"
  }
}

In Abrova Console, turn on Abrova Auth for your project (Settings › Products) and the sign-in methods you want (Abrova Auth › Sign-in methods).

Initialization#

Create one client for your app, with your public API key (ab_live_...):

typescript
import { createAbrovaAuth } from '@abrova/auth-web';

export const auth = createAbrovaAuth({ apiKey: 'ab_live_your_api_key' });

auth.onAuthStateChanged((userId) => {
  console.log(userId ? `signed in as ${userId}` : 'signed out');
});
OptionDefaultDescription
apiKey-Your public API key (ab_live_...). Never the server secret.
storagememoryWhere the refresh token is kept between page loads. See Staying signed in.
storageKeyderived from the API keyStorage key, so two projects never share a session.
timeoutMs15000Request timeout in milliseconds.

await auth.ready() resolves once a stored session (if any) is loaded. onAuthStateChanged calls your listener with the current user id at that point, and again on every sign-in or sign-out. It returns a function that stops listening.

Sign in#

typescript
const { methods } = await auth.getMethods(); // e.g. ['anonymous', 'google', 'password']

await auth.ready();
if (!auth.isSignedIn && methods.includes('anonymous')) {
  await auth.signInAnonymously();
}
console.log(auth.currentUserId); // ru_...

Show only the buttons for the methods getMethods() returns.

Use the user with other Abrova SDKs#

Coming soon. Abrova Sync, Chat, Push, Storage and A/B Testing are coming soon. The calls below show how they will take the Auth user; today, attach the user to Abrova Trace with its Next.js SDK (setUserId(auth.currentUserId)).
typescript
import { AbrovaSync } from '@abrova/sync-web';

// Sync, Push and A/B Testing: null while signed out
const sync = new AbrovaSync({ apiKey: 'ab_live_your_api_key', tokenProvider: () => auth.getToken() });

auth.getToken() resolves to a fresh access token, or null when nobody is signed in. auth.tokenProvider resolves to the token and rejects with UNAUTHENTICATED when nobody is signed in: use it for Abrova Chat, whose provider must return a token. After a sign-in or sign-out, call the other SDK's refresh method (for example sync.refreshUserToken()). More: Use with other Abrova products.

Sign-in methods#

Each method has its own page with the Console setup: Anonymous, Apple, Google, E-mail & password, Magic link, Telegram.

E-mail and password

typescript
const result = await auth.signUpWithEmail({ email, password, redirectUrl: 'https://example.com/auth/verify' });
// result.signedIn is false when the project requires a verified e-mail first.

await auth.signInWithEmail({ email, password });

await auth.sendPasswordReset({ email, redirectUrl: 'https://example.com/auth/reset' });
await auth.resetPassword({ token, password }); // token from the link's ?token=
await auth.verifyEmail({ token });
await auth.sendEmailVerification({ redirectUrl: 'https://example.com/auth/verify' });

Magic link

typescript
await auth.sendMagicLink({ email, redirectUrl: 'https://example.com/auth/magic' });

// On the page the link opens:
const token = new URL(location.href).searchParams.get('token');
if (token) await auth.signInWithMagicLink({ token });

Telegram

typescript
const { sent, botUrl } = await auth.requestTelegramCode({ phone: '+989121234567' });
if (!sent && botUrl) window.open(botUrl); // the user starts the bot and shares the number
await auth.verifyTelegramCode({ phone: '+989121234567', code: '123456' });

Apple and Google

Run the provider's own sign-in with a nonce from the SDK, then hand its ID token to Abrova:

typescript
const { rawNonce, hashedNonce } = await auth.createNonce();
// Give hashedNonce to Google Identity Services or Sign in with Apple JS as the `nonce`.
await auth.signInWithGoogle({ idToken, rawNonce });
await auth.signInWithApple({ idToken, rawNonce, authorizationCode, name });

Create a new nonce for every sign-in. Google sign-in needs a connection to Google, which some users in Iran don’t have. Anonymous, e-mail and password, magic link and Telegram code work without Google, so offer at least one of them as well. See Sign in with Google. On the web, Apple uses your Services ID: add it under Abrova Auth › Sign-in methods › Apple.

Upgrade an anonymous user#

Linking keeps the user id, so the user's data stays theirs:

typescript
import { isAbrovaAuthError } from '@abrova/auth-web';

try {
  await auth.linkGoogle({ idToken, rawNonce }); // or linkApple(...), linkPassword({ email, password })
} catch (e) {
  if (isAbrovaAuthError(e) && e.code === 'PROVIDER_ALREADY_LINKED') {
    // This Google account already belongs to another user.
  }
}

The current user#

typescript
auth.currentUserId;           // null when signed out
auth.isSignedIn;
const me = await auth.getMe(); // { id, anonymous, emailVerified, createdAt, providers }
auth.currentUser;             // the user last loaded by getMe()

Sign out and delete the account#

typescript
await auth.signOut();           // this device
await auth.signOutEverywhere(); // every device
await auth.deleteAccount();     // deletes the user, then signs out

Staying signed in#

By default nothing is stored: the user signs in again after a page reload. To keep the session, opt in to localStorage:

typescript
import { createAbrovaAuth, localStorageStorage } from '@abrova/auth-web';

const auth = createAbrovaAuth({ apiKey: 'ab_live_your_api_key', storage: localStorageStorage() });
localStorage and XSS.

Any script on your page can read localStorage. An XSS bug or a compromised dependency could take the refresh token and with it the user's session. Use this only with a strict Content Security Policy and no untrusted scripts. The SDK never sets cookies.

You can also pass your own storage: an object with async get, set and remove.

Handle errors#

Every failure is an AbrovaAuthError with a code:

typescript
import { isAbrovaAuthError } from '@abrova/auth-web';

try {
  await auth.signInWithEmail({ email, password });
} catch (e) {
  if (isAbrovaAuthError(e) && e.code === 'INVALID_CREDENTIALS') showError('Wrong e-mail or password');
  if (isAbrovaAuthError(e) && e.code === 'RATE_LIMITED') retryIn(e.retryAfterSec);
}

Besides the server's codes the SDK uses NETWORK_ERROR. A network error never signs the user out; a revoked or expired session does (onAuthStateChanged(null)). The access token is renewed 2 minutes before it expires, and calls at the same time share one renewal. All codes: Error codes.

Troubleshooting#

Signed out after every reload

That is the default: the session is kept in memory. Pass storage: localStorageStorage() if your page has a strict Content Security Policy, or sign in anonymously again.

currentUserId is null right after a reload

The stored session loads asynchronously. Wait for auth.ready() or the first onAuthStateChanged call.

E-mail link refused with INVALID_INPUT

The redirectUrl must start with one of the link addresses under Abrova Auth › Settings.

Was this page helpful?
Report a problem