NIP-98 Décalage des paramètres de requête URL
Problème
L'authentification NIP-98 échoue avec 401 Unauthorized même si le token est créé avec succès et la signature est valide. La cause réelle est une non-correspondance d'URL entre le tag u dans l'événement signé et ce que le serveur attend.
Contexte / Conditions de déclenchement
- Le serveur retourne 401 Unauthorized pour les endpoints protégés NIP-98
- Les logs montrent que la création du token a réussi (événement signé et validé)
- La réponse d'erreur contient "URL mismatch" avec les URLs attendue et réelle
- Les URLs diffèrent uniquement par les paramètres de requête
Exemple de réponse d'erreur :
{"error":"Auth failed: URL mismatch: expected .../notifications, got .../notifications?limit=50"}
Cause racine
La spec NIP-98 dit :
Le tag
uDOIT être exactement identique à l'URL de requête absolue (y compris les paramètres de requête).
Mais les implémentations serveur varient :
- Certains serveurs suivent la spec strictement (exigent les paramètres de requête dans le tag
u) - Certains serveurs normalisent les URLs en supprimant les paramètres de requête avant la validation
- Vous DEVEZ correspondre à ce que votre serveur spécifique fait
Solution
Étape 1 : Ajouter du logging pour voir l'erreur réelle
} else if (response.statusCode == 401) {
Log.error(
'NIP-98 auth failed (401)\n'
'URL: $url\n'
'Response: ${response.body}', // <-- Ceci révèle le problème réel
...
);
}
Étape 2 : Vérifier le message d'erreur
- Si l'erreur dit "expected .../path?params, got .../path" → Inclure les paramètres de requête
- Si l'erreur dit "expected .../path, got .../path?params" → Supprimer les paramètres de requête
Étape 3 : Ajuster la normalisation d'URL en conséquence
Pour les serveurs qui SUPPRIMENT les paramètres de requête (comme Divine Relay) :
final uri = Uri.parse(url);
// Le serveur supprime les paramètres de requête avant la validation NIP-98
final normalizedUrl = '${uri.scheme}://${uri.host}${uri.path}';
Pour les serveurs qui EXIGENT les paramètres de requête (selon la spec NIP-98) :
final uri = Uri.parse(url);
final normalizedUrl = uri.hasQuery
? '${uri.scheme}://${uri.host}${uri.path}?${uri.query}'
: '${uri.scheme}://${uri.host}${uri.path}';
Vérification
- Après le correctif, les URLs dans le tag
udoivent correspondre à ce que le serveur attend - L'erreur 401 doit être remplacée par une authentification réussie (200)
- Vérifier les logs pour confirmer la correspondance des URLs
Exemple
Comportement de Divine Relay (supprime les paramètres de requête) :
- URL de requête :
https://relay.dvines.org/api/users/abc/notifications?limit=50 - Le serveur valide contre :
https://relay.dvines.org/api/users/abc/notifications - Le tag
udoit être :https://relay.dvines.org/api/users/abc/notifications
Notes
- La spec NIP-98 est claire sur l'inclusion des paramètres de requête, mais tous les serveurs ne la suivent pas
- Toujours vérifier la réponse d'erreur réelle pour déterminer le comportement du serveur
- En cas de doute, essayer les deux approches et voir celle qui fonctionne
- Envisager de signaler un bug auprès des serveurs qui ne suivent pas la spec
Références
- Spécification NIP-98 HTTP Auth
- Citation de la spec : "Le tag
uDOIT être exactement identique à l'URL de requête absolue (y compris les paramètres de requête)" - Réalité : Les implémentations serveur varient, toujours vérifier le comportement réel