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

@amsom-habitat/user-manager

v1.12.0

Published

Gestionnaire centralisé d'authentification utilisateur pour les applications AMSOM Habitat.

Downloads

464

Readme

@amsom-habitat/user-manager

Gestionnaire centralisé d'authentification utilisateur pour les applications AMSOM Habitat.

🎯 Objectifs

  • Gestion unifiée : Gérer les utilisateurs de la même manière sur tous les outils
  • Token partagé : Partage du token JWT via cookies entre les sites du même domaine
  • Réactivité : Synchronisation bidirectionnelle automatique avec les stores (Pinia/Vue)
  • Cross-tab : Détection automatique des changements de token depuis d'autres onglets

📦 Installation

npm install @amsom-habitat/user-manager

🚀 Utilisation de base

Configuration initiale

import userManager from '@amsom-habitat/user-manager'

// Configuration (dans main.js)
userManager.setLoginPageUrl('/login')
userManager.setDefaultLoginUrl('https://api.example.com/login')
userManager.setDefaultDomain('example.com')
userManager.setRoleHierarchy({
  ROLE_ADMIN: ['ROLE_USER']
})
userManager.setRoleKey('mon-application')

Authentification

// Login
const credentials = {
  _username: '[email protected]',
  _password: 'password123'
}

userManager.login(credentials)
  .then(token => {
    console.log('Connecté avec succès')
  })
  .catch(error => {
    console.error('Erreur de connexion:', error)
  })

// Logout
userManager.logout()

// Vérifier si connecté
if (userManager.isLogged()) {
  console.log('Utilisateur connecté')
}

// Redirection vers la page de login
userManager.goToLoginPage() // Sauvegarde l'URL actuelle pour redirection après login

Gestion du token

// Récupérer le token
const token = userManager.getToken()

// Définir un token manuellement
userManager.setToken(token, expirationTimestamp, domain)

// Décoder le token
const payload = userManager.getDecodedToken()
console.log(payload.login, payload.email, payload.roles)

// Vérifier la validité
if (userManager.isTokenValid()) {
  console.log('Token valide et non expiré')
}

// Temps restant avant expiration (en secondes)
const remaining = userManager.getTokenTimeRemaining()
console.log(`Token expire dans ${remaining} secondes`)

Gestion des rôles

// Récupérer les rôles
const roles = userManager.getRoles()

// Vérifier un rôle (avec hiérarchie)
if (userManager.isGranted('ROLE_ADMIN')) {
  console.log('Utilisateur admin')
}

⚡ Nouvelles fonctionnalités - Réactivité (v1.12.0)

Synchronisation automatique avec Pinia

// stores/user.js
import { defineStore } from 'pinia'
import userManager from '@amsom-habitat/user-manager'

let unwatchTokenCallback = null

export default defineStore('user', {
  state: () => ({
    token: userManager.getToken(),
  }),

  getters: {
    isLogged: (state) => userManager.isLogged(state.token),
    isTokenValid: (state) => userManager.isTokenValid(state.token),
    tokenTimeRemaining: (state) => userManager.getTokenTimeRemaining(state.token),
  },

  actions: {
    initTokenSync() {
      // Active la synchronisation bidirectionnelle
      unwatchTokenCallback = userManager.watchToken((newToken) => {
        if (newToken !== this.token) {
          console.log('Token synchronisé depuis userManager')
          this.token = newToken
        }
      })
    },

    stopTokenSync() {
      if (unwatchTokenCallback) {
        unwatchTokenCallback()
        unwatchTokenCallback = null
      }
    },

    logout() {
      this.token = null
      userManager.logout()
    },
  },
})

Initialisation dans l'application

// App.vue
import useUserStore from '@/stores/user'

export default {
  setup() {
    const userStore = useUserStore()
    return { userStore }
  },

  mounted() {
    // Initialise la synchronisation bidirectionnelle
    this.userStore.initTokenSync()
  },

  beforeUnmount() {
    // Nettoie les watchers
    this.userStore.stopTokenSync()
  },

  watch: {
    'userStore.token': function (newValue) {
      // Synchronisation store → userManager
      if (newValue) {
        this.$userManager.setToken(newValue)
      } else {
        this.$userManager.logout()
      }
    }
  },
}

Watchers personnalisés

// Écouter les changements de token
const unwatch = userManager.watchToken((newToken, oldToken) => {
  console.log('Token changé:', {
    from: oldToken ? 'token présent' : 'pas de token',
    to: newToken ? 'token présent' : 'pas de token'
  })

  // Réagir au changement
  if (!newToken) {
    // Utilisateur déconnecté
    router.push('/login')
  }
})

// Arrêter l'écoute
unwatch()

// Ou avec unwatchToken
userManager.unwatchToken(callback)

Détection cross-tab

Les changements de token dans un onglet sont automatiquement détectés dans tous les autres onglets du même domaine :

// Onglet 1 : l'utilisateur se connecte
userManager.login(credentials)

// Onglet 2 : le watcher détecte automatiquement le nouveau token
// → L'interface se met à jour automatiquement
// → L'utilisateur est connecté dans tous les onglets

📚 API complète

Configuration

| Fonction | Description | |----------|-------------| | setDefaultDomain(domain) | Définit le domaine pour les cookies | | setDefaultExpirationToken(timestamp) | Définit l'expiration par défaut | | setDefaultLoginUrl(url) | Définit l'URL de l'API de login | | setLoginPageUrl(url) | Définit l'URL de la page de login | | setRoleHierarchy(hierarchy) | Définit la hiérarchie des rôles | | setRoleKey(key) | Définit la clé pour les rôles multi-apps | | getDomain() | Récupère le domaine actuel |

Authentification

| Fonction | Description | |----------|-------------| | login(credentials, url?, domain?) | Connecte l'utilisateur | | logout() | Déconnecte l'utilisateur | | goToLoginPage(saveRedirect?) | Redirige vers la page de login | | redirectionAfterLogin(callback) | Gère la redirection après login |

Gestion du token

| Fonction | Description | |----------|-------------| | getToken() | Récupère le token actuel | | setToken(token, exp?, domain?) | Définit le token | | getDecodedToken(token?) | Décode le token JWT | | isTokenExpired(token?) | Vérifie si le token est expiré | | isTokenValid(token?) | Vérifie si le token est valide ⭐ | | getTokenTimeRemaining(token?) | Temps restant (secondes) ⭐ | | refreshTokenCache() | Rafraîchit le cache ⭐ |

Rôles

| Fonction | Description | |----------|-------------| | getRoles(token?) | Récupère la liste des rôles | | isGranted(role, token?) | Vérifie si un rôle est accordé | | isLogged(token?) | Vérifie si l'utilisateur est connecté |

Réactivité ⭐ Nouveau

| Fonction | Description | |----------|-------------| | watchToken(callback) | Écoute les changements de token ⭐ | | unwatchToken(callback) | Arrête l'écoute ⭐ | | getWatchersCount() | Nombre de watchers actifs ⭐ |

⭐ = Nouvelles fonctionnalités v1.12.0

🔄 Flux de synchronisation

┌─────────────────┐
│  Cookie Token   │ ◄──────────────────┐
│  (Partagé)      │                    │
└────────┬────────┘                    │
         │                             │
         │ getToken()          setToken()
         │                             │
         ▼                             │
┌─────────────────┐    watchToken()   │
│  userManager    │ ────────────────► │
│    (Cache)      │                   │
└────────┬────────┘                   │
         │                            │
         │ watchToken()               │
         │                            │
         ▼                            │
┌─────────────────┐     watch        │
│  Store Pinia    │ ─────────────────┘
│   (Réactif)     │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   Composants    │
│      Vue        │
└─────────────────┘

🛡️ Sécurité

  • Validation automatique des tokens avant utilisation
  • Détection des tokens expirés dans le cache
  • Protection contre les cookies trop volumineux
  • Validation des paramètres des fonctions critiques
  • Gestion sécurisée des erreurs

📝 Structure du token

interface TokenInterface {
  typeUSer: string
  login: string
  nomComplet: string
  email: string
  nom: string
  prenom: string
  poste: string
  departement: string
  extras: Record<string, any>
  roles: { [key: string]: string[] } | string[]
  exp: number          // Expiration du token
  expRefresh?: number  // Expiration du refresh token
}

🎯 Cas d'usage

Déconnexion automatique sur expiration

userManager.watchToken((newToken) => {
  if (!newToken || !userManager.isTokenValid(newToken)) {
    // Token expiré ou supprimé
    router.push('/login')
  }
})

Affichage du temps restant

setInterval(() => {
  const remaining = userManager.getTokenTimeRemaining()
  if (remaining !== null && remaining < 300) { // 5 minutes
    showWarning(`Session expire dans ${remaining}s`)
  }
}, 30000) // Vérifier toutes les 30s

Synchronisation multi-onglets

// L'utilisateur se déconnecte dans l'onglet A
// → Tous les onglets B, C, D détectent le changement via watchToken
// → Redirection automatique vers /login dans tous les onglets

🔧 Debug

// Afficher les informations du token
console.log('Token:', userManager.getToken())
console.log('Payload:', userManager.getDecodedToken())
console.log('Valide:', userManager.isTokenValid())
console.log('Temps restant:', userManager.getTokenTimeRemaining(), 's')
console.log('Watchers actifs:', userManager.getWatchersCount())

// Rafraîchir manuellement le cache
userManager.refreshTokenCache()

📄 Changelog

Voir CHANGELOG.md

📜 Licence

Propriétaire - AMSOM Habitat