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:
- Introduction
- Creating & Configuring the Project
- Defining User Roles & Relationship
- Auth Controller
- Logout
- Register
- Login
- Registering Routes
- Authentication Forms
- Registering Middleware
- Testing Authentication
- Defining User Role Model Relationship
- Checking User Role
- Checking User Role Using Role Enum
- Adding IsAdmin Computed Property
- Wrapping Up
📜 Transcript:
so a couple of lessons ago we went over
how to set up multi-model authentication
Within adonisjs by creating two
different models for each of our two
different roles we had an administrator
model and a user model now that came out
of a user question that we had on how to
go about that approach but I personally
would not recommend creating your
project with that type of setup where
you need two different models for two
different types of roles or however many
rules you might need within your project
and instead using just a single user
model and rigging that up to your
authentication system and then setting
up a different role model to control
whether or not that user is an
administrator a regular old user or even
a moderator So today we're going to
start fresh from a brand new project and
go over that approach so let's go ahead
and create a new project by doing npm
init Adonis TS app at latest and let's
call this Adonis user role
authentication we'll set this up as a
web project hit OK on the name I'll skip
bslant I'll skip the webpack Encore and
let this do its thing all right once
that succeeds let's go ahead and CD into
it and let's install the dependencies
that we'll need so let's do npmi at
adonisjs lucid and at adonisjs auth and
then we'll also need phc argon2 to Hash
our passwords once that's complete let's
configure them within our project by
doing node Ace configure adonisjs and
you want to do Lucid first so I'm going
to be selecting postgres but you select
whatever database driver you'll be using
and I'll take the instructions in the
terminal I'm going to go ahead and copy
this up through the DB connection and
then we'll just cut out the MySQL stuff
once we plop it in there but before we
do that let's go ahead and create our
role model and migration so let's do
node Ace make model call this roll and
then provide hyphen M to also create a
migration for that model okay once
that's created we are good to go ahead
and do node Ace configure adonisjs auth
I'll be using Lucid we'll set this up
for web the model name for this user
will be user and let's have it go ahead
and create the migration for us as well
so with that out of the way let's go
ahead and jump into our text editor and
let's jump into our emv.ts and plop what
we had earlier copied for our Lucid
configuration into here and let's get
rid of everything in between there and
DB connection okay and then before we
move on let's set up our environment
variables so host Port should be good
the user for me is postgres the password
I believe is password and then I have a
test database I use for each of these
individual lessons like this one next
let's go ahead and Define the roles via
an enum now some people like to place
their idioms within their contracts I
like placing mine with an app so I'm
going to go ahead and create a new
folder with an app called enums and then
place the roles definition within a
roles.ts file call this enum rolls we
will have a user role as one and an
admin role as two let's go ahead and
Export default roles from there and then
let's jump into our database directory
underneath migration options go into
roles and add a name to this table so
table string name limited to 50
characters and set it to not nullable
now we're also going to want to create
those two roles that we just defined the
enums for after this migration runs so
we can do this dot defer this takes an
async callback that's provided to the
database and we can do await DB dot
table provide this dot table name to
provide it the roles and then specify
that we want to insert we can do
actually a multi-insert to provide an
array with ID set to roles we can import
our enum there use our user key to set
that to one and then name is user then
we can do that again for our admin so
roles dot admin to set that to name dot
admin now typically whenever you insert
data into your database you don't need
to specify the ID but I do want this to
be strictly bound to the enum so if my
enum and database become out of sync
with their ID values at any point I want
to be alerted of that via an error so
this would give me like a duplicate key
error if I tried to get that to where I
was trying to insert this as anything
other than one and two so that's why I'm
providing the ID in the specific use
case Okay so let's give that a save and
then jump over to our users migration
and let's rig this up with our role so
let's do table dot integer roll
underscore ID set it to unsigned since
increments columns are unsigned and
specify that this references ID in the
table rolls give that a save and we
should be good to go ahead and run our
migrations so let's do node Ace
migration and you should be able to do
run but since I'm using a test database
that I frequently use for multiple
different lessons I'm not 100 whether or
not it's empty so I'm going to run fresh
and that will just clear anything out
that's in it and then run our migrations
cool so we should be good on that front
next let's go ahead and create a
controller for our authentication so
let's do node Ace make controller off
and let's set that up so let's jump into
at controllers off and since we only
have one user model that we need to work
with and we don't need to work with a
separate role in admin model all we need
is this one controller and we can just
strictly use whatever the default guard
is which whenever we set up and
configured authentication we set the web
so we don't need to worry about
specifying the guard all we need to use
is the auth module directly so we'll
walk public async and we can do register
specify this as HTTP context contract
copy that we'll have another one for
login and then another one for log out
we'll just show all of this on a single
page so log in log out log out is
easiest so we'll start with that so all
that we'll need is response so that we
can redirect the user back and then off
so all we need to do is await auth.log
out again we don't need to worry about
the guard that we're using on the auth
module and then return response dot
redirect and then let's specifically
redirect them to path whatever the home
page is and it looks like Visual Studio
code may not be picking up that auth was
registered within this project so I'm
going to go ahead and open up the
command palette and just restart the
typescript server and after that type
script server reboot it's gone ahead and
picked it up so we're good to move it
forward so that should be log out nice
easy and done all we need to do is rig
that route up so let's go ahead and do
const data equals awaits let's grab
request response and then auth out of
there so we'll await request dot
validate schema and then we will need to
Define that schema so let's do const
user schema equals and then let's import
schema and rules from at ioc Adonis core
validator and then we can do schema dot
create we'll have an email key and we'll
do schema not string to specify that
that is a required string and then for
the rules for this let's do rules dot
email to verify that it is an email and
then we can do rules dot trim to trim
that value and then let's do password
and schema.string we'll make that
required as well and then for this we'll
do rules dot Min length of eight and
then for our schema down here on our
validation all that we need to do is rig
that up to our user schema so once we
get back that validated data we're good
to go ahead and create our user so await
user dot create provided our data and
then within our migration for our user
since we said oh we didn't
um okay let's go ahead and alter this so
let's also set this default to roles
let's import that enum and set this to
user so now anytime a user is inserted
into our database they'll default to
being set to the user role so let's give
that a save and let's rerun our
migration as well so node is migration
and refresh which will roll back and
then run it again all right so there we
go so now this user will be set up with
just the user role by default so we
don't need to worry about rigging that
up either and then since we have the
actual user record all we need to do is
await
auth.login and provided the actual user
record once that's done we can return
response.redirect and redirect them
wherever we need to for simplicity's
sake in this lesson we'll just do the
home page and then lastly for login
we'll take the requests response and
auth out of here as well and let's do
const email and password equals request
dot only we'll grab the email and
password out of there then we can try oh
wait auth dot attempt provide at the
email and the password and if that
succeeds then we will continue onward
otherwise we want to catch this you can
do something with this error if you
would like I will not be right now so
I'm just going to prefix that with an
underscore and instead actually let's
also grab session on that and we will
called set session slash errors email or
password is incorrect and then return
response dot redirect them back to that
form otherwise we can assume that that
succeeded if we don't get caught within
the catch so all that we need to do is
return response dot redirect and then
you can redirect them wherever
applicable again for simplicity's sake
we'll just do the home page here so
that's it um apart from that we need to
rig up the routes in the form but that
is it logic wise we don't need to worry
about guards it's a lot more simpler
than dealing with two different models
for one migration system so let's go
ahead and rig up those routes so we will
do route dot post slash auth slash
register point that to auth
controller.register as auth.register
route dot post
auth.login off controller.login as
off.login and then route dot get off log
out off controller dot logout as
off.lock out give that a save we can
head into our resources our welcome page
here get rid of everything within main
set up two different forms so we can do
form method equals post action equals
route auth.register we'll give this a
heading just so that we know what it is
register form set this up with an input
type email name email and placeholder
email another input type equals password
name equals password and placeholder of
password lastly a button type submit
register let's copy this form change
this to login change the post to login
button log in and let's paste that one
more time and actually let's wrap this
these two forms up in an if not
auth.user and we'll put it else then
we'll end our if so if the user is not
logged in we will have these two forms
here otherwise we'll have a link route
auth log out log out and lastly before
we actually test this out let's jump
into our start directory underneath
kernel and within our Global middleware
let's also register silent auth so let's
import app middleware silent off and
while we're here let's also register the
auth middleware so let's do off import
app middleware off and this comes
pre-configured with the adonisjs auth
system and all we need to do is register
this middleware if there's any routes
that we want to be restricted to just
authenticated users so let's give that a
save jump back into our welcome looks
like I didn't save that and then let's
boot up our server so npm run Dev and
let's open that up in our terminal and
then let's do Test Plus user gmail.com
give them some password there and looks
like that's working A-Okay we can go
ahead and log them out try logging them
in Test Plus user gmail.com so all right
so that worked there next let's say that
we need an admin so let's do Test Plus
admin gmail.com and give them a password
okay so now we're logged in as what
should be our administrator let's go
ahead and jump into to our database here
go into our users table and remember our
admin role is an ID of two so let's
switch the test admin record from an ID
of 1 to an ID of 2 commit that change
and let's go ahead and dive back into
here now before we actually try to read
whether or not the user is an admin or
regular user we're going to need to jump
into our model for our user and rig that
up so you can see we don't have anything
within here set up to the role ID so
what we'll want to do is at column
public role ID as camel case set that up
as a number scroll down and let's rig up
the relationship so let's do at belongs
to role and specify that towards the
role model public role is belongs to
type of role give that a save and now we
can jump back into our regular now we
should have done that before we even
created any users but I slipped my mind
there so now we can do at if off user
Dot and then we have that role ID to
reference off of so if that role ID
equals one we know that we have a
regular user who's not an admin if it's
two now we know that we have an admin so
let's do at if two meaning an admin and
then let's do end if so here we can do
regular user off user and let's print
out their email and then we can do admin
user auth user email give that a save
jump back into our browser give it a
refresh and here you can see we have
admin user testplus admin gmail.com just
as we would expect to make sure that
this is working let's log out and log in
as Test Plus user gmail.com log them in
and you can see we have regular user
testplususer gmail.com so that's working
just fine we're able to discern whether
or not they're an admin or regular user
next let's clean this up a little bit so
it's better to not just reference the ID
directly we can instead provide that
roles enum directly into edge here by
let's do node Ace make prld file for
preload file and let's set this up as
globals and this should really only be
applicable to http requests since it's
specific to rendering out pages so we're
going to go ahead and hit that all right
then let's go ahead and jump into that
file and let's import view from at ioc
Adonis core View and now we can do
view.global roles and set this up as our
actual roles enum so we can give that a
save jump back into our welcome page
here and now instead of doing two we can
do roles dot admin give that a save jump
back into our browser give it a refresh
and there we go so working just fine we
can log out verify that it works for our
admin as well and there you go it's
picking up the admin just fine
additionally if you wanted to simplify
that check a little bit you could go
into the user model directly so jump
into the user and we could create a
computed property so let's do this above
the relationship definition and let's do
at computed make sure that you're
grabbing the one from Adonis Lucid or
Ram there and let's do public get is
admin and then return that check back so
we can return this dot roll ID because
this is the reference to the actual user
record equals roles using our enum there
and whether or not that's equal to the
rules.admin give that a save now we can
jump back into our welcome.edge and we
don't need to reference this check at
all instead we can just do auth user dot
is admin give that a save and voila it's
still working just fine so as you can
see this single user model works a lot
better than setting up a separate model
for each role that we need within our
application and it's also a lot easier
to scale as well because all that we
need to do is add a record to our roles
table if we should need to add one later
on no changes to our auth system at all
whereas with the multi-model approach in
order to add an additional role to our
system we needed to go through the whole
nine yards by setting up a migration a
model setting up our off system to work
with it as well so this system's a lot
more scalable and I definitely recommend
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.
Be the first to Comment!