Authentication
Show the login and signup modal, handle OAuth providers, and react to auth state changes.
Open the modal
After calling init(), use these methods to open the auth modal from anywhere in your code.
// Open as login
Auther.login();
// Open as signup
Auther.signup();
// Mount to a button - opens login on click
Auther.mount('#login-btn');
Auther.mount(document.getElementById('login-btn'));React to auth changes
Pass an onAuth callback to init(), or subscribe after the fact. Fires immediately with the current user (or null), then on every login/logout.
// Option A - inline with init()
Auther.init({
clientId: 'req_live_...',
onAuth: (user) => {
if (user) showApp();
else showLanding();
},
});
// Option B - subscribe after init
const unsubscribe = Auther.onAuthStateChange((user) => {
console.log(user ? 'Signed in' : 'Signed out');
});
// Stop listening when your component unmounts
unsubscribe();Names
The signup form collects a full name and it is stored against the user, so user.name is available everywhere the user is: the SDK, your backend via verify(), and the dashboard. Signing in with Google, GitHub or Meta takes the name from the provider instead.
It is optional, so treat user.name as string | null. It is null for anyone who left the field blank, for accounts created before names were collected, and for providers that do not return one. Fall back to user.email rather than rendering an empty space.
Magic link
Enable Magic Link under Settings → Authentication and the modal offers Email me a sign-in link. The user gets a link, clicks it, and lands back in your app signed in. There is nothing to build: the SDK picks up the token on init(), exchanges it, and strips it from the URL, so you do not need a /magic-link route.
Set your App URL under Settings → General first, or the link will not point at your app. Links expire in 15 minutes and work once. Magic link signs in existing accounts only: it will not create one, so an address typed into the form cannot be used to fill your user table.
Two-factor authentication
Users can enable TOTP (Google Authenticator, 1Password, Authy, anything standard) on their account. When they do, signing in takes a second step, and the SDK modal handles it: after the password it asks for the 6-digit code and completes the login itself.
If you have built your own UI instead of using the modal, this changes the shape of what you get back. A correct password no longer always means a session:
const result = await Auther.authenticate('login', { email, password });
if ('mfaRequired' in result) {
// Password was right, but this is NOT a session yet — no token was issued.
const user = await Auther.verifyMfa(result.challengeId, codeFromUser);
} else {
// Signed in.
}A wrong code does not cancel the attempt, so the user can simply retype it. Recovery codes are entered in the same field as authenticator codes: someone reaching for one has usually lost their phone, and the server accepts either.
OAuth providers
Google, GitHub and Meta are supported. Enable them in the Auther dashboard under Settings → Social Providers and add your OAuth app credentials. The SDK picks up the config automatically, with no extra code: a button appears for each provider you enable.
Google signs in on the page itself. GitHub and Meta open a short-lived popup that hands the session back to your app and closes. In their provider consoles, set the callback URL to https://oautherbackend.ziloris.com/api/v1/auth/oauth/[provider]/callback (with github or meta in place of [provider]).
Appearance
The modal ships a light theme. To match your product, open Settings → Appearance in the dashboard and set the accent, surface, background, text, input and border colours, a corner radius, and a logo. There is a live preview, and nothing to change in your code: the SDK picks the theme up with the rest of your project config.
Colours accept hex (#0f172a, #fff, #0f172acc) and rgb()/rgba(). Named colours like red are rejected, and so is anything that is not purely a colour — these values are written into CSS, so the input is deliberately narrow. The logo must be https, or browsers block it as mixed content.
Leave a field blank to keep the default. A project that sets nothing looks exactly as it does today.
Bot protection
Turn on Bot Protection under Settings → General and the modal adds a Cloudflare Turnstile check to sign-up, sign-in, and password reset. There is nothing to add to your code: the SDK renders the widget and sends the token itself.
Most people never see a challenge. Turnstile runs in managed mode, so for visitors it recognises it resolves on its own in about a second. Only traffic that looks automated gets interrupted.
This covers something rate limits cannot. Limits are counted per address, so a large enough botnet stays under them while still creating thousands of accounts; a captcha does not care how many addresses a request came from.
Upgrade the SDK before you turn this on
@auther-sdk/frontend or @auther-sdk/react 1.4.0 or later. Older versions cannot send a captcha token, so enabling this while your site still runs one will reject every sign-in and sign-up with Captcha verification is required. Upgrade and deploy first, then switch it on.You need your own Turnstile keys
Registering the domains is the step people miss. Keys alone are not enough — if the widget does not list the host your app is served from, it silently refuses to render there, and because the API is still expecting a token, sign-in fails. Add every domain you use, including localhost for local development.
Allowed origins
By default any site can sign users in to your project. To lock that down, list the origins your app runs on under Settings → Allowed Origins in the dashboard. Once the list is non-empty it is enforced, and a request from anywhere else is rejected with a 403.
Add every origin you actually use, including local development (for example http://localhost:3000) and any preview deployments. An origin is the scheme, host and port only: no path. Leaving the list empty keeps the current behaviour of allowing every origin.
Log out
Auther.logout();
// Clears the in-memory session, fires onAuthStateChange(null), and
// revokes the refresh token on the server so it cannot be resumedIn memory, never localStorage
getToken() so you can call your own APIs. Nothing is written to localStorage, so a stored token cannot be exfiltrated by XSS.