@authticon/client
v0.0.20
Published
Official Authticon API client for Node.js
Downloads
1,413
Readme
@authticon/client
Oficjalny klient JavaScript/TypeScript dla Authticon — usługi uwierzytelniania. Biblioteka wspiera zarówno środowisko Node.js (SSR, API routes), jak i przeglądarkę (SPA, client-side).
Instalacja
npm install @authticon/clientWymagania: Node.js >= 18
Dwa entry pointy
Biblioteka dostarcza dwa osobne moduły z odrębnymi implementacjami cookie i sesji:
| Import | Środowisko | Cookie adapter |
| ------------------------------------------------ | ------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| @authticon/client lub @authticon/client/node | Node.js / SSR | Parsuje cookies z obiektu Request, zwraca CookieStorageAdapter z metodami applyToResponse(), stringifySetCookies() |
| @authticon/client/browser | Przeglądarka / SPA | Używa document.cookie |
Szybki start
Node.js (np. Next.js, Express, Hono)
import { createAuthticon } from "@authticon/client/node";
const authticon = createAuthticon({
projectId: "your-project-id",
});
// W handlerze HTTP:
async function handler(request: Request) {
const { getUser, login, logout, cookies } = await authticon.session({
request,
});
const user = getUser(); // SessionUser | null
// cookies.applyToResponse(response) — ustawia Set-Cookie na odpowiedzi
}Przeglądarka
import { createAuthticon } from "@authticon/client/browser";
const authticon = createAuthticon({
projectId: "your-project-id",
});
const session = await authticon.session({});
const user = session.getUser();
await session.login({ email: "[email protected]", password: "secret" });Konfiguracja
type AuthticonOptions = {
projectId: string; // ID projektu w Authticon (wymagane)
baseUrl?: string; // URL API (domyślnie: "https://authticon.com")
jwksUrl?: string; // URL do JWKS (domyślnie: {baseUrl}/.well-known/jwks.json)
jwksCacheTtlMs?: number; // TTL cache kluczy JWKS (domyślnie: 1h)
cache?: CacheAdapter; // Zewnętrzny adapter cache (domyślnie: in-memory)
logger?: Logger; // Instancja pino logger
};Session (API stanowe)
session() to główny sposób interakcji z biblioteką. Tworzy stanowy obiekt sesji, który:
- Przy tworzeniu automatycznie odczytuje tokeny z cookies
- Weryfikuje access token za pomocą JWKS
- Jeśli token wygasł — automatycznie odświeża go za pomocą refresh tokena
- Cache'uje obiekt
SessionUserw pamięci na czas życia sesji - Operacje takie jak
login(),logout(),createGuest()automatycznie aktualizują wewnętrzny stan sesji i zapisują nowe tokeny w cookies
Tworzenie sesji
Node.js — z obiektu Request
const session = await authticon.session({ request });
// session.cookies — CookieStorageAdapter z metodami applyToResponse(), stringifySetCookies()Node.js — z własnym CookieAdapter
const session = await authticon.session({ cookies: myCookieAdapter });Przeglądarka
const session = await authticon.session({});
// Automatycznie używa document.cookieOpcjonalnie można przekazać tokenStorage do nadpisania nazw cookies i ich parametrów:
const session = await authticon.session({
request,
tokenStorage: {
accessTokenName: "my_access_token",
refreshTokenName: "my_refresh_token",
secure: true,
sameSite: "Strict",
domain: ".example.com",
},
});Metody sesji
Autentykacja
| Metoda | Opis |
| ---------------------------- | ------------------------------------------------------------------------- |
| login(params) | Logowanie (email/password). Zwraca SessionUser. |
| register(params) | Rejestracja. Zwraca dane rejestracji (tokeny nie są jeszcze zapisywane). |
| loginByMagicLink(params) | Loguje użytkownika przez magic link (z deviceId). Zwraca SessionUser. |
| forgotPassword(params) | Inicjuje reset hasła. |
| verifyEmail(params) | Weryfikuje email. |
| createGuest(params) | Tworzy użytkownika-gościa. Zwraca SessionUser. |
| acceptInvitation(params) | Akceptuje zaproszenie. Zwraca SessionUser. |
| resendConfirmation(params) | Ponownie wysyła email potwierdzający. |
| logout() | Wylogowuje (server-side + czyści cookies). |
| refresh() | Wymusza odświeżenie tokenów. |
Stan użytkownika
| Metoda | Opis |
| --------------------- | --------------------------------------------------------------------------------- |
| getUser() | Zwraca SessionUser \| null. |
| requireUser() | Zwraca SessionUser lub rzuca AuthticonError. |
| isLoggedIn() | Zwraca boolean — czy istnieje refresh token (sesja aktywna). |
| isLoggedInByAdmin() | Zwraca boolean — czy istnieje admin refresh token (logowanie jako użytkownik). |
| getFirstChallenge() | Zwraca pierwszy challenge (np. "verifyTwoFa", "setPassword") lub undefined. |
Profil użytkownika
| Metoda | Opis |
| ------------------ | -------------------------------------------- |
| getMe() | Pobiera dane zalogowanego użytkownika z API. |
| updateMe(params) | Aktualizuje dane zalogowanego użytkownika. |
Zarządzanie kontem
| Metoda | Opis |
| ------------------------ | ---------------------------------- |
| changeEmail(params) | Zmiana emaila. |
| changePassword(params) | Zmiana hasła. |
| setPassword(params) | Ustawienie hasła (np. po resecie). |
| changePhone(params) | Zmiana numeru telefonu. |
| verifyPhone(params) | Weryfikacja numeru telefonu. |
Dwuskładnikowe uwierzytelnianie (2FA)
| Metoda | Opis |
| ---------------------------------- | ------------------------------------------------------------- |
| getTwoFaSecret() | Pobiera sekret 2FA (do wyświetlenia QR code). |
| enableTwoFa(params) | Włącza 2FA. |
| disableTwoFa(params) | Wyłącza 2FA. |
| sendTwoFaCode(params) | Wysyła kod 2FA. |
| verifyTwoFaCode(code, remember?) | Weryfikuje kod 2FA i aktualizuje sesję. Zwraca SessionUser. |
Social OAuth (Google, Facebook, GitHub)
Logowanie i linkowanie kont przez zewnętrznych dostawców OAuth. Session automatycznie wybiera odpowiedni flow:
- Niezalogowany —
socialAuthorize/socialExchangeinicjuje logowanie/rejestrację - Zalogowany — te same metody linkują konto social do istniejącego użytkownika
// 1. Pobierz URL autoryzacji i przekieruj użytkownika
const { authorizationUrl } = await session.socialAuthorize("google", "https://app.example.com/callback");
// redirect(authorizationUrl)
// 2. Po powrocie z providera (w callback route) — wymień code na sesję
const user = await session.socialExchange("google", code, state);
// user jest zalogowany (lub konto zostało zlinkowane)| Metoda | Opis |
| ------------------------------------------- | -------------------------------------------------------------------------- |
| socialAuthorize(provider, redirectUri) | Generuje URL autoryzacji OAuth. Zalogowany? Linkuje. Niezalogowany? Loguje.|
| socialExchange(provider, code, state) | Wymienia code+state na sesję. Zalogowany? Linkuje. Niezalogowany? Loguje. |
| getSocialAccounts() | Lista powiązanych kont social (wymaga zalogowania). |
| socialUnlink(provider) | Odłącza konto social (wymaga zalogowania). |
Zaproszenia
| Metoda | Opis |
| -------------------------- | ------------------- |
| createInvitation(params) | Tworzy zaproszenie. |
| deleteInvitation(params) | Usuwa zaproszenie. |
Tokeny
Obiekt session.tokens daje bezpośredni dostęp do tokenów:
session.tokens.getAccessToken(); // string | null
session.tokens.getRefreshToken(); // string | null
session.tokens.verify(); // weryfikuje aktualny access token
session.tokens.verify(customToken); // weryfikuje dowolny token
session.tokens.clear(); // czyści tokeny z cookies i resetuje stan sesjiEventy
Sesja emituje eventy o zmianach stanu autoryzacji. session.on(event, handler) zwraca funkcję unsubscribe.
const off = session.on("login", (user) => {
console.log("Użytkownik w pełni zalogowany:", user.id);
});
session.on("register", () => {
console.log("Rejestracja zakończona");
});
session.on("challengeCompleted", (challenge, user) => {
console.log("Zaliczony challenge:", challenge);
});
session.on("logout", () => {
console.log("Wylogowano");
});
off(); // anuluje subskrypcję| Event | Argumenty | Kiedy emitowany |
| -------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| login | (user: SessionUser) | Po przejściu do stanu w pełni zalogowanego — dopiero gdy challenges jest puste. Nie emituje się gdy sesja jest tylko odtwarzana z cookies. |
| register | () | Po pomyślnym register(). |
| challengeCompleted | (challenge, user: SessionUser) | Gdy challenge znika z listy (np. po verifyTwoFaCode() lub po refresh() po weryfikacji emaila). |
| logout | () | Po wylogowaniu lub utracie sesji. |
Ważne: event login czeka aż wszystkie challenges (np. verifyTwoFa, verifyEmail) zostaną wykonane. Typowy flow z 2FA:
session.login(...)— tokeny zapisane, alechallenges = ["verifyTwoFa"]→ brak eventuloginsession.verifyTwoFaCode(code)— challenge zaliczony → emitujechallengeCompleted("verifyTwoFa")i zaraz po tymlogin(user)
SessionUser
type SessionUser<Payload> = {
id: string; // ID użytkownika
sessionId: string; // ID sesji
projectId: string; // ID projektu
role: "guest" | "user";
isGuest: boolean;
challenges: Challenge[]; // np. ["verifyTwoFa", "setPassword"]
payload: Payload; // custom claims z tokena
raw: AccessTokenPayload; // surowy payload JWT
};Low-level API
Oprócz stanowej sesji, createAuthticon() udostępnia niskopoziomowe klienty API, które nie zarządzają stanem ani cookies.
authticon.auth() — klient publiczny (bez autoryzacji)
Bezstanowy klient do endpointów niewymagających tokenu:
const auth = authticon.auth();
await auth.login({ email: "[email protected]", password: "secret" });
await auth.register({ email: "[email protected]", password: "secret" });
await auth.forgotPassword({ email: "[email protected]" });
await auth.loginByMagicLink({ token: "..." });
await auth.verifyEmail({ token: "..." });
await auth.createGuestUser({ ... });
await auth.acceptInvitation({ token: "...", password: "..." });
await auth.resendConfirmation({ email: "..." });
// Social OAuth
await auth.socialAuthorize("google", { redirectUri: "https://app.example.com/callback" });
await auth.socialExchange("google", { code: "...", state: "..." });Uwaga: Te metody zwracają surowe dane z API (np. tokeny). Zarządzanie cookies/stanem leży po stronie wywołującego.
authticon.admin(options) — klient administracyjny (tylko Node.js)
Wymaga klucza API. Służy do operacji administracyjnych:
const admin = authticon.admin({ apiKey: "your-api-key" });
await admin.listUsers({ page: 1 });
await admin.createUser({ email: "[email protected]", password: "..." });
await admin.getUser("user-id");
await admin.updateUser("user-id", { ... });
await admin.deleteUser("user-id");
await admin.loginAs({ userId: "user-id" });
await admin.sendMagicLink({ email: "[email protected]", url: "https://example.com/login" });
// Role
await admin.createUserRole("user-id", { ... });
await admin.listUserRoles("user-id");
await admin.updateUserRole("user-id", "role-id", { ... });
await admin.deleteUserRole("user-id", "role-id");
// Magic link
await admin.sendMagicLink({ email: "[email protected]" });
// SMS
await admin.sendSmsCode("user-id");
await admin.verifySms("user-id", { code: "123456" });
// Dostępność
await admin.isEmailAvailable("[email protected]"); // boolean
await admin.isPhoneAvailable("+48123456789"); // boolean
// Block/Unblock
await admin.isUserBlocked("user-id"); // boolean
await admin.blockUser("user-id");
await admin.unblockUser("user-id");
// Invitations
await admin.listInvitations({ limit: 10, email: "[email protected]" });
await admin.deleteInvitation("invitation-id");
// Test email
await admin.testEmail({ ... });authticon.tokens (tylko Node.js)
Bezpośredni dostęp do weryfikatora tokenów na poziomie instancji:
const payload = await authticon.tokens.verify(accessToken);
authticon.tokens.clearKeyCache(); // czyści cache kluczy JWKSCookie adaptery
Node.js — createNodeCookieStorageAdapter
Parsuje cookies z Request, buforuje zmiany i pozwala je aplikować do Response:
import { createNodeCookieStorageAdapter } from "@authticon/client/node";
const cookies = createNodeCookieStorageAdapter(request);
// Po operacjach sesji:
cookies.applyToResponse(response);
// lub:
const setCookieHeaders = cookies.stringifySetCookies(); // string[]Przeglądarka — createBrowserCookieAdapter
Operuje bezpośrednio na document.cookie:
import { createBrowserCookieAdapter } from "@authticon/client/browser";
const cookies = createBrowserCookieAdapter();Własny adapter
Możesz zaimplementować interfejs CookieAdapter:
interface CookieAdapter {
get(name: string): string | null;
set(name: string, value: string, options: CookieSetOptions): void;
remove(name: string, options: CookieRemoveOptions): void;
}Token storage — konfiguracja cookies
type TokenStorageOptions = {
accessTokenName?: string; // domyślnie: "access_token"
refreshTokenName?: string; // domyślnie: "refresh_token"
deviceIdName?: string; // domyślnie: "device_id"
adminRefreshTokenName?: string; // domyślnie: "admin_refresh_token"
path?: string; // domyślnie: "/"
domain?: string;
secure?: boolean; // domyślnie: true
sameSite?: "Strict" | "Lax" | "None"; // domyślnie: "Lax"
accessTokenMaxAge?: number; // domyślnie: 900 (15 min)
refreshTokenMaxAge?: number; // domyślnie: 2592000 (30 dni)
};Obsługa błędów
Biblioteka definiuje dedykowaną hierarchię błędów:
import {
AuthticonError,
AuthticonApiError,
AuthticonTokenError,
isAuthticonError,
isAuthticonApiError,
isAuthticonApiCodeError,
isAuthticonTokenError,
type ErrorCode,
} from "@authticon/client";| Klasa | Opis |
| --------------------- | --------------------------------------------------------------------------------- |
| AuthticonError | Bazowy błąd (np. brak tokenu, użytkownik niezalogowany) |
| AuthticonApiError | Błąd odpowiedzi API (zawiera statusCode, code i opcjonalny response) |
| AuthticonTokenError | Błąd weryfikacji JWT (zawiera code i oryginalny joseError) |
| Type guard | Opis |
| --------------------------------------- | --------------------------------------------------------------------------------- |
| isAuthticonError(error) | Sprawdza czy błąd jest instancją AuthticonError |
| isAuthticonApiError(error, code?) | Sprawdza czy błąd jest AuthticonApiError, opcjonalnie z danym kodem |
| isAuthticonApiCodeError(error, code) | Jak wyżej, ale zawęża typ code do konkretnego literału (lepsza inferencja) |
| isAuthticonTokenError(error, code?) | Sprawdza czy błąd jest AuthticonTokenError, opcjonalnie z kodem JOSE |
Kody błędów (ErrorCode)
AuthticonApiError zawiera pole code z typowanym kodem błędu, umożliwiającym precyzyjne rozróżnianie błędów o tym samym statusie HTTP:
try {
await session.login({ email, password });
} catch (error) {
// isAuthticonApiCodeError — zawęża typ `code` do konkretnego literału
if (isAuthticonApiCodeError(error, "USER_BLOCKED")) {
error.code; // typ: "USER_BLOCKED"
showBlockedMessage();
}
// isAuthticonApiError — sprawdza opcjonalnie kod bez zawężania typu
if (isAuthticonApiError(error, "UNAUTHORIZED")) {
showLoginError();
}
}Dostępne kody błędów:
| Kod | Opis |
| ---------------------------- | ------------------------------------------- |
| UNAUTHORIZED | Brak autoryzacji / nieprawidłowe dane |
| USER_BLOCKED | Konto zablokowane |
| SESSION_NOT_FOUND | Sesja nie istnieje |
| EMAIL_ALREADY_EXISTS | Email już zarejestrowany |
| EMAIL_NOT_VERIFIED | Email nie zweryfikowany |
| EMAIL_ALREADY_INVITED | Email już zaproszony |
| EMAIL_SEND_FAILED | Błąd wysyłki emaila |
| PHONE_ALREADY_EXISTS | Numer telefonu już istnieje |
| PHONE_NOT_SET | Numer telefonu nie ustawiony |
| PASSWORD_NOT_SET | Hasło nie ustawione |
| INVALID_OLD_PASSWORD | Nieprawidłowe stare hasło |
| NEW_PASSWORD_SAME_AS_OLD | Nowe hasło takie samo jak stare |
| USER_NOT_FOUND | Użytkownik nie znaleziony |
| USER_ALREADY_MEMBER | Użytkownik już jest członkiem |
| INVALID_2FA_CODE | Nieprawidłowy kod 2FA |
| TWO_FA_ALREADY_ENABLED | 2FA już włączone |
| TWO_FA_NOT_ENABLED | 2FA nie włączone |
| INVITATION_NOT_FOUND | Zaproszenie nie znalezione |
| OAUTH_PROVIDER_NOT_CONFIGURED | Dostawca OAuth nie skonfigurowany |
| OAUTH_FAILED | Błąd uwierzytelniania OAuth |
| OAUTH_STATE_INVALID | Nieprawidłowy state token OAuth |
| OAUTH_ALREADY_LINKED | Konto OAuth już połączone |
| SOCIAL_ACCOUNT_NOT_FOUND | Konto social nie znalezione |
| INVALID_REDIRECT_URI | Nieprawidłowy redirect URI |
| MAGIC_LINK_NOT_ENABLED | Magic link nie włączony |
| DEVICE_NOT_FOUND | Urządzenie nie znalezione |
| INVALID_VERIFICATION_CODE | Nieprawidłowy kod weryfikacyjny |
| TOO_MANY_CODES_SENT | Za dużo wysłanych kodów |
| SMS_SEND_FAILED | Błąd wysyłki SMS |
| NOT_FOUND | Zasób nie znaleziony |
| BAD_REQUEST | Nieprawidłowe żądanie |
| FORBIDDEN | Brak dostępu |
| INTERNAL_ERROR | Błąd wewnętrzny serwera |
try {
const user = session.requireUser();
} catch (error) {
if (isAuthticonApiError(error)) {
console.log(error.statusCode); // np. 401
console.log(error.code); // np. "USER_BLOCKED"
}
if (isAuthticonTokenError(error, "ERR_JWT_EXPIRED")) {
// Token wygasł
}
}Typowanie custom payloadu
Biblioteka wspiera generyczne typowanie payloadu JWT:
type MyPayload = {
organizationId: string;
permissions: string[];
};
const authticon = createAuthticon<MyPayload>({
projectId: "...",
});
const session = await authticon.session({ request });
const user = session.getUser();
user?.payload.organizationId; // string
user?.payload.permissions; // string[]Licencja
MIT
