1
votes

I'm having trouble getting an AJAX call to work with the Flask-Login current_user object and the @login_required decorator. I believe it has something to do with the session not being passed.

The goal is for a user to click on a link that makes an AJAX call to a route URL that will provide a SSO session ID through a SOAP service. The SOAP service requires an emp_id to be passed which can be accessed through the current_user.emp_id attribute.

I initially attempted the AJAX call without the @login_required decorator and just passing a simple message as such which returned successfully:

app.js

const ssoLinks = document.querySelectorAll('.sso');

ssoLinks.forEach((ssoLink) => {
  ssoLink.addEventListener('click', (e) => generateSSOLink(e, ssoLink.id));
});


function generateSSOLink(e, page) {
  e.preventDefault();

  fetch("/get_bpm_sso_link")
    .then(response => response.json())
    .then(data => console.log(data))
}

views.py

@app.route('/get_bpm_sso_link')
def get_bpm_sso_link():
    data = {
        'msg': 'success'
    }

    print('*'*75)
    print('SUCCESS')
    print('*'*75)

    return jsonify(data)

My next attempt was to access the emp_id in current_user:

views.py

@app.route('/get_bpm_sso_link')
def get_bpm_sso_link():
    data = {
        'msg': 'success'
    }

    print('*'*75)
    print(current_user.emp_id)
    print('*'*75)

    return jsonify(data)

Which gave me this error:

AttributeError: 'AnonymousUserMixin' object has no attribute 'emp_id'

Okay, so then I decided to try and access the same emp_id attribute on the initial index page(where the AJAX lives as well) and was able to do so. It sounds like during an AJAX request, the Flask-Login LoginManager doesn't recognize the session of the current_user. To further this claim, I added the @login_required decorator to the route and the JSON response returned a redirect to the login page.

What can I do to have Flask-Login recognize a user during an AJAX call? If not, what other libraries can handle user session management and AJAX requests seamlessly?

2
Are the index and the get_bpm_sso_link routes hosted on the same or different domain?Miguel
The same domain.laskeym13
That's really strange then. First check in the browser's JS console that the Ajax request has the session cookie header. If it doesn't, then you need to figure out why, given that it is the same domain as the index page. If it does appear in the Ajax request, then I guess it is possible that the server decides the signature is invalid for some reason, but can't imagine why. You'll need to instrument the session code in Flask to find out more.Miguel

2 Answers

2
votes

So I did some digging this week at Pycon 2018 in Cleveland and came across a noteworthy topic regarding the Fetch API. Seems by default that the API doesn't send any user credentials. To remedy this, it all came down to setting an option within the API:

app.js

function generateSSOLink(e, page) {
  e.preventDefault();

  fetch("/get_bpm_sso_link", {
    credentials: 'include'
  })
    .then(response => response.json())
    .then(data => console.log(data))
}

Now my AJAX request hits my view even with the @login_required decorator.

0
votes

What you need is to login using request loader as per this example on flask for each request

And probably disable setting the Flask Session cookie

Then Using fetch add headers with basic authentication

    fetch('/get_bpm_sso_link', { 
    method: 'get', 
    //or whichever method you are using
    headers: new Headers({
     'Authorization': 'Basic '+btoa('username:password') 
     //btoa base64 encodes your credentials, the flask example above shows how to decode
     })
 });

Ideally because the credentials are not hashed, they should be passed over https in production