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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@retish/auth

v1.0.7

Published

SDK client pour ReTiCh Auth

Readme

@retish/auth

SDK client pour ReTiCh Auth — similaire à Firebase Auth.

Installation

npm i @retish/auth

Usage

1. Initialiser le SDK

// main.tsx
import { ReTiChAuth } from "@retish/auth"
import { AuthProvider } from "@retish/auth/react"

const auth = new ReTiChAuth({
  baseUrl: "https://auth.mondomaine.com",
  clientId: "ton-client-id",
  clientSecret: "ton-client-secret",
  redirectUri: "https://monapp.com/callback",
})

export default function Root() {
  return (
    <AuthProvider auth={auth}>
      <App />
    </AuthProvider>
  )
}

2. Utiliser dans un composant

import { useAuth } from "@retish/auth/react"

export default function App() {
  const { user, loading, signIn, signOut } = useAuth()

  if (loading) return <p>Chargement...</p>

  if (!user) return <button onClick={signIn}>Se connecter</button>

  return (
    <div>
      <p>Connecté en tant que {user.email}</p>
      <button onClick={signOut}>Déconnexion</button>
    </div>
  )
}

3. Page de callback

// pages/Callback.tsx
import { useEffect } from "react"
import { useAuth } from "@retish/auth/react"
import { useNavigate } from "react-router-dom"

export default function Callback() {
  const { handleRedirectResult } = useAuth()
  const navigate = useNavigate()

  useEffect(() => {
    handleRedirectResult()
      .then(() => navigate("/dashboard"))
      .catch(() => navigate("/"))
  }, [])

  return <p>Connexion en cours...</p>
}

4. Appels API authentifiés

const { getAccessToken } = useAuth()

const token = await getAccessToken() // refresh automatique si expiré

const res = await fetch("https://api.mondomaine.com/profile", {
  headers: { Authorization: `Bearer ${token}` },
})

API

new ReTiChAuth(config)

| Paramètre | Type | Description | |-----------|------|-------------| | baseUrl | string | URL du service ReTiCh Auth | | clientId | string | client_id obtenu via la console admin | | clientSecret | string | client_secret obtenu via la console admin | | redirectUri | string | URL de callback après login |

useAuth()

| Propriété | Type | Description | |-----------|------|-------------| | user | ReTiChUser \| null | Utilisateur connecté ou null | | loading | boolean | true pendant la restauration de session | | signIn() | () => void | Redirige vers la page de login | | signOut() | () => Promise<void> | Déconnecte et révoque les tokens | | handleRedirectResult() | () => Promise<ReTiChUser \| null> | À appeler sur la page de callback | | getAccessToken() | () => Promise<string \| null> | Retourne un token valide (refresh auto) |

auth.onAuthStateChanged(callback)

const unsubscribe = auth.onAuthStateChanged((user) => {
  console.log(user) // ReTiChUser | null
})

// Arrêter d'écouter
unsubscribe()

Sécurité

Ce que le SDK protège

Vol de code OAuth (PKCE)

Quand l'utilisateur se connecte, ReTiCh Auth retourne un code dans l'URL. Sans PKCE, un attaquant qui intercepte cette URL pourrait l'échanger contre des tokens.

Le SDK génère un code_verifier aléatoire stocké uniquement dans le browser, et envoie son hash (code_challenge) à ReTiCh Auth. Pour échanger le code, il faut présenter le code_verifier original — impossible à deviner ou intercepter.

Attaquant intercepte ?code=abc123
  → tente d'échanger le code
  → ReTiCh Auth exige le code_verifier
  → attaquant ne l'a pas → refusé

CSRF (Cross-Site Request Forgery)

Un site malveillant pourrait tenter de déclencher un callback OAuth sur ton app avec un faux code.

Le SDK génère un state aléatoire avant chaque login et vérifie qu'il correspond au retour du callback. Un callback forgé aura un state différent et sera rejeté.

Site malveillant envoie /callback?code=...&state=faux
  → SDK compare state reçu vs state stocké
  → différent → erreur, tokens jamais émis

Protection par client_secret + PKCE

Le clientId et clientSecret sont requis pour échanger un code OAuth contre des tokens. Même si quelqu'un intercepte le code dans l'URL de callback, il lui faut aussi le code_verifier PKCE (jamais envoyé sur le réseau) et le clientSecret.

Attaquant intercepte ?code=abc123
  → tente d'échanger le code
  → ReTiCh Auth exige le clientSecret ET le code_verifier
  → attaquant n'a ni l'un ni l'autre → refusé

En production, mets le clientSecret dans une variable d'environnement (ex: VITE_CLIENT_SECRET) et ne le commite jamais dans git.

Redirect URI fixe

ReTiCh Auth refuse tout callback vers une URL non enregistrée dans la console admin. Même avec ton clientId, personne ne peut rediriger les tokens vers un autre domaine.

Attaquant modifie redirect_uri=https://evil.com
  → ReTiCh Auth compare avec les URIs enregistrées
  → non autorisé → flow annulé

Refresh token à usage unique

Chaque fois que le SDK renouvelle l'access token, l'ancien refresh token est révoqué et un nouveau est émis. Si un refresh token est volé et utilisé par un attaquant, l'utilisation légitime suivante le détectera (token déjà révoqué).

Signature RS256 vérifiée côté serveur

Les access tokens sont signés avec une clé RSA privée que seul ReTiCh Auth possède. Ton backend Node vérifie cette signature via la clé publique — un token forgé sera automatiquement rejeté même s'il contient les bonnes données.

Attaquant crée un faux token avec user_id=admin
  → Node vérifie la signature RS256
  → signature invalide → 401

Ce que le SDK ne fait pas

| Responsabilité | Qui s'en charge | |---|---| | Vérifier la signature JWT | Ton backend Node (via JWKS) | | Hasher les mots de passe | ReTiCh Auth | | Protéger contre le brute force | ReTiCh Auth (lockout après 5 tentatives) | | Révoquer les tokens à la déconnexion | ReTiCh Auth + signOut() |

Fonctionnement général

  • Login via OAuth 2.0 PKCE — aucun mot de passe ne transite par ton app
  • Tokens stockés dans localStorage, restaurés automatiquement au rechargement
  • Refresh automatique — l'access token est renouvelé 1 minute avant expiration

Valider les tokens côté serveur (Node.js)

npm i jose
import { createRemoteJWKSet, jwtVerify } from "jose"

const JWKS = createRemoteJWKSet(
  new URL("https://auth.mondomaine.com/.well-known/jwks.json")
)

async function requireAuth(req, res, next) {
  const token = req.headers.authorization?.slice(7)
  if (!token) return res.status(401).json({ error: "Token manquant" })

  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: "https://auth.mondomaine.com",
      audience: "ton-client-id", // client_id obtenu via la console admin
    })
    req.user = payload
    next()
  } catch {
    res.status(401).json({ error: "Token invalide ou expiré" })
  }
}