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.