npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@obsidiane/auth-sdk

v0.1.56

Published

SDK JavaScript/TypeScript pour consommer l’API Obsidiane Auth depuis un navigateur :

Readme

@obsidiane/auth-sdk – SDK JS navigateur pour Obsidiane Auth

SDK JavaScript/TypeScript pour consommer l’API Obsidiane Auth depuis un navigateur :

  • gère automatiquement les cookies (__Secure-at, __Host-rt) via fetch + credentials: 'include' ;
  • génère et envoie un token CSRF stateless dans l’en‑tête csrf-token pour les endpoints sensibles ;
  • expose des sous‑clients dédiés (auth, users, invites, setup) avec une méthode par endpoint ;
  • fournit des types TypeScript et une gestion d’erreurs structurée (ApiError).

Ce SDK est conçu pour être utilisé côté navigateur uniquement (pas de Node côté serveur).


1. Installation

npm install @obsidiane/auth-sdk
# ou
yarn add @obsidiane/auth-sdk

Le package est publié en ESM et inclut les définitions TypeScript (.d.ts).

Build (depuis le dépôt monorepo)

Depuis la racine du projet auth :

cd packages/auth-client-js
npm install        # si nécessaire
npm run build      # génère dist/*

ou via un script dans le package.json racine :

{
  "scripts": {
    "auth-sdk:build": "npm --prefix packages/auth-client-js run build"
  }
}

2. Concepts clés

2.1 Cookies & sessions JWT

  • L’API Auth émet deux cookies :
    • __Secure-at : access token (JWT), SameSite Lax, partagé ;
    • __Host-rt : refresh token (Gesdinet), SameSite Strict, host-only.
  • Le SDK ne manipule pas directement ces tokens :
    • ils restent dans les cookies HTTP Only ;
    • les appels se font toujours avec credentials: 'include'.

2.2 CSRF stateless

  • Aucun token CSRF n’est généré par le backend.
  • Le SDK doit :
    • générer un jeton aléatoire par requête sensible ;
    • l’envoyer dans l’en‑tête csrf-token.
  • Ce comportement est intégré dans le client HTTP :
    • csrf: true dans les options de requête → génération automatique d’un token ;
    • csrf: 'valeur' → utilisation d’un token fourni ;
    • csrf: false/undefined → aucun header CSRF.

2.3 Base URL & CORS

  • baseUrl doit pointer vers la racine d’Obsidiane Auth, par ex. :
    • https://auth.example.com
    • http://localhost:8000
  • Côté navigateur, pensez à :
    • autoriser l’origine dans ALLOWED_ORIGINS côté backend ;
    • toujours activer credentials: 'include' (géré par le SDK).

3. AuthClient – configuration de base

Le point d’entrée du SDK est la classe AuthClient.

import { AuthClient } from '@obsidiane/auth-sdk';

const auth = new AuthClient({
  baseUrl: 'https://auth.example.com',
});

3.1 Options disponibles

type AuthClientOptions = {
  baseUrl: string;                     // requis
  defaultHeaders?: HeadersInit;        // en-têtes appliqués à toutes les requêtes
  credentials?: RequestCredentials;    // 'include' (défaut), 'same-origin', 'omit'
  csrfHeaderName?: string;             // 'csrf-token' par défaut
  csrfTokenGenerator?: () => string;   // pour surcharger la génération CSRF
  fetchImpl?: typeof fetch;            // pour tests ou environnements custom
};
  • baseUrl : URL du service d’authentification.
  • defaultHeaders : ex. {'X-App': 'my-frontend'}.
  • credentials : par défaut 'include' pour envoyer/recevoir les cookies JWT.
  • csrfHeaderName : à ne changer que si le backend n’utilise pas csrf-token.
  • csrfTokenGenerator : permet d’imposer votre propre stratégie de génération.
  • fetchImpl : utile pour :
    • mocker fetch dans des tests ;
    • injecter une version instrumentée de fetch.

4. Sous-clients & endpoints

AuthClient expose plusieurs sous‑clients pour regrouper les endpoints par domaine.

const client = new AuthClient({ baseUrl: 'https://auth.example.com' });

client.auth;    // endpoints /api/auth/*
client.users;   // endpoints /api/users*
client.invites; // endpoints /api/invite_users*
client.setup;   // endpoint /api/setup/admin

Les types suivants sont exportés pour typer vos appels :

import type {
  // Auth
  User,
  UserRead,
  LoginResponse,
  MeResponse,
  RegisterInput,
  RegisterResponse,
  RefreshResponse,
  PasswordForgotResponse,
  InviteStatusResponse,
  CompleteInviteResponse,
  InitialAdminInput,
  InitialAdminResponse,
  // JSON-LD
  Item,
  Collection,
  // Resources API Platform
  InviteUserRead,
} from '@obsidiane/auth-sdk';

4.1 Auth – client.auth

Endpoints principaux :

  • POST /api/auth/login
  • GET /api/auth/me
  • POST /api/auth/refresh
  • POST /api/auth/register
  • POST /api/auth/logout
  • POST /api/auth/password/forgot
  • POST /api/auth/password/reset
  • POST /api/auth/invite
  • POST /api/auth/invite/complete

Méthodes exposées :

// Profil courant
const me: MeResponse = await client.auth.me();

// Login (CSRF requis)
const { user, exp }: LoginResponse = await client.auth.login(email, password);
// user.lastLoginAt contient la date ISO de dernière connexion (mise à jour à chaque login)

// Refresh silencieux (CSRF non requis, mais possible)
const refresh: RefreshResponse = await client.auth.refresh();

// Logout (CSRF requis)
await client.auth.logout();

// Inscription (CSRF requis)
const registration: RegisterResponse = await client.auth.register({
  email: '[email protected]',
  password: 'Secret123!',
});

// Demande de reset (CSRF requis)
const forgot: PasswordForgotResponse = await client.auth.requestPasswordReset('[email protected]');

// Reset password (CSRF requis)
await client.auth.resetPassword('<reset-token>', 'NewSecret123!');

// Invitation (admin, CSRF requis)
const inviteStatus: InviteStatusResponse = await client.auth.inviteUser('[email protected]');

// Compléter une invitation (public, CSRF requis)
const completed: CompleteInviteResponse = await client.auth.completeInvite('token', 'Secret123!');

4.2 Setup initial – client.setup

Endpoint :

  • POST /api/setup/admin
const input: InitialAdminInput = {
  email: '[email protected]',
  password: 'Secret123!',
};

const created: InitialAdminResponse = await client.setup.createInitialAdmin(input);

Ce flux n’est disponible que tant qu’aucun utilisateur n’existe dans la base.

4.3 Users – client.users

Endpoints Api Platform :

  • GET /api/users
  • GET /api/users/{id}
  • DELETE /api/users/{id}
  • POST /api/users/{id}/roles (admin, CSRF stateless)

Le backend expose les ressources utilisateur au format JSON-LD (API Platform) :

  • une collection JSON-LD typée Collection<UserRead> ;
  • des items JSON-LD portant leurs métadonnées via Item (@id, @type, @context).
import type { Collection, UserRead } from '@obsidiane/auth-sdk';

// Liste d’utilisateurs (admin uniquement)
const users: Collection<UserRead> = await client.users.list();

// Accès aux métadonnées JSON-LD de la collection
console.log(users['@id'], users.totalItems, users.view);

// Détail des items JSON-LD
const first: UserRead | undefined = users.member[0];

// Détail d’un utilisateur seul
const user: UserRead = await client.users.get(1);

// Mise à jour des rôles (admin + CSRF stateless automatique)
const updated = await client.users.updateRoles(1, { roles: ['ROLE_ADMIN'] });
console.log(updated.user.roles);

// Suppression via l’ID numérique
await client.users.delete(42);

4.4 Invites – client.invites

Endpoints Api Platform :

  • GET /api/invite_users
  • GET /api/invite_users/{id}

Les invitations admin exposées par API Platform sont également renvoyées en JSON-LD.

import type { Collection, InviteUserRead } from '@obsidiane/auth-sdk';

// Collection JSON-LD d’invitations
const invites: Collection<InviteUserRead> = await client.invites.list();

// Accès aux items
const firstInvite: InviteUserRead | undefined = invites.member[0];

// Lecture d’une invitation spécifique
const invite: InviteUserRead = await client.invites.get('some-id');

5. Gestion des erreurs

Toutes les erreurs HTTP (codes 4xx/5xx) sont mappées sur une exception ApiError :

import { ApiError } from '@obsidiane/auth-sdk';

try {
  await client.auth.register({ email, password });
} catch (error) {
  if (error instanceof ApiError) {
    console.error('Status HTTP :', error.status);
    console.error('Code métier :', error.errorCode);
    console.error('Détails :', error.details);
    console.error('Payload brut :', error.payload);
  } else {
    console.error('Erreur inattendue :', error);
  }
}

ApiError reflète la structure d’erreur du backend :

  • status : code HTTP ;
  • errorCode : champ error du payload (EMAIL_ALREADY_USED, INVALID_REGISTRATION, etc.) ;
  • details : champ details lorsque présent ;
  • payload : payload complet retourné par l’API.

Un alias AuthSdkError est également exporté pour des usages plus génériques.


6. Intégration front – exemples

6.1 Vanilla JS / TS

import { AuthClient } from '@obsidiane/auth-sdk';

const client = new AuthClient({
  baseUrl: 'https://auth.example.com',
});

async function handleLogin(email: string, password: string) {
  const { user } = await client.auth.login(email, password);
  console.log('Connecté en tant que', user.email);
}

6.2 React (hook simple)

import { useState, useEffect } from 'react';
import { AuthClient, ApiError, type MeResponse } from '@obsidiane/auth-sdk';

const client = new AuthClient({ baseUrl: 'https://auth.example.com' });

export function useCurrentUser() {
  const [data, setData] = useState<MeResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<ApiError | null>(null);

  useEffect(() => {
    let cancelled = false;

    client.auth
      .me()
      .then((res) => {
        if (!cancelled) {
          setData(res);
        }
      })
      .catch((err) => {
        if (!cancelled && err instanceof ApiError && err.status === 401) {
          setData(null);
        } else if (!cancelled) {
          setError(err);
        }
      })
      .finally(() => {
        if (!cancelled) {
          setLoading(false);
        }
      });

    return () => {
      cancelled = true;
    };
  }, []);

  return { data, loading, error };
}

6.3 Refresh silencieux

async function scheduleRefresh(client: AuthClient, exp: number) {
  const now = Date.now() / 1000;
  const delaySeconds = Math.max(exp - now - 30, 10); // rafraîchit 30s avant l’expiration

  setTimeout(async () => {
    const { exp: newExp } = await client.auth.refresh();
    await scheduleRefresh(client, newExp);
  }, delaySeconds * 1000);
}

const { exp } = await client.auth.login('[email protected]', 'Secret123!');
scheduleRefresh(client, exp);

7. Utilisation avancée

7.1 Générer un token CSRF compatible

Dans la plupart des cas, vous n’avez pas besoin d’appeler cela directement (les méthodes du SDK s’en chargent). Pour des appels HTTP personnalisés, vous pouvez générer un token compatible :

import { AuthClient } from '@obsidiane/auth-sdk';

const client = new AuthClient({ baseUrl: 'https://auth.example.com' });
const csrf = client.generateCsrfToken();

await fetch('https://auth.example.com/api/custom/endpoint', {
  method: 'POST',
  credentials: 'include',
  headers: {
    'Content-Type': 'application/json',
    'csrf-token': csrf,
  },
  body: JSON.stringify({ foo: 'bar' }),
});

7.2 Surcharger fetch

Pour instrumenter toutes les requêtes du SDK (logs, tracing, mocking) :

const instrumentedFetch: typeof fetch = async (input, init) => {
  const start = performance.now();
  const response = await fetch(input, init);
  const duration = performance.now() - start;

  console.debug('Auth SDK request', { input, status: response.status, duration });

  return response;
};

const client = new AuthClient({
  baseUrl: 'https://auth.example.com',
  fetchImpl: instrumentedFetch,
});

7.3 Surcharger la génération CSRF

const client = new AuthClient({
  baseUrl: 'https://auth.example.com',
  csrfTokenGenerator: () => myCustomRandomHex(32),
});

8. Bonnes pratiques de sécurité côté front

  • Ne stockez jamais les JWT dans localStorage ou sessionStorage :
    • laissez le backend gérer les cookies HttpOnly ;
    • utilisez toujours credentials: 'include' (géré par le SDK).
  • Générer un token CSRF par requête sensible (le SDK le fait pour toutes ses méthodes).
  • Garder baseUrl cohérent avec la politique CORS (ALLOWED_ORIGINS) du backend.
  • En production :
    • servir le frontend en HTTPS ;
    • s’assurer que les cookies Secure sont bien activés.

Pour le détail fonctionnel des endpoints (payloads, codes d’erreurs métier, flows complets), reportez‑vous au README.md du projet principal Obsidiane Auth. Ce SDK en est un simple wrapper typé, respectant les mêmes conventions (CSRF stateless, JWT + refresh, allowlist de redirection).