Comment gérer les téléchargements de fichiers avec une authentification basée sur JWT?

Voici un moyen de le télécharger sur le client en utilisant l’atsortingbut de téléchargement , l’API d’ extraction et URL.createObjectURL . Vous devez extraire le fichier en utilisant votre JWT, convertir la charge utile en un blob, placer le blob dans un objectURL, définir la source d’une balise d’ancrage sur cet objectURL et cliquer sur cette objectURL en javascript.

 let anchor = document.createElement("a"); let file = 'https://www.example.com/some-file.pdf'; let headers = new Headers(); headers.append('Authorization', 'Bearer MY-TOKEN'); fetch(file, { headers }) .then(response => response.blob()) .then(blobby => { let objectUrl = window.URL.createObjectURL(blobby); anchor.href = objectUrl; anchor.download = 'some-file.pdf'; anchor.click(); window.URL.revokeObjectURL(objectUrl); }); 

La valeur de l’atsortingbut de download sera le nom de fichier éventuel. Si vous le souhaitez, vous pouvez extraire un nom de fichier voulu de l’en-tête de réponse de disposition de contenu, comme décrit dans d’autres réponses .

Technique

Sur la base de ce conseil de Matias Woloski d’Auth0, connu évangéliste JWT, je l’ai résolu en générant une demande signée avec Hawk .

Woloski citant:

La façon dont vous résolvez cela est en générant une demande signée comme le fait AWS, par exemple.

Vous avez ici 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:

 POST /api/sign Content-Type: application/json Authorization: Bearerhttps://stackoverflow.com/questions/29452031/how-to-handle-file-downloads-with-jwt-based-authentication/... {"url": "https://path.to/protected.file"} 

Réponse:

 {"url": "https://path.to/protected.file?bewit=NTUzMDYzZTQ2NDYxNzQwMGFlMDMwMDAwXDE0NTU2MzU5OThcZDBIeEplRHJLVVFRWTY0OWFFZUVEaGpMOWJlVTk2czA0cmN6UU4zZndTOD1c"} 

Avec une URL signée, nous pouvons obtenir le fichier

Demande:

 GET https://path.to/protected.file?bewit=NTUzMDYzZTQ2NDYxNzQwMGFlMDMwMDAwXDE0NTU2MzU5OThcZDBIeEplRHJLVVFRWTY0OWFFZUVEaGpMOWJlVTk2czA0cmN6UU4zZndTOD1c 

Réponse:

 Content-Type: multipart/mixed; charset="UTF-8" Content-Disposition': attachment; filename=protected.file {BLOB} 

frontend (par jojoyuji )

De cette façon, vous pouvez tout faire sur un seul utilisateur, cliquez sur:

 function clickedOnDownloadButton() { postToSignWithAuthorizationHeader({ url: 'https://path.to/protected.file' }).then(function(signed) { window.location = signed.url; }); } 

Je générerais des jetons à télécharger.

En angular, faites une demande authentifiée pour obtenir un jeton temporaire (par exemple 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 https://stackoverflow.com/questions/29452031/how-to-handle-file-downloads-with-jwt-based-authentication/…)

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:@some.url/file/35/download

Exemple avec un jeton factice:

http://jwt:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwIiwibmFtZSI6IiIsImlhdCI6MH0.KsKmQOZM-jcy4l_7NFsv1lWfpH8ofniVCv75ZRQrWno@some.url/file/35/download

Vous pouvez alors le dans ou window.open("https://stackoverflow.com/questions/29452031/how-to-handle-file-downloads-with-jwt-based-authentication/...") – le navigateur gère le rest.


Du côté serveur

La mise en œuvre dépend de vous et dépend de la configuration de votre serveur – ce n’est pas très différent de l’utilisation du paramètre ?token= query.

En utilisant Laravel, je suis passé par la voie la plus simple et j’ai transformé le mot de passe d’authentification de base en en-tête JWT Authorization: Bearer , laissant le middleware d’authentification normal gérer le rest:

 class CarryBasic { /** * @param Request $request * @param \Closure $next * @return mixed */ public function handle($request, \Closure $next) { // if no basic auth is passed, // or the user is not "jwt", // send a 401 and sortinggger the basic auth dialog if ($request->getUser() !== 'jwt') { return $this->failedBasicResponse(); } // if there _is_ basic auth passed, // and the user is JWT, // shove the password into the "Authorization: Bearer " // header and let the other middleware // handle it. $request->headers->set( 'Authorization', 'Bearer ' . $request->getPassword() ); return $next($request); } /** * Get the response for basic authentication. * * @return void * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException */ protected function failedBasicResponse() { throw new UnauthorizedHttpException('Basic', 'Invalid credentials.'); } }