AdonisJS User Role Authentication in 15 Minutes

In this lesson, we'll learn how we can specify user roles using a User and Role AdonisJS Model within our project.

Published
Nov 10, 22
Duration
15m 43s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

In this lesson, we'll learn how we can specify user roles using a User and Role AdonisJS Model within our project. This serves as a follow-up to our earlier, viewer-requested, lesson on multi-model authentication to display how a single-model approach given a role is an easier authentication structure to work with.

👍 If You Enjoy This Video, Consider Hitting The Like Button!
🔓 Multi-Model Authentication lesson this is meant to follow-up
🥁 Subscribe To Stay Notified For New Lessons
📺 View on YouTube
👨‍💻 Find The Code For This Lesson Here

📚 Chapters:
00:00 - Introduction
00:43 - Creating & Configuring the Project
02:25 - Defining User Roles & Relationship
04:40 - Auth Controller
05:20 - Logout
05:58 - Register
07:52 - Login
09:00 - Registering Routes
09:30 - Authentication Forms
10:26 - Registering Middleware
11:00 - Testing Authentication
11:40 - Defining User Role Model Relationship
12:20 - Checking User Role
13:12 - Checking User Role Using Role Enum
14:04 - Adding IsAdmin Computed Property
14:52 - Wrapping Up


📜 Transcript:

0:00

so a couple of lessons ago we went over

0:01

how to set up multi-model authentication

0:03

Within adonisjs by creating two

0:05

different models for each of our two

0:07

different roles we had an administrator

0:08

model and a user model now that came out

0:12

of a user question that we had on how to

0:14

go about that approach but I personally

0:15

would not recommend creating your

0:17

project with that type of setup where

0:19

you need two different models for two

0:21

different types of roles or however many

0:23

rules you might need within your project

0:25

and instead using just a single user

0:28

model and rigging that up to your

0:30

authentication system and then setting

0:31

up a different role model to control

0:34

whether or not that user is an

0:36

administrator a regular old user or even

0:38

a moderator So today we're going to

0:40

start fresh from a brand new project and

0:42

go over that approach so let's go ahead

0:44

and create a new project by doing npm

0:46

init Adonis TS app at latest and let's

0:50

call this Adonis user role

0:53

authentication we'll set this up as a

0:55

web project hit OK on the name I'll skip

0:57

bslant I'll skip the webpack Encore and

1:00

let this do its thing all right once

1:01

that succeeds let's go ahead and CD into

1:03

it and let's install the dependencies

1:05

that we'll need so let's do npmi at

1:07

adonisjs lucid and at adonisjs auth and

1:11

then we'll also need phc argon2 to Hash

1:14

our passwords once that's complete let's

1:15

configure them within our project by

1:17

doing node Ace configure adonisjs and

1:19

you want to do Lucid first so I'm going

1:21

to be selecting postgres but you select

1:23

whatever database driver you'll be using

1:24

and I'll take the instructions in the

1:26

terminal I'm going to go ahead and copy

1:28

this up through the DB connection and

1:30

then we'll just cut out the MySQL stuff

1:32

once we plop it in there but before we

1:34

do that let's go ahead and create our

1:36

role model and migration so let's do

1:38

node Ace make model call this roll and

1:41

then provide hyphen M to also create a

1:43

migration for that model okay once

1:45

that's created we are good to go ahead

1:47

and do node Ace configure adonisjs auth

1:51

I'll be using Lucid we'll set this up

1:52

for web the model name for this user

1:55

will be user and let's have it go ahead

1:56

and create the migration for us as well

1:58

so with that out of the way let's go

1:59

ahead and jump into our text editor and

2:02

let's jump into our emv.ts and plop what

2:05

we had earlier copied for our Lucid

2:07

configuration into here and let's get

2:10

rid of everything in between there and

2:11

DB connection okay and then before we

2:14

move on let's set up our environment

2:15

variables so host Port should be good

2:17

the user for me is postgres the password

2:20

I believe is password and then I have a

2:23

test database I use for each of these

2:25

individual lessons like this one next

2:27

let's go ahead and Define the roles via

2:28

an enum now some people like to place

2:30

their idioms within their contracts I

2:32

like placing mine with an app so I'm

2:34

going to go ahead and create a new

2:35

folder with an app called enums and then

2:37

place the roles definition within a

2:39

roles.ts file call this enum rolls we

2:42

will have a user role as one and an

2:45

admin role as two let's go ahead and

2:48

Export default roles from there and then

2:50

let's jump into our database directory

2:52

underneath migration options go into

2:54

roles and add a name to this table so

2:56

table string name limited to 50

2:59

characters and set it to not nullable

3:01

now we're also going to want to create

3:03

those two roles that we just defined the

3:04

enums for after this migration runs so

3:07

we can do this dot defer this takes an

3:10

async callback that's provided to the

3:12

database and we can do await DB dot

3:15

table provide this dot table name to

3:17

provide it the roles and then specify

3:19

that we want to insert we can do

3:21

actually a multi-insert to provide an

3:24

array with ID set to roles we can import

3:27

our enum there use our user key to set

3:30

that to one and then name is user then

3:33

we can do that again for our admin so

3:35

roles dot admin to set that to name dot

3:39

admin now typically whenever you insert

3:41

data into your database you don't need

3:42

to specify the ID but I do want this to

3:45

be strictly bound to the enum so if my

3:48

enum and database become out of sync

3:50

with their ID values at any point I want

3:52

to be alerted of that via an error so

3:55

this would give me like a duplicate key

3:57

error if I tried to get that to where I

3:59

was trying to insert this as anything

4:00

other than one and two so that's why I'm

4:02

providing the ID in the specific use

4:04

case Okay so let's give that a save and

4:06

then jump over to our users migration

4:07

and let's rig this up with our role so

4:10

let's do table dot integer roll

4:12

underscore ID set it to unsigned since

4:14

increments columns are unsigned and

4:17

specify that this references ID in the

4:20

table rolls give that a save and we

4:22

should be good to go ahead and run our

4:23

migrations so let's do node Ace

4:26

migration and you should be able to do

4:27

run but since I'm using a test database

4:29

that I frequently use for multiple

4:31

different lessons I'm not 100 whether or

4:33

not it's empty so I'm going to run fresh

4:35

and that will just clear anything out

4:36

that's in it and then run our migrations

4:38

cool so we should be good on that front

4:39

next let's go ahead and create a

4:41

controller for our authentication so

4:43

let's do node Ace make controller off

4:46

and let's set that up so let's jump into

4:49

at controllers off and since we only

4:51

have one user model that we need to work

4:53

with and we don't need to work with a

4:54

separate role in admin model all we need

4:57

is this one controller and we can just

4:59

strictly use whatever the default guard

5:01

is which whenever we set up and

5:03

configured authentication we set the web

5:04

so we don't need to worry about

5:06

specifying the guard all we need to use

5:08

is the auth module directly so we'll

5:10

walk public async and we can do register

5:13

specify this as HTTP context contract

5:16

copy that we'll have another one for

5:18

login and then another one for log out

5:20

we'll just show all of this on a single

5:22

page so log in log out log out is

5:25

easiest so we'll start with that so all

5:26

that we'll need is response so that we

5:28

can redirect the user back and then off

5:30

so all we need to do is await auth.log

5:33

out again we don't need to worry about

5:35

the guard that we're using on the auth

5:36

module and then return response dot

5:39

redirect and then let's specifically

5:41

redirect them to path whatever the home

5:43

page is and it looks like Visual Studio

5:45

code may not be picking up that auth was

5:46

registered within this project so I'm

5:48

going to go ahead and open up the

5:49

command palette and just restart the

5:51

typescript server and after that type

5:52

script server reboot it's gone ahead and

5:55

picked it up so we're good to move it

5:56

forward so that should be log out nice

5:58

easy and done all we need to do is rig

5:59

that route up so let's go ahead and do

6:01

const data equals awaits let's grab

6:04

request response and then auth out of

6:08

there so we'll await request dot

6:11

validate schema and then we will need to

6:14

Define that schema so let's do const

6:17

user schema equals and then let's import

6:20

schema and rules from at ioc Adonis core

6:26

validator and then we can do schema dot

6:29

create we'll have an email key and we'll

6:32

do schema not string to specify that

6:34

that is a required string and then for

6:36

the rules for this let's do rules dot

6:37

email to verify that it is an email and

6:40

then we can do rules dot trim to trim

6:42

that value and then let's do password

6:44

and schema.string we'll make that

6:47

required as well and then for this we'll

6:48

do rules dot Min length of eight and

6:51

then for our schema down here on our

6:53

validation all that we need to do is rig

6:55

that up to our user schema so once we

6:57

get back that validated data we're good

6:58

to go ahead and create our user so await

7:01

user dot create provided our data and

7:04

then within our migration for our user

7:06

since we said oh we didn't

7:08

um okay let's go ahead and alter this so

7:11

let's also set this default to roles

7:14

let's import that enum and set this to

7:16

user so now anytime a user is inserted

7:18

into our database they'll default to

7:20

being set to the user role so let's give

7:22

that a save and let's rerun our

7:24

migration as well so node is migration

7:27

and refresh which will roll back and

7:29

then run it again all right so there we

7:31

go so now this user will be set up with

7:33

just the user role by default so we

7:35

don't need to worry about rigging that

7:36

up either and then since we have the

7:37

actual user record all we need to do is

7:40

await

7:41

auth.login and provided the actual user

7:44

record once that's done we can return

7:46

response.redirect and redirect them

7:48

wherever we need to for simplicity's

7:51

sake in this lesson we'll just do the

7:52

home page and then lastly for login

7:54

we'll take the requests response and

7:57

auth out of here as well and let's do

7:59

const email and password equals request

8:02

dot only we'll grab the email and

8:05

password out of there then we can try oh

8:08

wait auth dot attempt provide at the

8:11

email and the password and if that

8:13

succeeds then we will continue onward

8:14

otherwise we want to catch this you can

8:16

do something with this error if you

8:18

would like I will not be right now so

8:20

I'm just going to prefix that with an

8:22

underscore and instead actually let's

8:24

also grab session on that and we will

8:26

called set session slash errors email or

8:30

password is incorrect and then return

8:33

response dot redirect them back to that

8:37

form otherwise we can assume that that

8:39

succeeded if we don't get caught within

8:41

the catch so all that we need to do is

8:43

return response dot redirect and then

8:45

you can redirect them wherever

8:46

applicable again for simplicity's sake

8:48

we'll just do the home page here so

8:50

that's it um apart from that we need to

8:52

rig up the routes in the form but that

8:53

is it logic wise we don't need to worry

8:55

about guards it's a lot more simpler

8:57

than dealing with two different models

8:58

for one migration system so let's go

9:01

ahead and rig up those routes so we will

9:03

do route dot post slash auth slash

9:08

register point that to auth

9:10

controller.register as auth.register

9:13

route dot post

9:16

auth.login off controller.login as

9:20

off.login and then route dot get off log

9:23

out off controller dot logout as

9:27

off.lock out give that a save we can

9:29

head into our resources our welcome page

9:31

here get rid of everything within main

9:33

set up two different forms so we can do

9:35

form method equals post action equals

9:39

route auth.register we'll give this a

9:41

heading just so that we know what it is

9:43

register form set this up with an input

9:45

type email name email and placeholder

9:49

email another input type equals password

9:52

name equals password and placeholder of

9:56

password lastly a button type submit

9:59

register let's copy this form change

10:01

this to login change the post to login

10:05

button log in and let's paste that one

10:08

more time and actually let's wrap this

10:11

these two forms up in an if not

10:15

auth.user and we'll put it else then

10:17

we'll end our if so if the user is not

10:19

logged in we will have these two forms

10:21

here otherwise we'll have a link route

10:24

auth log out log out and lastly before

10:27

we actually test this out let's jump

10:28

into our start directory underneath

10:30

kernel and within our Global middleware

10:32

let's also register silent auth so let's

10:34

import app middleware silent off and

10:37

while we're here let's also register the

10:39

auth middleware so let's do off import

10:41

app middleware off and this comes

10:43

pre-configured with the adonisjs auth

10:45

system and all we need to do is register

10:47

this middleware if there's any routes

10:49

that we want to be restricted to just

10:50

authenticated users so let's give that a

10:52

save jump back into our welcome looks

10:54

like I didn't save that and then let's

10:55

boot up our server so npm run Dev and

10:59

let's open that up in our terminal and

11:01

then let's do Test Plus user gmail.com

11:04

give them some password there and looks

11:07

like that's working A-Okay we can go

11:09

ahead and log them out try logging them

11:11

in Test Plus user gmail.com so all right

11:14

so that worked there next let's say that

11:17

we need an admin so let's do Test Plus

11:18

admin gmail.com and give them a password

11:22

okay so now we're logged in as what

11:24

should be our administrator let's go

11:25

ahead and jump into to our database here

11:28

go into our users table and remember our

11:30

admin role is an ID of two so let's

11:32

switch the test admin record from an ID

11:34

of 1 to an ID of 2 commit that change

11:37

and let's go ahead and dive back into

11:39

here now before we actually try to read

11:42

whether or not the user is an admin or

11:44

regular user we're going to need to jump

11:46

into our model for our user and rig that

11:48

up so you can see we don't have anything

11:50

within here set up to the role ID so

11:52

what we'll want to do is at column

11:55

public role ID as camel case set that up

11:58

as a number scroll down and let's rig up

12:00

the relationship so let's do at belongs

12:03

to role and specify that towards the

12:06

role model public role is belongs to

12:10

type of role give that a save and now we

12:13

can jump back into our regular now we

12:15

should have done that before we even

12:16

created any users but I slipped my mind

12:20

there so now we can do at if off user

12:23

Dot and then we have that role ID to

12:25

reference off of so if that role ID

12:27

equals one we know that we have a

12:29

regular user who's not an admin if it's

12:31

two now we know that we have an admin so

12:34

let's do at if two meaning an admin and

12:37

then let's do end if so here we can do

12:39

regular user off user and let's print

12:43

out their email and then we can do admin

12:45

user auth user email give that a save

12:50

jump back into our browser give it a

12:52

refresh and here you can see we have

12:53

admin user testplus admin gmail.com just

12:56

as we would expect to make sure that

12:58

this is working let's log out and log in

13:00

as Test Plus user gmail.com log them in

13:04

and you can see we have regular user

13:06

testplususer gmail.com so that's working

13:08

just fine we're able to discern whether

13:10

or not they're an admin or regular user

13:12

next let's clean this up a little bit so

13:14

it's better to not just reference the ID

13:16

directly we can instead provide that

13:17

roles enum directly into edge here by

13:20

let's do node Ace make prld file for

13:24

preload file and let's set this up as

13:27

globals and this should really only be

13:29

applicable to http requests since it's

13:31

specific to rendering out pages so we're

13:33

going to go ahead and hit that all right

13:34

then let's go ahead and jump into that

13:35

file and let's import view from at ioc

13:40

Adonis core View and now we can do

13:44

view.global roles and set this up as our

13:48

actual roles enum so we can give that a

13:50

save jump back into our welcome page

13:51

here and now instead of doing two we can

13:54

do roles dot admin give that a save jump

13:56

back into our browser give it a refresh

13:58

and there we go so working just fine we

14:00

can log out verify that it works for our

14:02

admin as well and there you go it's

14:04

picking up the admin just fine

14:06

additionally if you wanted to simplify

14:08

that check a little bit you could go

14:10

into the user model directly so jump

14:12

into the user and we could create a

14:14

computed property so let's do this above

14:16

the relationship definition and let's do

14:18

at computed make sure that you're

14:20

grabbing the one from Adonis Lucid or

14:21

Ram there and let's do public get is

14:25

admin and then return that check back so

14:27

we can return this dot roll ID because

14:31

this is the reference to the actual user

14:34

record equals roles using our enum there

14:37

and whether or not that's equal to the

14:39

rules.admin give that a save now we can

14:41

jump back into our welcome.edge and we

14:43

don't need to reference this check at

14:44

all instead we can just do auth user dot

14:47

is admin give that a save and voila it's

14:50

still working just fine so as you can

14:52

see this single user model works a lot

14:54

better than setting up a separate model

14:57

for each role that we need within our

14:58

application and it's also a lot easier

15:00

to scale as well because all that we

15:02

need to do is add a record to our roles

15:04

table if we should need to add one later

15:06

on no changes to our auth system at all

15:09

whereas with the multi-model approach in

15:11

order to add an additional role to our

15:13

system we needed to go through the whole

15:14

nine yards by setting up a migration a

15:16

model setting up our off system to work

15:18

with it as well so this system's a lot

15:21

more scalable and I definitely recommend

15:23

using this approach if you can

Join The Discussion! (0 Comments)

Please sign in or sign up for free to join in on the dicussion.

robot comment bubble

Be the first to Comment!