I'm wanting to wrap Navigation Guards around my SPA Vue3 app, hosted inside a UIBUILDER instance. My goals are:
- Nav Guards which prevent unauthorised access to fragments via redirect to either a login page if not authenticated, or a "home" page if not authorised.
- DB lookups from my NodeRED flow, to check for a valid user sessionId, and the level of authorisation (none/readOnly/readWrite) that the sessionId has to the requested resource and sub-features within the requested resource.
I intend to have granular authorisation controls on the DB side where a user inherits from a group set of permissions, but can then have per-user variations on a given permission. I've made good progress on the Flow/DB side with DB storage of hashed pwd only, datetime-based expiry of sessionId, and force pwd change on first login.
I'm open minded whether to use UibRouter vs VueRouter (vue-router), but I think my needs are pushing me toward the latter. In particular, the .beforeEach(to,from) is very attractive. I prefer the idea of denying access and re-routing (or not loading fragments), rather than loading all fragments and relying on client-side hiding of unauthorised fragments. For due diligence I've taken the UibRouter example for a spin then I've had a read through uibrouter.js (v7.4.1) source. My reading of the source suggests that the current UibRouter logic lacks the "check before routing" capability that VueRouter offers. No shade on UibRouter - just an observation of what it does/doesn't do. Hopefully this give understanding why I'm leaning toward VueRouter for my scenario/complexity.
So, with UIBUILDER wrapping a Vue3 app which is using VueRouter for Nav Guards, my question then becomes:
What is the sensible approach in implementing an async call from within a UIBUILDER-hosted VueRouter .beforeEach() to trigger a Flow-based DB lookup, such that the results from DB lookup and other Flow-based processing returns to ultimate outcome to the UIBUILDER-hosted VueRouter .beforeEach() function, for VueRouter .beforeEach() to make the final re-route?
I guess the question is rather broad, multi-facetted and probably touches on:
- Vue+UIBUILDER architecture and best practice.
- How to make async calls properly - something I'm no expert at (yet!).
- Whether the standard UIBUILDER mechanism of uibuilder.send() and uibuilder.onChange('msg', (msg)=>{}) is the correct/only way of exchanging data between the VueRouter .beforeEach() function and the Flow.
- Assuming the above point is the case, what the appropriate approach would be in uibuilder.onChange('msg', (msg)=>{}) to tie the DB go/no-go results back into the VueRouter .beforeEach() function, for VueRouter to perform the final re-route.
Aside: I consider myself to be "beginner" with Vue experience, with my experience entirely developed within UIBUILDER Vue2 templates. I'm gradually stepping into Vue3. My development style is one of "need drives me to try things", historically though trial-and-error but more recently through AI queries. My formal studies majored in AI many decades ago but my subsequent career path took a different direction. I know well enough to treat AI guidance with a grain of salt, but with separate exploration of its suggestions it also tells me things I would otherwise remain ignorant about.
With all the above in mind, my efforts have lead me to the current position. I'm currently defining router.beforeEach() with an async callback. The callback is accessing an object in my Vue app using the provide()/inject() approach. Only showing pertinent code bits for now, to keep it a bit focused.
router.beforeEach(async (to, from) => {
const getSessionObject = app.runWithContext(()=>inject('getSessionObject',({ /* Other secret sauce items of interest to me */ })));
...
try{
const sessionObject = await getSessionObject(sessionId); //Provided by the Vue app below.
//Check my results in sessionObject. Re-route as appropriate.
}
});
const app = Vue.createApp({
data() { return {
sessionId: '',
/* Other secret sauce */
}},
/* Other app stuff */
});
app.provide('getSessionObject', async (sessionId) => {
uibuilder.send({topic:'checkSession',payload:{sessionId:sessionId}});
/* ??????? How do I "pause" here and wait for a message back from the DB-lookup/Flow, the content of which will be used in the below return {} statement. ????? */
return { /* Things for the .beforeEach() NavGuard to use to decide re-routing outcome.*/};
});
app.use(router);
app.mount('#app');
Phew, that was a long question! Congrats to those still reading.