imba-router
imba-router copied to clipboard
Creating auth workflow with imba and imba-router
Hi. I'm playing with imba-router and struggle with developing simple authentication process.
[request private page] → [controller (check the session)] → [render private page] or [redirect to sign-in page]
I've tried place conditional rendering logic in load
method but it doesn't work (method runs only once, at the first render).
Example of the code (also available at Scrimba: https://scrimba.com/c/ck2n2c3):
var user
tag Home
def render
<self> <h1> "Home"
tag SignIn
def render
<self> <h1> "Sign in page"
tag PrivatePage
def load
console.log "private.load" # runs only ones
if (user)
self
else
router.go('/sing-in')
def render
console.log "private.render"
<self> <h1> "Private page"
export tag Site
def render
<self.vbox>
<nav.main>
<a route-to='/'> 'Home'
<a route-to='/private'> 'Private page'
<label>
<input type="checkbox" :click=(do user = !user) checked=user>
<span> "Access to private page"
<Home route='/'>
<PrivatePage route='/private'>
<SignIn route="/sign-in">
Where can I place page-access logic in imba? And how you build auth workflow with imba and imba-router in general?
Thanks.
Well, I'm new to Imba and just start experimenting a few days ago. I find the load
method is only running once for a every different url. You can just use the logic for redirecting unpriviledged user on mount
method instead. So it should be like this:
tag PrivatePage
def mount
if (!user)
router.go('/sign-in')
def render
console.log "private.render"
<self> <h1> "Private page"
I am also very new to imba, but I have been doing it in my router tag's render
:
if !user
<Home route='/'>
else
<Dashboard route='/'>
I have the router output & menu in the same component, so it's very quick & simple to move routes around, and make the menu look different. I also have the login modal in the same component, so it all just works together.
I think both methods are good (as far as I can tell) depending on how your app is structured and if you want tag-pages to know about their auth-login, or a top-level (in my case <App>
) tag to handle it.
Months ago, I've created an admin panel using imba and using some structure like this:
Create a constant for determining the authentication level of user:
Auth.imba
const AuthLevel = {
1: ['visitor', 'guest', 'consumer', 'admin', 'superadmin', 'developer']
2: ['guest', 'consumer', 'admin', 'superadmin', 'developer']
3: ['consumer', 'admin', 'superadmin', 'developer']
4: ['admin', 'superadmin', 'developer']
5: ['superadmin', 'developer']
6: ['developer'],
}
Implement authlevel on Parent Page component
import {AuthLevel} from './Auth.imba'
export tag Page
prop authLevel default: 3
def checkAuth
# Redirect to another page if the auth level not meet requirement
if !AuthLevel[authLevel].includes(Store:session:user:currentRole)
router.go '/another-page'
return
# Check the authlevel in every load or mounting page
def load
self.checkAuth
def mount
self.checkAuth
Extend the Page component
LandingPage.imba
import {Page} from './Page.imba'
export tag LandingPage < Page
def load
super
# Your code
def mount
super
# Your code
DashboardPage.imba
import {Page} from './Page.imba'
export tag DashboardPage < Page
def load
super
# Your code
def mount
super
# Your code
Using them with route component
App.imba
<LandingPage route='/' authLevel=1>
<DashboardPage route='/dashboard' authLevel=4>
@rmdwirizki hope you are still around.
Tried something similar to what you did just above (with only a boolean for test).
Here is my code:
require "imba-router"
var isLogged = false
tag Page
prop login_required default: false
def checkAuth
if login_required and !isLogged
router.go '/login'
return
# Check the authlevel in every load or mounting page
def load
self.checkAuth
def mount
self.checkAuth
tag Login < Page
def render
<self>
<p> "Login you"
tag ProtectedPage < Page
def render
<self>
<p> "You whould not see this"
tag UnprotectedPage < Page
def render
<self>
<p> "You can see this"
tag Site
def render
<self>
<p><a route-to='/protected'> "protected"
<p><a route-to='/unprotected'> "unprotected"
<p><a route-to='/login'> "login"
<ProtectedPage route='/protected' login_required=true>
<UnprotectedPage route='/unprotected' login_required=false>
<Login route='/login' login_required=false>
Imba.mount <Site>
It works quite fine and the redirection works as expected. BUT, i cannot find why, if I click on te protected link, I see that it gets rendered for a few milliseconds before being replaced by the logging text. Any idea?
Yes, this is a problem I encountered as well
It seems the render
function is executed almost at the same time as the mount
. You just have to insert additional condition in render function.
Actually my workaround is something along this line: (I don't know about the sideeffect for doing this)
tag Page
def render
if !login_required or isLogged
self.subrender
tag ProtectedPage < Page
def subrender
<self>
<p> "You whould not see this"
And keep using subrender
instead of render
method for every component that extending Page
@rmdwirizki thx for the trick, will try it
@somebee any additional info / idea on why it get's rendered first and how to prevent it?
Thanks in advance