0
votes

Using ExtJS 6.20 with a bunch of models with fixed urls to an API that look like:

Ext.define('Admin.model.Patient', {
    extend: 'Ext.data.Model',

    requires: [
        'Ext.data.proxy.Rest',
        'Ext.data.schema.Association'
    ],

    fields: [
        { name: 'id', type: 'string' },
        { name: 'mrn', type: 'string' },
        { name: 'birth_date', type: 'date', format: 'Y-m-d' },
        { name: 'sex', type: 'string' },
        { name: 'first_name', type: 'string' },
        { name: 'last_name', type: 'string' }
    ],

    proxy: {
        type: 'ajax',
        url: 'http://localhost/patientview/api/read',
        reader: {
            type: 'json',
            rootProperty: ''
        }
    }

});

The issue is that an authentication session cookie needs to be obtained from the API upon a successful login before I can connect to the API's protected resources. I am easily able to grab the session cookie from the HTTP response (and plan on stashing it in session storage), but am not sure how to pass this as payload (as extraParams?) to the proxy, especially since it's already bound to my data models when the app is launched.

FWIW, for testing the API with an authenticated cookie, I am using the cookie parameter in curl as in curl -v --cookie "session=XXX" http://0.0.0.0:5000/patientview/api/read, where XXX is the value of the session cookie.

Since the Set-Cookie header is not generally available due to it being a 'forbidden' object (see for example, how to get a cookie from an AJAX response).

It does look like there is a way to make these credentials available through subsequent requests, using the withCredentials: true keyword (see getting extjs to work with cors and cookies on chrome).

I modified my login to the API as per this:

onLoginButton: function() {

    var formPanel = this.lookupReference('loginForm'),
        form = formPanel.getForm(),
        url = 'http://localhost/login_api/';

    if (form.isValid()) {
        values = form.getValues(true);

        console.log(values);

        Ext.Ajax.request({
            method: 'POST',
            cors: true,
            withCredentials: true,
            useDefaultXhrHeader: false,
            url: url,
            params: values, //username and password
            headers: {
                'Accept': 'application/json'
            },
            disableCaching: false,

            success: function (response) {
                json = Ext.decode(response.responseText);
                test = response.getAllResponseHeaders();
                console.log('json');
                console.log(json);
                console.log(test);
                console.log(response);

                if (response.status === 201) {
                    console.log(json.items);

                }
                else {
                    Ext.MessageBox.alert('Error', response.message);
                }
            }
        });

    }

},

But, even with this, it is not working as expected. I am assuming that since third party cookies are not available in the responseHeaders and since they should be injected through use of the withCredentials keyword, that something else needs to be done.

FWIW, my ExtJS app has the path: http://localhost/test/, while the Flask/API is accessible under http://localhost/... (To eliminate any cross-domain issues, I'm running these locally on an nginx/uwsgi web/app server, which serves these up over port 80). I DO get a successful login, btw. I am just not sure how to now pass the credentials to my other API calls (e.g., my Patient model above).

NB: it works if I nest another AJAX call (as per the blog post above) to http://localhost/patientview/api/read within my first Ext.request.Ajax call's success callback, as per

success: function (response) {

    if (response.status === 201) {
       console.log(json.items);
       Ext.Ajax.request({
           url: 'http://localhost/patientview/api/read',
           method: 'GET',
           cors: true,
           useDefaultXhrHeader: false,
           withCredentials: true,
           success: function(response) {
              var result = Ext.decode(response.responseText);
              console.log(result);

           }
        });

    }
}

But, while this is proof of concept that this is feasible, it does not solve the issue of how to inject the credentials into my predefined models.

EDIT

I removed the cors and withCredentials options, since the web app and api are on same domain, etc. It did work fine. Also, I tested another function in which I am calling Ext.request.Ajax and that too worked by grabbing the credentials automagically from the browser.

It still is not working with the model proxies (which are all defined similar to that above; same protocol, same domain, same port, etc.)

Of note though, if I refresh the browser, then the data is made available to the all the different stores bound to the models with ajax proxies (which makes sense). I am wondering if there is a way to reload the stores bound to the model after I get a success on login?

My stores are bound to a model view as per:

Ext.define('Admin.view.main.MainModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.main',

    stores: {
        patients: {
            model: 'Admin.model.Patient',
            autoLoad: true
        }
    }
});

And then, for example, via data binding it is fed to a dropdown combobox, with a bind command:

bind: {
    store: '{patients}'
},

If I were to do a store reload, where exactly would this go? I am kind of new to data binding in ExtJS.

2
How does your api expect to receive the session token? In the URL of each api call?Kevin Collins
See edited comment above.horcle_buzz
Ah. It's due autoload true on the stores. Without you'll need to call store.load at the appropriate point in your controllers. Or, you could execute the auth request in an onbeforeroute, and only continue if you get a success.Kevin Collins
FYI, I just found this blog post by Sencha Dude, Mitchell Simoens: sencha.guru/2015/07/02/handling-user-session-with-routes ... it's perfect, since one of the comments was a question on how to apply this to the admin dashboard (which is the template I am using for my web app). Way cool! Thanks man!horcle_buzz

2 Answers

0
votes

To set an authorization cookie, globally, once, you can try to use

Ext.util.Cookies.set('session', token);

However, if the resource is on a different endpoint, this fails, because the cookies are saved for the endpoint the index.html is under, and are not submitted to a different endpoint. In that case, you have to check your remote API how else it may take the token.

Because of these cross-domain issues, if you were using a standardized authentication method like OAuth, the token would usually be expected as a Bearer token, which meant that you could set it once, globally, for all your Ajax calls, like this:

Ext.Ajax.setDefaultHeaders({
    'Authorization': 'Bearer ' + token,
    'Accept': 'application/json'
});
0
votes

I ended up finally using a modified version of this: ExtJS login app... it was much easier to implement than trying to manage routing behavior based on a user session. Now, all my models with ajax proxies do not load until the main view gets created, which only happens after a success callback in my AJAX authentication request.