imba-router icon indicating copy to clipboard operation
imba-router copied to clipboard

Creating auth workflow with imba and imba-router

Open panych opened this issue 6 years ago • 6 comments

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.

panych avatar Mar 12 '18 17:03 panych

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"

rmdwirizki avatar May 18 '18 06:05 rmdwirizki

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.

konsumer avatar Dec 03 '18 20:12 konsumer

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 avatar Dec 08 '18 08:12 rmdwirizki

@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?

florianlouvet avatar Dec 18 '18 16:12 florianlouvet

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 avatar Dec 18 '18 20:12 rmdwirizki

@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

florianlouvet avatar Dec 19 '18 07:12 florianlouvet