@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'
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
const credentials = {
_username: 'user@example.com',
_password: 'password123'
}
userManager.login(credentials)
.then(token => {
console.log('Connecté avec succès')
})
.catch(error => {
console.error('Erreur de connexion:', error)
})
userManager.logout()
if (userManager.isLogged()) {
console.log('Utilisateur connecté')
}
userManager.goToLoginPage()
Gestion du token
const token = userManager.getToken()
userManager.setToken(token, expirationTimestamp, domain)
const payload = userManager.getDecodedToken()
console.log(payload.login, payload.email, payload.roles)
if (userManager.isTokenValid()) {
console.log('Token valide et non expiré')
}
const remaining = userManager.getTokenTimeRemaining()
console.log(`Token expire dans ${remaining} secondes`)
Gestion des rôles
const roles = userManager.getRoles()
if (userManager.isGranted('ROLE_ADMIN')) {
console.log('Utilisateur admin')
}
⚡ Nouvelles fonctionnalités - Réactivité (v1.12.0)
Synchronisation automatique avec Pinia
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() {
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
import useUserStore from '@/stores/user'
export default {
setup() {
const userStore = useUserStore()
return { userStore }
},
mounted() {
this.userStore.initTokenSync()
},
beforeUnmount() {
this.userStore.stopTokenSync()
},
watch: {
'userStore.token': function (newValue) {
if (newValue) {
this.$userManager.setToken(newValue)
} else {
this.$userManager.logout()
}
}
},
}
Watchers personnalisés
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'
})
if (!newToken) {
router.push('/login')
}
})
unwatch()
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 :
userManager.login(credentials)
📚 API complète
Configuration
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
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
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
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
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
expRefresh?: number
}
🎯 Cas d'usage
Déconnexion automatique sur expiration
userManager.watchToken((newToken) => {
if (!newToken || !userManager.isTokenValid(newToken)) {
router.push('/login')
}
})
Affichage du temps restant
setInterval(() => {
const remaining = userManager.getTokenTimeRemaining()
if (remaining !== null && remaining < 300) {
showWarning(`Session expire dans ${remaining}s`)
}
}, 30000)
Synchronisation multi-onglets
🔧 Debug
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())
userManager.refreshTokenCache()
📄 Changelog
Voir CHANGELOG.md
📜 Licence
Propriétaire - AMSOM Habitat