Réauthentification automatique

Vue d’ensemble

La réauthentification automatique est une fonctionnalité qui garantit que vos requêtes ne échouent jamais à cause d’un token expiré. Le client vérifie automatiquement la validité du token avant chaque requête et se réauthentifie si nécessaire.

Avantages

  • Pas besoin de vérifier manuellement la validité du token

  • Aucune interruption de service due à l’expiration du token

  • Code plus simple et plus lisible

  • Idéal pour les applications long-running

Fonctionnement

Le processus de réauthentification automatique suit ces étapes :

  1. Stockage des credentials : Lors de l’authentification initiale, le client stocke username et password

  2. Vérification automatique : Avant chaque requête, le client vérifie si le token est valide

  3. Réauthentification : Si le token est expiré, le client se réauthentifie automatiquement

  4. Exécution : La requête est ensuite exécutée avec le nouveau token

Note

Les credentials sont stockés uniquement en mémoire, jamais sur disque.

Comment ça marche

Stockage des credentials

Lors de l’authentification, le client stocke automatiquement vos identifiants :

from fasoarzeka import ArzekaPayment

client = ArzekaPayment()
client.authenticate("votre_username", "votre_password")

En coulisses, le client stocke :

  • self._token : Le token d’accès JWT

  • self._expires_at : Timestamp d’expiration du token

  • self._username : Votre username (pour réauthentification)

  • self._password : Votre password (pour réauthentification)

Vérification automatique avant chaque requête

Avant initiate_payment() et check_payment(), le client appelle automatiquement _ensure_valid_token() :

def _ensure_valid_token(self):
    """Vérifie la validité du token et réauthentifie si nécessaire"""
    if not self.is_token_valid():
        if not self._username or not self._password:
            raise ArzekaAuthenticationError(
                "Token expired and no credentials stored for re-authentication"
            )
        # Réauthentification automatique
        self.authenticate(self._username, self._password)

Utilisation

Avec instance de classe

C’est la méthode recommandée pour bénéficier de la réauthentification automatique.

from fasoarzeka import ArzekaPayment

# Créer le client et s'authentifier
client = ArzekaPayment()
client.authenticate("votre_username", "votre_password")

# Faire des requêtes sur une longue période
# La réauthentification se fait automatiquement si nécessaire

# Première requête (token valide)
response1 = client.initiate_payment(
    amount=1000,
    merchant_id="MERCHANT_123",
    # ... autres paramètres
)

# ... le temps passe, le token expire ...

# Deuxième requête (token expiré → réauthentification automatique)
response2 = client.initiate_payment(
    amount=2000,
    merchant_id="MERCHANT_123",
    # ... autres paramètres
)
# ✅ Le client se réauthentifie automatiquement avant cette requête

# Fermer le client
client.close()

Avec Context Manager

from fasoarzeka import ArzekaPayment
import time

with ArzekaPayment() as client:
    # S'authentifier une fois
    client.authenticate("username", "password")

    # Faire plusieurs opérations
    for i in range(10):
        response = client.initiate_payment(...)
        print(f"Paiement {i+1} initié")

        # Simuler un délai
        time.sleep(600)  # 10 minutes

        # La réauthentification se fait automatiquement si le token a expiré

Avec fonctions de convenance

Note

Les fonctions de convenance (initiate_payment(), check_payment()) créent un nouveau client pour chaque appel, donc la réauthentification automatique n’est pas applicable dans ce cas.

Pour bénéficier de la réauthentification automatique, utilisez une instance de classe.

Exemples pratiques

Exemple 1 : Application long-running

from fasoarzeka import ArzekaPayment
import time

# Application qui tourne en continu
client = ArzekaPayment()
client.authenticate("username", "password")

print("✅ Application démarrée")

try:
    while True:
        # Vérifier les paiements en attente
        pending_orders = get_pending_orders()

        for order in pending_orders:
            # La réauthentification se fait automatiquement si nécessaire
            status = client.check_payment(order['id'])

            if status['status'] == 'COMPLETED':
                mark_as_paid(order['id'])
                print(f"✅ Paiement {order['id']} confirmé")

        # Attendre avant la prochaine vérification
        time.sleep(300)  # 5 minutes

except KeyboardInterrupt:
    print("Arrêt de l'application")
finally:
    client.close()

Exemple 2 : API REST avec Flask

from flask import Flask, request, jsonify
from fasoarzeka import ArzekaPayment

app = Flask(__name__)

# Créer un client global (réutilisé pour toutes les requêtes)
client = ArzekaPayment()

@app.before_first_request
def init_client():
    """Initialiser le client au démarrage de l'application"""
    client.authenticate("username", "password")
    print("✅ Client Arzeka initialisé")

@app.route('/api/payment/initiate', methods=['POST'])
def initiate_payment_endpoint():
    data = request.json

    try:
        # Le client se réauthentifie automatiquement si nécessaire
        response = client.initiate_payment(
            amount=data['amount'],
            merchant_id=data['merchant_id'],
            additional_info=data['additional_info'],
            mapped_order_id=data['order_id'],
            hash_secret=app.config['HASH_SECRET'],
            link_for_update_status=app.config['WEBHOOK_URL'],
            link_back_to_calling_website=app.config['RETURN_URL']
        )

        return jsonify(response), 200

    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/payment/status/<order_id>', methods=['GET'])
def check_payment_endpoint(order_id):
    try:
        # Réauthentification automatique si nécessaire
        status = client.check_payment(order_id)
        return jsonify(status), 200
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    try:
        app.run(debug=True)
    finally:
        client.close()

Exemple 3 : Worker de traitement en arrière-plan

from fasoarzeka import ArzekaPayment
import redis
import json
import time

# Connexion Redis pour la queue
redis_client = redis.Redis(host='localhost', port=6379, db=0)

# Client Arzeka
arzeka_client = ArzekaPayment()
arzeka_client.authenticate("username", "password")

print("✅ Worker démarré")

while True:
    # Récupérer un paiement de la queue
    _, payment_data = redis_client.blpop('payment_queue')
    payment = json.loads(payment_data)

    try:
        # Initier le paiement (réauth auto si nécessaire)
        response = arzeka_client.initiate_payment(
            amount=payment['amount'],
            merchant_id=payment['merchant_id'],
            # ... autres paramètres
        )

        print(f"✅ Paiement {payment['order_id']} initié")

        # Stocker le résultat
        redis_client.set(
            f"payment:{payment['order_id']}",
            json.dumps(response)
        )

    except Exception as e:
        print(f"❌ Erreur : {e}")
        # Remettre dans la queue ou logger l'erreur

Comparaison avant/après

Sans réauthentification automatique (ancien code)

from fasoarzeka import ArzekaPayment

client = ArzekaPayment()
client.authenticate("username", "password")

# Avant chaque requête, vérifier manuellement
if not client.is_token_valid():
    print("Token expiré, réauthentification...")
    client.authenticate("username", "password")

response = client.initiate_payment(...)

# ... plus tard ...

# Encore vérifier manuellement
if not client.is_token_valid():
    print("Token expiré, réauthentification...")
    client.authenticate("username", "password")

status = client.check_payment(...)

# ❌ Répétitif et facile d'oublier

Avec réauthentification automatique (nouveau code)

from fasoarzeka import ArzekaPayment

client = ArzekaPayment()
client.authenticate("username", "password")

# Pas besoin de vérifier, c'est automatique !
response = client.initiate_payment(...)

# ... plus tard ...

# Toujours pas besoin de vérifier
status = client.check_payment(...)

# ✅ Simple et sans risque d'oubli

Gestion des erreurs

Erreur : Pas de credentials stockés

Si vous appelez initiate_payment() ou check_payment() sans vous être authentifié au préalable :

from fasoarzeka import ArzekaPayment, ArzekaAuthenticationError

client = ArzekaPayment()

# Pas d'authentification !

try:
    response = client.initiate_payment(...)
except ArzekaAuthenticationError as e:
    print(f"❌ Erreur : {e}")
    # → "Token expired and no credentials stored for re-authentication"

Solution : Toujours s’authentifier au moins une fois :

client = ArzekaPayment()
client.authenticate("username", "password")  # ✅
response = client.initiate_payment(...)

Erreur : Credentials invalides

Si les credentials stockés deviennent invalides (changement de mot de passe) :

client = ArzekaPayment()
client.authenticate("username", "old_password")

# ... le mot de passe est changé sur le serveur ...

try:
    response = client.initiate_payment(...)
except ArzekaAuthenticationError as e:
    print(f"❌ Réauthentification échouée : {e}")
    # Mettre à jour les credentials
    client.authenticate("username", "new_password")

Considérations de sécurité

Stockage en mémoire uniquement

Les credentials sont stockés uniquement en mémoire :

  • Sécurisé : Pas de persistence sur disque

  • Temporaire : Supprimés à la fin du programme

  • Isolé : Chaque instance de client a ses propres credentials

Avertissement

Même si le stockage est sécurisé, suivez ces bonnes pratiques :

  • Utilisez HTTPS en production

  • Ne loggez jamais les credentials

  • Utilisez des variables d’environnement

  • Limitez l’accès au serveur d’application

Variables d’environnement

La meilleure pratique est d’utiliser des variables d’environnement :

import os
from fasoarzeka import ArzekaPayment

client = ArzekaPayment()
client.authenticate(
    os.getenv('ARZEKA_USERNAME'),
    os.getenv('ARZEKA_PASSWORD')
)

Rotation des credentials

Si vous devez changer régulièrement de credentials :

def rotate_credentials(client, new_username, new_password):
    """Mettre à jour les credentials du client"""
    try:
        client.authenticate(new_username, new_password)
        print("✅ Credentials mis à jour")
    except ArzekaAuthenticationError as e:
        print(f"❌ Échec de la mise à jour : {e}")
        raise

# Utilisation
client = ArzekaPayment()
client.authenticate("old_username", "old_password")

# ... après un certain temps ...

rotate_credentials(client, "new_username", "new_password")

Performance

La réauthentification automatique a un impact minimal sur les performances :

  • Vérification rapide : is_token_valid() est très rapide (simple comparaison de timestamp)

  • Réauthentification rare : Se produit seulement quand le token expire (toutes les heures typiquement)

  • Pas de surcharge : Pas de requête supplémentaire si le token est valide

Benchmark

import time
from fasoarzeka import ArzekaPayment

client = ArzekaPayment()
client.authenticate("username", "password")

# Mesurer le temps de 100 vérifications
start = time.time()
for _ in range(100):
    client.is_token_valid()
elapsed = time.time() - start

print(f"100 vérifications en {elapsed:.4f}s")
# Résultat typique : 0.0001s (quasi instantané)

Prochaines étapes