AdonisJS in 15 #3.0

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.

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.