@eventista/sdk-auth
Authentication SDK for Eventista. Provides the AuthClient class with login, register, refresh, logout flows. Cross-platform (web / Node / React Native) — token persistence is delegated to a consumer-supplied TokenStorageAdapter.
The class shape mirrors RestClient from @eventista/sdk-api-client for consistency across the SDK suite.
Install
Section titled “Install”bun add @eventista/sdk-auth @eventista/sdk-api-client @eventista/sdk-coreQuickstart
Section titled “Quickstart”import { AuthClient, AuthEnvironment } from "@eventista/sdk-auth";
const auth = new AuthClient({ environment: AuthEnvironment.Production, // or AuthEnvironment.Development onTokensChanged: (tokens) => { if (tokens) { console.log("signed in until", new Date(tokens.expiresAt)); } else { console.log("signed out"); } },});
const result = await auth.login({ email: "a@b.test", password: "secret" });if (result.isErr) { if (result.code === "AUTH_INVALID_CREDENTIALS") { console.error("wrong password"); } else { console.error(result.code, result.errorCode, result.err); }} else { console.log("welcome", result.data.user.email);}The SDK manages the underlying Account service URL per environment —
consumers do not need to know infra-level URLs. Pass
AuthEnvironment.Production for live traffic,
AuthEnvironment.Development for the dev/staging tier.
Response shape
Section titled “Response shape”Every AuthClient method returns an AuthResponse<T> discriminated
result. Methods never throw on auth failure — branch on result.isErr
first, then on result.code for typed error handling.
| Field | When | Description |
|---|---|---|
isErr | always | false on success, true on failure. |
data | success only | Method-specific payload (e.g. LoginResult, RegisterResult). |
code | error only | Stable {@link AuthErrorCode} from {@link AUTH_ERROR_CODES}. Branch on this — it is part of the public API. |
errorCode | error only (when available) | Raw upstream code (number or string, e.g. 101009). Surface this in support tickets without parsing. |
err | error only | Human-readable message. |
const result = await auth.register({ email, password, redirectUri });if (result.isErr) { // result.code is AuthErrorCode; result.errorCode is the raw BE code. return showError(result.code, result.errorCode, result.err);}// result.data is RegisterResult.if (result.data.status === "verified") { setUser(result.data.user);} else { // status === "pendingVerification" showVerifyEmailNotice();}Email verification itself is fully server-side: the backend mails a
callback URL (built from redirectUri) with an embedded token; clicking
the link triggers verification on the BE and redirects back with status
in the URL params. The SDK never makes a verify-email HTTP call —
consumer apps only need to parse the URL params on their callback page.
Token storage
Section titled “Token storage”The default MemoryTokenStorageAdapter stores tokens in memory only — fine for tests and SSR, but not persistent across page reloads. Wire your own per platform.
Web example (Next.js)
Section titled “Web example (Next.js)”Use localStorage so tokens survive a hard refresh. Only construct the
client on the client side — localStorage is not available during SSR.
"use client";import { AuthClient, AuthEnvironment, type TokenStorageAdapter,} from "@eventista/sdk-auth";
const KEY = "eventista_auth_tokens";
const webStorage: TokenStorageAdapter = { async getTokens() { const raw = window.localStorage.getItem(KEY); return raw ? JSON.parse(raw) : null; }, async setTokens(tokens) { window.localStorage.setItem(KEY, JSON.stringify(tokens)); }, async clearTokens() { window.localStorage.removeItem(KEY); },};
export const auth = new AuthClient({ environment: AuthEnvironment.Production, storage: webStorage,});"use client";import { useState } from "react";import { useRouter } from "next/navigation";import { auth } from "@/lib/auth";
export default function LoginPage() { const router = useRouter(); const [error, setError] = useState<string | null>(null);
async function onSubmit(formData: FormData) { const result = await auth.login({ email: String(formData.get("email")), password: String(formData.get("password")), }); if (result.isErr) { setError( result.code === "AUTH_INVALID_CREDENTIALS" ? "Wrong email or password" : result.err, ); return; } router.push("/"); }
return ( <form action={onSubmit}> <input name="email" type="email" required /> <input name="password" type="password" required /> <button type="submit">Sign in</button> {error && <p>{error}</p>} </form> );}React Native example (Expo)
Section titled “React Native example (Expo)”Use @react-native-async-storage/async-storage for persistence across
app restarts.
import { AuthClient, AuthEnvironment, type TokenStorageAdapter,} from "@eventista/sdk-auth";import AsyncStorage from "@react-native-async-storage/async-storage";
const KEY = "eventista_auth_tokens";
const rnStorage: TokenStorageAdapter = { async getTokens() { const raw = await AsyncStorage.getItem(KEY); return raw ? JSON.parse(raw) : null; }, async setTokens(tokens) { await AsyncStorage.setItem(KEY, JSON.stringify(tokens)); }, async clearTokens() { await AsyncStorage.removeItem(KEY); },};
export const auth = new AuthClient({ environment: AuthEnvironment.Production, storage: rnStorage,});import { useState } from "react";import { Button, Text, TextInput, View } from "react-native";import { router } from "expo-router";import { auth } from "../lib/auth";
export default function LoginScreen() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState<string | null>(null);
async function handleLogin() { const result = await auth.login({ email, password }); if (result.isErr) { setError( result.code === "AUTH_INVALID_CREDENTIALS" ? "Wrong email or password" : result.err, ); return; } router.replace("/"); }
return ( <View> <TextInput value={email} onChangeText={setEmail} autoCapitalize="none" keyboardType="email-address" /> <TextInput value={password} onChangeText={setPassword} secureTextEntry /> <Button title="Sign in" onPress={handleLogin} /> {error && <Text>{error}</Text>} </View> );}Surface
Section titled “Surface”| Method | Description |
|---|---|
login | Email/password sign-in |
loginWithGoogle | Google OAuth sign-in (id_token) |
register | Create a new account |
forgotPassword | Trigger a password-reset email |
resetPassword | Set a new password using a reset token |
changePassword | Change password for the signed-in user |
refresh | Exchange a refresh token for a new access token |
logout | Revoke refresh token and clear local state |
getUserInfo | Fetch the authenticated user’s profile |
getTokens | Read currently stored tokens |
clearTokens | Drop local tokens without calling the backend |
All methods return Promise<AuthResponse<T>> — see the response shape
section above. getTokens and clearTokens are local-only and return
plain values (AuthTokens | null and void).
Status
Section titled “Status”WIP. Public API is unstable — do not depend on it for production yet.
Internally consolidated: every method’s logic lives directly on AuthClient. There is no separate services/ module — to extend the SDK, add a method on the class.
Migration from earlier (throwing) versions
Section titled “Migration from earlier (throwing) versions”Prior versions threw an AuthError class on failure. That class has
been removed. Replace try/catch with if (result.isErr):
// Beforetry { const result = await auth.login({ email, password }); console.log(result.user);} catch (err) { if (err instanceof AuthError && err.code === "AUTH_INVALID_CREDENTIALS") { // ... }}
// Afterconst result = await auth.login({ email, password });if (result.isErr) { if (result.code === "AUTH_INVALID_CREDENTIALS") { // ... }} else { console.log(result.data.user);}Field renames on the failure path:
err.code→result.code(unchanged semantic —AuthErrorCode)err.backendErrorCode→result.errorCode(raw upstream code)err.message→result.err(human message; matchesAPIResponsefrom@eventista/sdk-api-client)