htmx
htmx copied to clipboard
loading history from server with authentication
using events htmx:confirm and htmx:configRequest, I'm adding an authorization header to the request and is working well.
However, when used with hx-push-url and hx-history="false", I noticed the XMLHttpRequest history restoration does not utilize the confirm/configRequest behavior, resulting in missing authorization header.
https://github.com/bigskysoftware/htmx/blob/7415f395b2c31d2f5f76597d425c9fbf403c9022/src/htmx.js#L3176-L3204
Is there another way to provide this authorization header in this scenario? If not, would you be open to a contribution to handle this?
Hey, would binding a listener to the existing htmx:historyCacheMiss event solve your usecase here?
As it provides a reference to the XMLHTTPRequest in its details (see xhr property in the snippet you linked), I would expect you to be able to add the headers from there. Could totally be wrong though, I never used this one.
Hope this helps!
thanks for the suggestion - would that result in a race condition as the event listener may be called after the request is sent?
@jbockle events fired by htmx run synchronously, so your listener callback will complete before htmx proceeds with its next line.
(Unless you explicitly declare an async listener of course, that htmx unfortunately doesn't support for now anyway)
Hopefully That callback works well. Just a note that ideally when using htmx you should prefer to use cookies for authentication instead of an authorization header. Normally in htmx the requests are not cross origin and here cookies are the ideal authentication method and have many advantages in ease of use and security compared to authorization headers. Authorization headers are normally used for accessing data api's from remote origins where cookies are not as easy to setup and use.
With htmx cookie based auth just works with no additional htmx changes required. Good security guide to understand how to use cookies well with htmx is https://htmx.org/essays/web-security-basics-with-htmx/
The loadHistoryFromServer() function you mention hitting is really almost equivalent to a full page reload when the history is missed even though it is handled by htmx in some situations. One issue is that all full page reloads of any url will not include the auth header so the user will be forced back to your login screen most likely. So while you can update the url with hx-push-url and maybe get it to handle sending auth header on some back actions using htmx:historyCacheMiss event listener you will not be able to handle the user doing a manual refresh without a lot of extra complexity. This is where moving to auth cookies can make things simpler.
This is related to #2486.
Even though we receive xhr in htmx:historyCacheMiss event, we cannot set headers using xhr.setRequestHeader() because htmx:historyCacheMiss is triggered before xhr.open()
function loadHistoryFromServer(path) {
const request = new XMLHttpRequest()
const details = { path, xhr: request }
// -- event is triggered before xhr.open --
triggerEvent(getDocument().body, 'htmx:historyCacheMiss', details)
request.open('GET', path, true)
// -- --
request.setRequestHeader('HX-Request', 'true')
If htmx:historyCacheMiss event triggered after xhr.open() something like below, then we can set headers in htmx:historyCacheMiss.
function loadHistoryFromServer(path) {
const request = new XMLHttpRequest()
const details = { path, xhr: request }
// -- changes --
request.open('GET', path, true)
triggerEvent(getDocument().body, 'htmx:historyCacheMiss', details)
// -- changes --
request.setRequestHeader('HX-Request', 'true')
This will be a small change and will not break anything.
/cc @1cg
Workaround to set custom headers in history navigation
document.body.addEventListener('htmx:historyCacheMiss', (event) => {
const xhr = event.detail.xhr;
xhr.addEventListener("readystatechange", function () {
if (xhr.readyState === XMLHttpRequest.OPENED) {
xhr.setRequestHeader("HEADER_NAME", "HEADER_VALUE");
}
});
});
yeah interesting workaround!
I think it would be great to move the trigger event right to the bottom just before it calls .send so then anyone consuming this event can easily change anything they need. And at the same time change it so the returning false with event.preventDefault() in the listener will stop the send happening. You can then modify the xhr with changes or do another .open which aborts it and starts it again maybe with an updated path if needed or you can preventDefault and handle the history restore in your own way. Also with the trigger event after xhr.onload set you can replace or wrap onload to perform your own response handling code if needed. I don't think HTMX will be making any more breaking changes or adding new major feature changes but supporting better extensibility via events or extensions or plugin points would still be great.
History funciton now re-written in 2.0.5 and it now supports more events and easier override as @manoharank suggested.