J'écris une webapp en Angular où l'authentification est gérée par un jeton JWT, ce qui signifie que chaque demande a un en-tête "Authentication" avec toutes les informations nécessaires.
Cela fonctionne bien pour les appels REST, mais je ne comprends pas comment je dois gérer les liens de téléchargement pour les fichiers hébergés sur le backend (les fichiers résident sur le même serveur où les services Web sont hébergés).
Je ne peux pas utiliser de <a href='...'/>
liens réguliers car ils ne porteront aucun en-tête et l'authentification échouera. Idem pour les différentes incantations de window.open(...)
.
Quelques solutions auxquelles j'ai pensé:
- Générer un lien de téléchargement temporaire non sécurisé sur le serveur
- Transmettez les informations d'authentification en tant que paramètre d'URL et gérez manuellement le cas
- Récupérez les données via XHR et enregistrez le fichier côté client.
Tout ce qui précède est loin d’être satisfaisant.
1 est la solution que j'utilise actuellement. Je n'aime pas ça pour deux raisons: premièrement ce n'est pas idéal du point de vue sécurité, deuxièmement ça marche mais ça demande beaucoup de travail surtout sur le serveur: pour télécharger quelque chose, je dois appeler un service qui génère un nouveau "aléatoire "url, le stocke quelque part (éventuellement sur la base de données) pendant un certain temps, et le renvoie au client. Le client obtient l'URL et utilise window.open ou similaire avec. Lorsqu'elle est demandée, la nouvelle URL doit vérifier si elle est toujours valide, puis renvoyer les données.
2 semble au moins autant de travail.
3 semble beaucoup de travail, même en utilisant les bibliothèques disponibles, et beaucoup de problèmes potentiels. (J'aurais besoin de fournir ma propre barre d'état de téléchargement, de charger le fichier entier en mémoire, puis de demander à l'utilisateur de sauvegarder le fichier localement).
La tâche semble cependant assez basique, alors je me demande s'il y a quelque chose de beaucoup plus simple que je puisse utiliser.
Je ne cherche pas forcément une solution "à la manière angulaire". Javascript régulier serait bien.
la source
Réponses:
Voici un moyen de le télécharger sur le client à l' aide de l'attribut de téléchargement , de l'API fetch et de URL.createObjectURL . Vous récupérez le fichier à l'aide de votre JWT, convertissez la charge utile en un objet blob, placez l'objet blob dans une objectURL, définissez la source d'une balise d'ancrage sur cette objectURL et cliquez sur cette objectURL en javascript.
La valeur de l'
download
attribut sera le nom éventuel du fichier. Si vous le souhaitez, vous pouvez extraire un nom de fichier prévu de l'en-tête de réponse de disposition de contenu comme décrit dans d'autres réponses .la source
Technique
Sur la base de ce conseil de Matias Woloski d'Auth0, évangéliste connu du JWT, je l'ai résolu en générant une requête signée avec Hawk .
Citant Woloski:
Voici un exemple de cette technique, utilisée pour les liens d'activation.
backend
J'ai créé une API pour signer mes URL de téléchargement:
Demande:
Réponse:
Avec une URL signée, nous pouvons obtenir le fichier
Demande:
Réponse:
frontend (par jojoyuji )
De cette façon, vous pouvez tout faire en un seul clic d'utilisateur:
la source
Une alternative aux approches existantes "fetch / createObjectURL" et "download-token" déjà mentionnées est un formulaire POST standard qui cible une nouvelle fenêtre . Une fois que le navigateur a lu l'en-tête de la pièce jointe sur la réponse du serveur, il fermera le nouvel onglet et commencera le téléchargement. Cette même approche fonctionne également bien pour afficher une ressource comme un PDF dans un nouvel onglet.
Cela permet une meilleure prise en charge des navigateurs plus anciens et évite d'avoir à gérer un nouveau type de jeton. Cela aura également une meilleure prise en charge à long terme que l'authentification de base sur l'URL, car la prise en charge du nom d'utilisateur / mot de passe sur l'URL est supprimée par les navigateurs .
Du côté client, nous utilisons
target="_blank"
pour éviter la navigation même en cas d'échec, ce qui est particulièrement important pour les SPA (applications à page unique).La principale mise en garde est que la validation JWT côté serveur doit obtenir le jeton à partir des données POST et non de l'en-tête . Si votre infrastructure gère automatiquement l'accès aux gestionnaires de route à l'aide de l'en-tête Authentication, vous devrez peut-être marquer votre gestionnaire comme non authentifié / anonyme afin de pouvoir valider manuellement le JWT pour garantir une autorisation appropriée.
Le formulaire peut être créé dynamiquement et immédiatement détruit afin qu'il soit correctement nettoyé (note: cela peut être fait en JS simple, mais JQuery est utilisé ici pour plus de clarté) -
Ajoutez simplement toutes les données supplémentaires que vous devez soumettre en tant qu'entrées cachées et assurez-vous qu'elles sont ajoutées au formulaire.
la source
Je générerais des jetons à télécharger.
Dans angular, faites une demande authentifiée pour obtenir un jeton temporaire (disons une heure), puis ajoutez-le à l'url en tant que paramètre get. De cette façon, vous pouvez télécharger des fichiers comme vous le souhaitez (window.open ...)
la source
Une solution supplémentaire: utiliser l'authentification de base. Bien que cela nécessite un peu de travail sur le backend, les jetons ne seront pas visibles dans les journaux et aucune signature d'URL ne devra être implémentée.
Côté client
Un exemple d'URL pourrait être:
http://jwt:<user jwt token>@some.url/file/35/download
Exemple avec un jeton factice:
http://jwt:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwIiwibmFtZSI6IiIsImlhdCI6MH0.KsKmQOZM-jcy4l_7NFsv1lWfpH8ofniVCv75ZRQrWno@some.url/file/35/download
Vous pouvez ensuite pousser ceci dans
<a href="...">
ouwindow.open("...")
- le navigateur s'occupe du reste.Du côté serveur
La mise en œuvre ici dépend de vous et dépend de la configuration de votre serveur - ce n'est pas trop différent de l'utilisation du
?token=
paramètre de requête.En utilisant Laravel, j'ai choisi la voie la plus simple et j'ai transformé le mot de passe d'authentification de base en en-
Authorization: Bearer <...>
tête JWT , laissant le middleware d'authentification normal gérer le reste:la source