After 2 days of trial and error, here is a workaround. Hopefully there will be an official solution to request interception soon.
# In my layout, I attached a turbo controller to the `body` element.
# <b>turbo:before-fetch-response</b> is one of the two events I found working in turbo frames.
# The other one is <b>turbo:before-fetch-request</b>
body data-controller="unauthorized" data-action="turbo:before-fetch-response->unauthorized#intercept"
= yield
// in unauthorized_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
intercept(event) {
const {url} = event.detail.fetchResponse.response;
// I was not able to intercept the 401 response, only the one after which.
// This is response is a 200 response. I had to use the url to match.
// This method is sufficient for my project, but not enough to be an general solution
if (url.match("runtime/project_users/projects/\\d+/sign_in")) {
event.preventDefault()
// Here you may ask, "shouldn't we redirect to the url in the url variable above?"
// The reason being, devise stores the location before signing out by default,
// to redirect the user back after a successful authorization. The url before signing out
// here, is the turbo frame request, which is part of the the current page, therefore may not
// an ideal target for the user to visit. The current page, which is the container of the turbo
// frame, is a more appropriate candidate.
window.location = window.location.href
}
}
}