0
votes
  • I have a file, served with flask, protected with token based authentication.
  • I want to offer a download to it from an angular app
  • the token is stored in the angular session and put into the header of each $http.get or post

But when I just place a link to the flask path, the token is not added in the request header since it is no angular $http.get() but just an ordinary anchor I cannot do this (right?).

I dont want to pass the token in the url as query string parameter. How do I offer the dowload to the user? Should I first $http.get() it into angular and then tunnel it through as a file download?

Token storage after login:

$window.sessionStorage.token = results.response.user.authentication_token;

It is injected in each $http get or post:

config.headers['Authentication-Token'] = $window.sessionStorage.getItem('token');

Flask (with flask-security) part:

@app.route("/download", methods=['GET'])
@auth_token_required
def download():
    response = make_response(open('filename.ext').read())
    response.headers["Content-Disposition"] = "attachment; filename=download.ext"
    return response

How do I solve this?

1
Take a look at the solution here. Only works with HTML5 though.Christian Eichelmann
Thanks! that is a nice trick, I will write the answer soonSebastian

1 Answers

0
votes

First you get the data via $http.get()

Then there are two approaches I found:

  1. Insert the data as base64 encoded data inside the href attribute

  2. Insert the data as javascript blob and place a blob link in the href attribute

The first method was suggested by the article recommended by @Christian in the comments. I implemented it and you can find the code below. However the method made Chrome crash for larger files (csv files in this case), apparently this is a limitation of chrome. So I would recommend method 2.

Method 2

Angular (for csv files in this case, change the type: 'text/csv' accordingly for other files:

$http.get('/downloadpath').
    success(function(downloadresult){
        fileData = new Blob([downloadresult], { type: 'text/csv' }); 
        fileUrl = URL.createObjectURL(fileData);
        $scope.download_href = fileUrl;
    });

HTML:

<a href="{{download_href}}" download="filename.ext">download</a>

Flask:

@app.route("/downloadpath", methods=['GET'])
@auth_token_required
def download():
    response = open('filename.ext','br').read()
    return response

And just to be complete, here is method 1:

Method 1

 $http.get('/downloadpath').
    success(function(downloadresult){
        $scope.download_href = downloadresult;
    });

And for the html the same:

<a href="{{download_href}}" download="filename.ext">download</a>

Flask (example for csv file, change the 'data:text/csv' if you have another file):

@app.route("/downloadpath", methods=['GET'])
@auth_token_required
def download():
    encoded = base64.b64encode(open('export.csv','br').read())
    response = 'data:text/csv;base64,{}'.format(str(encoded, encoding='utf-8'))
    return response

Angular requirement for both methods

You need to whitelist the blob protocol for method 2 and the data protocol for method 1:

var app = angular.module('myApp', []);

app.config( [
    '$compileProvider',
    function( $compileProvider )
    {   
        $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|data|blob):/);
    }
]);