Join me live as I begin rebuilding Jagr.Co with AdonisJS. In this livestream, we'll add a system to lock users' accounts after so many bad login attempts. We'll add in the password reset flow. And, we'll start on adding social authentication. I have some off-screen work to do on the social auth to ensure my tokens and secrets are correct and will continue this next weekend!
Transcript
Looking for something specific? Feel free to search the page for keywords to find a specific portion of the live stream that interests you most.
okay let's try this again yeah there we go this time it seems like it's working
all right that was weird um so let me go back
and
let's see because yeah now that one is a live replay and it's just me talking about how the stream wasn't starting so
let me delete that one it's gone and now we're
live and now i need to retweet out the new link my goodness
oh my goodness let's see
okay sorry about that all right
so as i attempted to say the first time um before i accidentally had my
environment variables up which is not good that's not the right thing um
kind of have a brief agenda for today's stream so first and foremost i want to fix the course so that it's working and
then get the course field added into all the existing forms and then we'll be working on social
authentication and then bad auth attempt lockout so if a user tries to lock in or log in and they get their password wrong
so many times it kind of forces the user to need to reset their password and then we need to have in order for
that to kind of be feasible a password reset flow and then we'll add an auth and roll
check to studio i'm not sure if we're going to get to all of that today but at least it gives us enough to work with
um so let's go ahead and get started with the course
um so let's see so
last stream i was having an issue where i was trying to add in the csrf field
and it was saying that it wasn't actually available so here i can recreate it really quick so if we go
back to the auth and sign in and we add in
csrf field um also i did some ui updates so this is what the
it's now got a header it kind of looks giant with my screen scale but bear with me here but so if i try to go to log in
here i was getting the csrf field is not a function so the issue there is that it's not a function that's registered
because i did not have the ioc adonis add-ons shield middleware set
within my kernel.ts so that needs to be added into the global middleware there
so as soon as i save that come back in refresh now that csrf field is valid and
there and i think i need to move this down just a smidge there we go
and so now if i try to log in with a user
it works so that middleware is checking that csrf field making sure that the csr
value that's within that field is valid if it isn't kick the user out with an error otherwise
we'll get what we just had right here which is a continuation of whatever we have the logic going there
so let's go ahead and get that field added into all of our forms real quick so we just added it on the sign in
and you also see that i have a honeypot component commented out that's a custom component that i carry through project
to project i will be adding that in at some point as well i might have it in there and comment it out as a reminder
so there's the one for our sign up and then i think the only other feel that we have so far is the crete and edit for
the post so all right i did not stub it in here so
we'll just do csrf field
if i can spell and there we go
oh i got the wrong chat up sorry got the old video up
okay now i'll get the right chat
cool so that should do it for the csrf stuff we should now be a okay on that front um so
we can go ahead and move into social authentication i could go ahead and test everything but i'm fairly certain that
everything would work okay um and so this is what the
uh studio now looks like for the time being at least uh so it's got a header
and then i kind of stubbed the table so i created an actual table component uh that looks kind of like this so it
can either take a specific t head or it can take an array of columns and it will spit out
those columns for you and then it can either take uh the actual paginated data
rows or it can take a t body as well and then later on i'll be adding in a node data state as well
and then if it is paginated there's a pagination component to this as well which is
mostly just checking whether or not stuff is a page and redirecting stuff to that applicable page
um using the get urls for range as the main thing so
that's what populates the url for these little numbers here so yeah not not too much work but just a
little bit to make things a little nicer looking so yeah we can go ahead and test
let's see so testing csrf just to make sure that this is okay um
and then as i remembered last time safari requires each one of these to be defined at some
point i'll put a if it's not uh provided to default to the current
date time and then i put a wysiwyg in here it's not styled or anything so it's kind of hidden um but it's something basic
so there we go and there we go and oh i broke it
what do we got here cannot define publish that date on post model since oh i'm trying to save
all right so let's take a look at i that we left without working but
evidently not no okay so yeah i think i guess we put it
in the validator and yeah publish that date publish at time
is not an actual member of the table we could make it a member but
yes um yeah this will be open source
and the code's actually out on github github right now so
um let's see so we need to take the publish that date
publish at time out and instead mutate it into what the column is which
i think is just publish at for the actual post creation so
publish at date publish at
time and oh yeah gotta spread out the data too
okay and then
oh okay that's what i did i did it for the update but not for the uh yeah cause i thought that we had it
working so i just need to kind of copy and paste what i did for the update and then move that into a service here
later on i kind of want to focus more on the social auth stuff today so we'll just
get it going for right now so then yeah
spread in the data and then plop the publish at at the end okay
so now that should be good if i copy and pasted everything over from our publish
so let's try this one more time all right cool kept all my stuff
and there we go cool so we got our testing csrf right down here so csrf is working a okay now
okay so on to the social authentication stuff i'm going to pull up the documentation for it
i don't quite remember what the package is exactly called it was called ally um but i don't remember it's just
adonisjs ally or did i pass it i must have passed it off
social off there we go okay yeah it is just the dos.js shall we so we'll install that npm i
already plopped the environment variables in here so hopefully it won't overwrite them
there is if i can spell
configure oh what do i have um i know i got github and then i think the
other one's just google
okay so grab google because i did not
put the rules in envy.ts
and github where are you there okay
so we have that done next
we have the config which it should have created already and then we got the drivers all right so
where's my code here we go so i'm going to create
let me get this stuff cleaned up here another controller uh specifically apart
from the off controller specifically for social auth so i guess i'll name this off social
controller so we'll do node ace make configure node ace make controller
off social controller okay
and then within here let's see we need
the redirect and the callback route
sorry so the redirect is pretty straightforward i think so we got
do github redirect
have i been putting spaces after them i can't remember yes okay i'll stick with that
and we should now have ally within our http contacts contract so let me go ahead and
actually import or uncomment that out http context contract
okay is that right i think that's right
no not quite not driver use okay there we go
and then we'll have another one for the callback which
no no no for this response and off
um and then let's see
i think i'm going to extract most of the functionality for this out into a service
so oh i don't have any services yet so i'm going to create the folder okay so we'll do new
um services slash
social service i guess we'll stick with the same naming pattern so auth social service dot ts
and then i want to do another service as well that i tend to like to
extend off of let me pull that up on the side real quick just so i can take a look at it
oh i'm looking at the wrong project okay here we go
you could use a route param romances you could use a route param to know the provider that's very true i
could oh tried switching
let's see so yeah
all right i'll i will keep that in mind and alter to match that that's a good
idea thank you um so let's oh now i want to create
another one that i like to extend off of so this one is http context service
and this uses uh let's see what's the name of that
property use async local storage there we go
which allows me to get my um http context kind of in an asynchronous
manner synchronously but you know outside of the route um normal route flow
so within here and i just closed it let me pull that back up there we go so i have export default class http contact
service and this will take a protected
ctx of type http context contract
there we go all right and then for the constructor
we will do this dot ctx equals and see i also need to import http context
from that nope it's a default export
there we go httpcontext.
get or fail okay so then we'll do another get off of
this for the user return this ctx off user
and then i put some validation stuff on here as well but um we'll leave it at this for right now
and then for this export default class of social service
extends http context service
uh let's close it sorry
okay
so then we have let's see
for this we want to do
so we'll have a check for errors call that kind of takes care of this checking
and then we'll have another one to actually get the user
which
yeah i don't know how i don't know how i pieced this together but all right
so we'll have public and let's see i'm changing it up here so this will no longer be
static public async async get user
taken social provider
uh let's see i already have the http context so i don't need that
um const social equals ctx and that nope this
dot ct x dot we should have ally use
social provider and then we need to have another one to check for the errors so we'll do
private check for errors
which at that point no it doesn't need to take that in at all we'll take in the social provider though
of type google driver contract or github driver no not config
my bed driver contract there we go i don't think that imported
github driver contract and then i don't want that but instead
we'll do it if social dot
access denied this ctx
session flash error
keep it basic if social dot
mismatch this ctx
request expired please try again
if social has error
that's pretty close yeah we'll stick with that um
instead of just error to get air
okay and then if it doesn't run into any of those we'll just return true
so then up here we will do let's see we already got the use so then we will do if not this
check for errors social
return false
const and then we need to get the find or create user
so we'll add that in as well so we'll do
another private method here this one will be async find or
create user which will also take in the social of the same
contract types um and then this one also
looks like i need the social provider
which is of type key of social providers that's just auto fill i'm going to turn
that off uh
pilot toggle off there we go
okay so we'll do const user equals await social dot
no yes yes i didn't turn it off i guess i needed to click one of those
typescript okay and then
we'll do const username equals await this
oh i got another method get unique username
okay gotta do collision checking so user dot name there and then we'll add in another
private method async get unique username username
string username equals
oh i was using that same sluggify package here did i leave that installed
let's see
no it does not look like it all right let me install that again real quick
okay
so username equals and then we'll try to slogify the username
just to make sure that it's a valid username within our system const
cur well no this isn't needed is it
no it is not sorry about that
so last uh stream on
the user itself we added where is it
it should be on the username
i thought i added it oh no it's on the post it's not on the username okay
well i guess we could add it to the username too
maybe and we'll see we'll see what happens
oh let's see no i think that would be for like a different field
look at the documentation for real quick
you
well it doesn't hurt to try
so we'll see if we can tack it on to the same field
essentially what i want is if the social authenticated user
if their username is already taken and the email doesn't match then i want
the username to then be incremented via a sluggification um
so previously i was doing that manually but we'll see if we'll see if um adonai sluggify can do it for me
if not then we'll fall back to doing it manually that's no biggie where was i
here okay so i'll go ahead and type this out i
guess just in case that way we have it there equals
sluggify username const
currencies equals await database from
users where raw
username like
all right oh i started it with a change you to a double
and you to a double and what am i missing now this
where did this guy come from
0-9 there we go
okay and then here if it did everything right is where
we tack in the array with the username it says extra
yes okay and then username equals
occurrences dot length
username occurrences otherwise when that comic oh
oh that's nope yep sorry that was a hair on my screen i thought it was a comma
otherwise username okay and did i not import it no i didn't okay
i'm gonna go look and see what that name username was import
slider five from it okay so we'll we'll leave that there
just in case we need it but we'll try
using adonis slugify for this first so username would then be
just user.username in that case so i will just for right now comment that out and we will just try
uh user.name okay
and then we will want the token key so this will need the social provider
access token and then we will return
our user find or uh
first or create sorry
so we'll search for the email user dot email
squiggly is for oh
yes okay there we go
username the username
well we're gonna do that then let's just comment the whole line out let's do username user.name
and then avatar url is user dot
avatar url otherwise under defined undefined
uh sorry if i seem a little out of it um yesterday i fasted the whole day for
blood work um i ended up fasting for a full 24 hours and so
i didn't really eat until really late last night and
sleep was just a whole nother story so
might be a little out of it today
roll.user oh wait we got a squiggly here what's the squiggly
oh okay i don't know if i added the avatar url to
my user evidently not i bet you that was in a
later migration that i had not yet copied over uh we'll take a look at that here in a
second but for right now we'll just ignore this quickly uh so
i don't know did my role not import oh uh no i want the enum
right yeah import
from app enums we got one for roll yeah
all right so i'll actually name that rolling them so that i know that's not the role model but it's the role enum
um and then that will take the token key user dot
dot okay so let's take a look at what's going on with this user url uh let's see
so i think i still have my old project over here so let's take a look
migrations migrations migrations database migrations
anything in here that would state i did not really name these all that well so
bear with me here for a second
double check and make sure it wasn't a member of the initial user no
there's the auth attempts
and post add post ad post add user access tokens ah there it is
okay so i could just copy over this migration i
think i might go ahead and just tack this onto the initial user
migration and just um rerun my migrations
since we don't have any data currently that we really need to keep besides our test users which
aren't really all that important so the user here just tack these on
see i ended up not doing twitter but i'll leave it on there just in case i decide to add it in the future
um yeah so that should be good and then just need to add those to
the model so make sure i still have those copied here we'll go ahead and add those onto the model so let's see user
model
put it before they created an updated app okay
um oh what casing is that camel
there we go
oh why did i copy password
i do not know that would already be defined let's make sure that i didn't do that on the migration too go and define it twice
no reason for a user to need two different passwords yeah i did okay there we go
what i must have changed something with that right oh yep made it nullable
because the user signs up with social authentication and they don't really need a password in our system now do they so
change that from not nullable to just nullable
okay i figured there must have been a reason why password was defined in there again okay so let's go ahead and just rerun
the migrations with the sign up again but that's no big deal so node ace
migration roll back i i think everything's already just on
batch one but we'll go ahead and just specify batch zero just in case my creation run
okay so and then i have a seed don't i what did i name well just do dbc then
i'll run it asdb seed there we go
and then we will need to log back in
sign up there we go or should we just wait and test the um
oh i didn't look at that i didn't make this scrollable how did i do that
okay well we'll wait and just try using a social user
so we'll go ahead and finish that up so we have let's see did we finish
this all right now why is roll id
yeah there we go okay let's see so we never finished the
thought we finished our methods that we needed but we never finished the thought with the actual get
user here so let's finish that up so const user equals awaits
this dot find or create user for social for the social provider
and then we will return back um see previously i was doing an array with
uh the whether it was successful and then returning back the user but i think this time it would make more sense to do
is success true and then tack on the user and i
will do is success false and
do user of i don't know i guess null
so we'll try to get the user using the provided social provider we'll check if
there were any errors for their login uh if there were then we will kick it
out as unsuccessful if there was then we will try to create
finder create the user within our system so we'll try to find the first based off of the provided email for the social
authentication excuse me if it cannot be found then it will create it using the following parameters
and then we are not currently using this but we have it there just in case we end up needing it
because we're going to try to see if the adonis slugify
will take care of the username for us so the service should now be good so let's
go ahead and actually work on the controller here
so like roman said we could use a route parameter to know what the
provider is so let's see let's take a look at the routes here i'm gonna
take a look at the old project see what i was doing so you can see with the old one i had individual routes defined for github google and i never
did twitter because i couldn't get the token or i never got the token
for it but we still have google and github individually defined so
instead of individually defining it like that where's my auth at here's my auth
um by using a route parameter what we could do is instead of
manually having github we could do just um something like provider there
and provide there we go i spelt it right provider
there which kind of gets cuts back on the redundancy that i had
and then instead of having this be github redirect and github callback it would just be
just i guess redirect callback and then this would
be well i wouldn't make much sense to just have a route called redirect uh
social see we got all of the other ones prefixed with auth so let's do auth
social redirect social
callback i should really put all of the auth routes inside of a group um
so there we go we got that with the route param there really should be a matcher on there too
but we'll test it just like this first to make sure i got everything working and then let's see we named this just
redirect and then this one just callback and then the
params params.provider would be this right here so params.provider
and then let me take a look and see what i have for my callback so uh
this is what i've been referencing off to the side um is my old code base just seeing what i was doing
so off social here so for the callback i'm just going to go
ahead and copy and paste this and we'll alter it to match what we need here
so first and foremost we need our auth social service within our auth social controller
so i'm just going to go ahead and inject it
public social service
is off social service
and then instead of having that like that it would be
and then instead of this being a manual provider name it would be coming from the params
so instead of github here it'd be params.provider
and then instead of returning back an array i now have is success and user as an
object and then
why did i do this where's you even used
i'm not seeing a you i was down here so i'm doing is getting the id for that
kind of looks like it yeah okay so we'll cut that out
and if it's not successful then we'll redirect back to login uh really that should be whether
they're trying to sign up or log in then it should redirect back to the appropriate one of those but we'll change that here in a little bit
let's get everything working first um and then we did we do the profile yet
don't think we did sign up no no profiles being created yet so
we'll skip on that for right now we'll go back through and add that in
later and let's see
what do we need here argument type user null is not assignable to parameter of type user
okay so that the cast was just given over squiggly because user is not imported so there we go
cast that to a user and yeah so let's go ahead and test it um
hopefully i'm already logged into a provider so this will be a nice easy step oh
hold on a minute we got to figure out why this won't allow me to scroll because i can't even get down to the buttons um
i don't know how i did that let's see uh
all right let's take a look at that would probably be in the layout i think that i messed that up in let's take a
look no no not in the layout well maybe
just tack on overflow auto on that see what that does so let me scroll now
try here well that's not quite what i'm going for
either ah
no i'm not enough style here let's do i'm just gonna break it just to make it work um for
right now so max height 200 pixels there we go now we can get to it
okay so i'll try google first i guess so um let me go ahead and make sure that i
have those routes so yeah so i have these manually defined um
which sure um but let's go ahead and use
so let's see let's do route and then i name this off social
redirect and then the param of the provider here would be
uh what is this one google google
and then there's there's the sad little twitter commented out and then down here is
github so we'll do the same thing for github off social
redirect provider and
github okay so let's try google
nope oh social controller what do i got going on here can i find module
oh oh okay yeah no has a s on it than auth socials
i'm gonna go ahead and rename that um to auth social
okay
so let's try that again okay
oh okay that thing that i was running into again with that package let me go ahead and remove that import
and i'm just going to go ahead and comment that method out really not sure what's up with that i
haven't looked into it yet at all okay let's try this one more time
that's not the error i was hoping to see [Laughter] i don't know how to take a look at my
emv without showing it to you guys um
shucks let's see i don't have a second screen on here is the issue otherwise i'd drag
this off to the second screen
um
i'm gonna play peek-a-boo all right so here you can take a look at my old project while i take a look real
quick in my emv oh i can't scroll down far enough
all right come on you
i'm gonna make that super small so i can squeeze it down in the blank area
and where is my env here it is all right
yeah okay i never wrote so let me see if i can no i can't undo
shucks all right so i'm going to do the same thing with this screen
this is oh i can't make this one super small shoot
fudge well i tried um
all right well all right let me put over my other screen here my desktop and take a look and see how long these are maybe
i could just type it out real quick ah that's not too bad okay
github client id
okay there's the github client id github client secret
i don't know i'm telling you what i'm typing in
i really hope i type that in right oh google you're killing me
it's long
chances i get this right pretty slim but we'll try
okay
okay so here's the hoping that i got that right let me expand this back out
and let's try that again
refresh just to make sure fingers crossed
oh my gosh all right ah
all right why am i unauthorized
so let's see
first and foremost is this a me error is this something i'm throwing i don't remember typing
no i've just got to redirect back so if it was a me thing then we would have just went back to the login page
um unless i don't recall putting it and i'm not
throwing any errors in here it's just all session stuff okay
so i did could be
that i either got the id wrong i don't
see so we got the call back if we're getting to the callback i would
think that the id and secret would have been all right i didn't pay any attention to see what it was
trying to log me in as um uh let's see sign up let me try
the other one um github missing token
missing invalid oauth 2 response missing auth token
social user
so that is throwing no not in here in the service
right here
missing access token so let me take a look at my old project
real quick
be in service be in the service where's the service here's the services
okay
all right i don't think i'm doing anything all right typed anything in wrong there
all right so let's i guess make sure that the um providers coming in is github okay
so let's see we did not get to the callback at all on oh we did we got to call back on github
okay well yeah okay yeah because it got to the get user call
um well yeah okay so yeah the params would not be there no it
would it would so console.log
params make sure that we got that um and within the get user
let's follow that flow use provider social provider
which would be that string let me make sure that that's right
ally used and then it would be the provider name which gives you back what i have stored is social
which then you could do github.user or in my case social.user to get the user
so use so that should be github so let's go ahead and test to make sure that we are
actually getting the provider back we should be but for sanity's sake
hit that okay check this let me scroll down to the bottom i don't
know how i accidentally scrolled all the way up to the top uh params provider github
okay so that is coming through right am i passing it in right
params provider yep okay
yeah so we'll see what social has that
sorry um
my keys so we won't take a look at that after
all
i'm gonna have to go and clear out my um social keys after this stream
um all right let's see so without taking a look at
that then what could we do so we've got our finder create user
here
kind of at a loss at how to debug this without showing my tokens again
um maybe i will take a look at this off stream and come back to it next week
like i did with the other thing um and we'll just move forward
as opposed to wasting too much time trying to figure out stuff that i can't currently take a look at without showing
you guys too that needs to be a secret um so let's see what was next
bad off attempts that's something that doesn't deal with any tokens so i don't think
um don't think i have that
in here no so let me take a look and see what i had for the migration for that um
bad attempts there you are so i'll go ahead and just copy this and we'll make a new migration and all that
for it and
let's see what did i actually have that table called it was just bad attempt off attempts oftentimes to ace make
model off attempts of off attempt
and we will want a migration and we don't need a controller because that'll just be handled with the actual
auth attempt so there we go
and then on that migration if we get back to the new project here
database off the temps paste that in
uh don't need timestamps i already got that down there give that a save
and go ahead and copy these
and we'll plop that on the auth attempt model
only deleted that is nullable okay
so we'll do that and then i'll go ahead and tag the ordinal on deleted app just so i don't forget it
and instead of timestamp you will be a datetime and instead of an integer you will be
a number
okay and then let's see
okay so that should be good now and then we'll want a service for that oh let me go ahead and run my migration before i
forget to do that notice migration run
there we go so we got our off attempts um
and then we'll go ahead and make a service for it so we'll do off attempt service so file
off attempt
service
and i don't remember if i need http context on this at all let me take a
look nope okay
so i'm going to go ahead and place a public static property
of the allowed attempts at the top
so we'll just allow three for right now so if a user gets their password wrong three times then we'll need to go through the password reset flow
um so we'll have a pub if i can spell public static so this
will be all of these methods will be static so we won't need to instantiate a new instance of the service
async attempts take the email string will return back
a promise with a number and this will return back
uh the number of current attempts that the user has so const
what the heck did i just spell period date time dot
nail dot so within the past two days
i didn't import daytime there we go
okay so within the past two days uh so we'll get a date for two days ago
the const attempts so awaits auth
attempt query dot where
created at is greater than our
period and where email is our
provided email where
null deleted that and i'm going to go ahead and
drop those down onto separate lines there we go then just return back the
attempts well why am i doing that let's get a count
count the id um and then instead of
doing that we will do dot extras
all right so what is attempts here yes that is not what attempt is
so let's see we got the count we're getting the first record so that we could read the extras
all right i'm just going to ignore that everything's going red there try to finish this up oh gotcha optional might not exist
because i didn't do first well on that we do want it to be optional i guess right
or or zero um so we're getting a so here we're doing a query for
come on tooltips where the created at for the i collapsed it down
off attempt so we have created that that's created with the record
um too many tabs open
so we're querying where the created ad is greater than two days ago so three days ago if i got my password
wrong those won't count against you two days ago if you got your password wrong then it will so give you kind of a grace period
um and then where the email is the provided email for the attempt to call
and then we're deleted at is null and then we're going to get the count of
the number of records that come back based off of the id then we're just going to get the first one because whenever you call account
you'll kind of get back an array with a single record with the count on it or wait
uh to select two there we go
all right so we'll select just the id and then we'll count just the id and we'll get the first record that has the actual account on it
thinking about that right i think so we'll see
return back and then attempts would then be our entire number so
if extras then is empty because our first record cannot find any records
then we'll just default back to zero because if i mean user hasn't had any attempt records created
then their count would be zero and then we need to be able to get the
remaining attempts so we'll do public static async
remaining attempts
this one will also return back a number
why am i doing that so the allowed attempts
the actual attempts that the user has so that's all that that is and then we have
a helper function to delete all of the bad attempts so public static async
delete bad attempts this will take an email as well
and it will return back just a promise of type void
and we will await our off attempt
where email is our email all right i'm gonna go ahead and break that down
uh where null is deleted at
and we want to update all of those matching records
with a value of deleted display right yeah deleted that of date
time now dot to format
why not just do two sql time yeah let's just do two sql
okay so that should be good
um and then
see previously had poorly named this just login but instead what it does is it logs a login
attempt so instead of naming that just log in we will want it to public static async so
previously i had this called just logged log in but this doesn't perform an actual login
instead what would make more sense is login attempt or log login attempt or record
login yeah record log attempt email string
okay and all that this does is on the
off attempt it will create a new record for the email
and then evidently i have an enum called auth attempt purpose
which will set the purpose id um let's see if i do indeed have that auth attempt
yep purpose as
log in and then we will have another one for change email so public static
async again i'd probably name that with the action that it was just recording for so we'll
do record change email
see so this is for recording a bad authentication attempt on change email so we will do record change email
attempt because it records an attempt to change the email
author create email
purpose id off attempt purpose dot
and change email there we go all right so that's all we need for the service um
that's actually everything that the service has in it is for the actual functionality
so on login what we would want to do is record a login attempt anytime that
the user gets a login wrong and then anytime that they get their login correct we want to make sure that
the bad attempts are deleted so that if a user logs in in the morning
gets their login wrong twice and then finally logs in correct the third time i have a tendency to do that
uh that those two bad attempts don't carry over to them whenever they try to log back in later in the day should they
log out in between those times uh so within our auth controller here on not
on sign up because they won't have any ability to log in yet so if they get it wrong so we have this catch down here
is that right yes
so within our catch we will want to do and this returns back
void avoid promise um we'll want to do off attempt no not purpose
attempt service dot law record login attempt there we go
and the email so which we have destructured right here
and that would be it for the login no no we need to check it so prior to allowing
them to log in we need to check whether or not they are locked so we will do
const um login attempts
remaining wait off
attempt service dot remaining attempts should really be get
remaining attempts email i'm going to go ahead and change the name of that
okay and this one will be get attempts not just attempts
okay oh and then that service calls that so now i just invalidated that so
get attempts there we go all right and then if
login attempts remaining is less than or equal to zero
just doing the less than check just for safety session
flash air your
account has been locked due to repeated
bad login attempts please reset your
password and now that i'm thinking about it i don't really like that two-day grease period
return response redirect
to path which we don't currently have yet
but this would be the forgot password so instead i'm just going to i guess manually define that path so
forgot password so redirect them to the forgot password page if
they get that wrong too many times and then this error message will show to them on that page as well so that they
know why they got redirected but as i was saying now that i'm
thinking about it i don't really like the two day grace period that i have
here
i'll think about it i'll bump it up a 7 for now at least but i'll think about getting rid of that
yeah better safe than sorry let's just get rid of it
take date out of consideration
okay so that should be it for login um
shouldn't need to do anything on sign up because you can't really get your password wrong if you don't have a
password yet so that should be it so let's go ahead and test well we don't have any users so we have to sign up real quick
let's go sign up real quick so we'll do let's get our test user one back
okay and then now that we have that user let's log out and then we will go to the sign up and
we will get their credentials or their we'll get
let's see i did change this now because you can log in with your username so we do need to change this a little
bit from how i had it because now email here could be the email or it could be the username
so instead of just providing the email into get remaining attempts or maybe we
should probably change that to uid so that i don't fall into a trap where i assume that that's an email in the
future so let's change that to uid let's go to the
sign in page change the name for the field here to
uid and then that would also mean that we need to update the
uh value there for the flash message getter
and then do i do have a validator for this is it an actual validator is it no it's inline
so back up to the auth controller here right nope that's for sign no i don't validate
sign in so then this would just change to uid and this would change the uid
and that would change the uid and everything just changes the uid
okay and then all of the auth service stuff
or auth attempt service stuff that we just did we need to change email to uid as well so uid there
and then this would change into where equals u where email equals uid or
username equals uid um so do that here so
do a callback query here to do that query dot
where email is uid or where
user name is uid
and we will want that for the bad auth attempts as well
and is that every email reference no right here
uid uid uid
a couple more well actually for these login
um records i need to change that
actually for the change email that one will be an email because they'll be providing their actual email if i remember right
and then for the auth attempt record we'll need to change that so that it's not actually just email but maybe
maybe uid instead
okay we'll change the model for that as well so off attempt email goes to uid
and then we'll need to roll back uh just one and then re-migrate that one so node ace
migration roll back right yeah roll back
one piece migration run okay
so we still have our test user one in the database because we didn't go back to batch zero
um everything should now be changed to uid if i remember that i did everything correctly that's that's the social
service let's go take a look now i gotta update you to uid as well so we'll do uid
pretty sure that the change email attempt will actually be an email instead of the uid
but keep that in mind for the future then everything else
should be good go recheck the auth controller real quick
uid yeah okay
so then we should be able to incorrectly provide just the username now
and let's see what we got going on here column oh email does not exist that's very true
fill into that trap so i'm trying to query yeah so we could simplify that really
too for some reason i was thinking i was querying off the user
so where uid is
uid okay let's try that again
all right count from off attempts for uid is one
deleted act is known two all the time must appear and the group by clause are we used in an aggregate function
right so i got i'm assuming it's this query wrong
because that's the only one that has the count on it
so i'm gonna go tinker with that real quick uh let's open it with a tab
and no ace rebel
load models
wait no lower case models dot auth attempt
query dot see if i can copy and paste this so i'll
cut out the extras portion and just get the raw query here
see if i can paste this in without it breaking yeah that broke it i think
let me go back into it
okay um so await models
auth attempt query where uid is
and then we provided test user one there where null
deleted app select the id
and we'll try to do the whole query without the first see what we get
now shoot didn't call query as a function
okay and yeah we're getting the same error
so let me take the count off see what we get empty array
and let me take the select off and put the count back on
i think that's what i meant to do so let's try doing what i had in there
so extras um
hold on a minute before we do that let's tack the first back on
there we go okay so now let's put it back in parentheses and then do our
extras let me leave off the count and there's our count
cool cool cool so take the select off i was overthinking that and now we should be good
is that still yeah that's still nullable let's count though so it should be there so we should be able to do first or fail
okay one of those instances where ruffle
comes in super handy because you can just jump in there and see exactly what you're getting back and just tinker around with
stuff so let's try this one more time should work this time
there we go friday username email password is incorrect let me jump into
we go here instead of users i want auth attempts i think i got
to refresh the hole yeah there we go whoops whoops whoops there we go and there we
go we got our auth attempt for the uid test user one the purpose is one which
would be um log in it's not deleted
and it was created today at three o'clock
which is the current time so all that looks good um then we need to
just get it wrong a couple more times and verify that we actually get logged out or we could get it right verify that it
clears and deletes and then yeah let's go and do that test user one
get it correct so now this should be deleted no i didn't add that in did i
nope sure did not so this should not be deleted because we didn't add it in yet we gotta go do that
um and that's why you test
so within our sign in just after the attempt call we will also do await
auth attempt service and this is where we want to do what what did i name it
remove delete delete bad attempts and we provided that uid
and we'll delete all of the bad attempts for that uid so now let's head back
log out log back in
and now this deleted that should be populated and there it is
okay so now if we log out
uh go to log back in get the password wrong again
wrong again we should have two so there should be our two marker
so we should be able to get it wrong one more time
and now if we get it wrong again we should be log locked out
so that kind of just verifies that that one's not in play
so let's try that again test user one
yeah so we got locked out because it tried to redirect us to the forgot password which is
right within this block right here
which takes us to the uh forgot password flow to get drink real quick sorry i can't
help you with that on apple watch
okay sorry about that okay so that takes to the
forgot password flow um so we're gonna need pages for that
um i don't exactly remember remember the naming schema that
i got for that i think it's forgot password for the actual form to enter your password in which would
then give you the signed url in your email with a
link that you can then go to to go to the actual reset password page
so these right here i'm going to go ahead and copy and paste these just so i don't
forget a step here
and i need to take that honeypot middleware off of this guy so i'm just going to comment that off so
don't forget to put it back on
okay and then we need a forgot password page to render the forgot password that
page so with an auth controller or we could do another controller for it actually
keep the auth controller nice and super clean let's do that
so i'm going to exit oh it's dot exit in that
come back over here and let's do node ace make controller
password reset controller i should have passed in the exact flag
kind of like the authentication based stuff to be singular okay so
what was it we need public say sync async there we go uh reset password i think it was was the first
one which would then take in the view
so return view render um and where do i want to put this
so we have resources views put it off that would be okay
although we have this in a separate folder so maybe we'd put it in password
let's do off password there we go passwords password
password reset and that will be the form for that so
we'll go ahead and create that page
and i'm just going to copy the sign in
reset your password
remember your
okay and then we already have the csrf field
the heck is this from forward oh
yeah okay yeah sorry that's to redirect the user back to where they were
uh after login we'll take care of that later i forgot i put that in there sorry we don't need that for here um
so we can get rid of it and we'll keep the errors and then all we need is the
email and we don't need this
and uh what's the button for this usually say yeah we'll go with what i thought i
turned oh i turned it off for typescript okay let's just say i thought it turned co-pilot off
um and then there will be no social auth for this because you would just go to that social provider if you forgot your password for
that provider okay so that should be it for the page render there
did i save the routes i did not so we're going to go ahead and save that and actually this needs to change from off
controller to password reset controller
and really we could simplify this since we put it in some controller now from forgot password to
yeah we'll be specific with it for right now i'll change my mind about that later um okay so let's verify that we can
actually see that page so really just refresh here missing
method forgot password on password reset controller that name something different
reset password forgot password now okay
oh okay yeah reset password is the one where you actually reset the password
forgot password is the one where you get the uh signed url
there we go okay so now we need to get the form action for this
which would be called forgot password sent oh no i don't want to print
so public async forgot password sent
for this one we need to request
[Music] yeah yeah
okay all right well we'll start with that and then the http context contract
okay
okay so is this an actual page then do i have that as a separate page
trying to remember what i did
let's see off yeah i guess forgot password sent would be an actual page
and then so then what do i need for the actual form submission
one should be a post forgot password oh send forgot password send
it's those minor things that you read whenever you're not fully there sorry
look async forgot password
send all right this is the one that would need to request in the response
http context contract okay
and then in this case all that this one needs is i think the view let me go take a look
just to make sure i'm remembering that correctly auth controller
scroll down forgot password renders the forgot password page sent just yeah
and then this don't mind me i'm just going to go ahead and
so this one was send okay
so we need our user we need our route
and i don't have that in there yet and looks like we also need our session
and we don't have that quite yet
okay and then we need the actual sent to page so it would be pretty much the same
thing as well it would be like the same layout and everything as reset
so copy this paste it in rename it to sent
this is the page that will tell them to check their email i'm gonna go see exactly what i wrote so password reset email sent
sure that's fine maybe check your email
instead and then i don't really know that there would be a
sub paragraph for that and we don't need a form
and i'm just going to copy and paste whatever
okay
so there is the sent page so now what we need is
no not the post controller the auth controller here no not the auth controller now the password reset controller sorry i'll get
with it eventually we need i can we can either use see i
think all that this event was doing was kicking off the email which there is
now a specific like send email later function i think so we can just like
send it straight from the function instead of taking it off to an event to do it
take a look
down to start events there's welcome
for got password
yeah okay yeah it's just kicking off the email so we're going to use the event system
or we can just kick it off here i really don't want to deal with emails
so i i'm just going to go ahead and copy the email directory
from um my old project and paste it in here i uh emails are a pain
so so for example like the password reset email it's just a full html document so
you're not missing anything for me typing this stuff out again just simple
html email that's populating using edge
and we'll go ahead and just kick that email off because i'm pretty sure that there is
we don't have the mailer installed yet so i guess that would be step one
so let's take a look at that npm and don's jazz mailer
oh mail sorry
put letters in there in my own head
and then we would configure it and that will add all of this fun stuff in
uh it might overwrite my emv variables again i already plopped those in there so if it does then we will just um
start it in this stream and fix it in the next stream because i'll plot my emv variables in in between
and then we'll do node ace configuration now just configure
is just mail to smtp
copy the smtp rules back into here scroll on down to our emv.ts
and we'll paste those in okay
so now we need to go back into our password reset controller and we should
now have a
mail i believe it's called
yeah mail so and that is imported from
dos just add-ons mail so import
mail from ioc donnis
add-ons mail
wake up computer
okay thanks john jay appreciate it
all right so now we have the mail module so we will
try to see let's see i'm pretty sure that there's like a sent later i'm not getting auto complete
no why am i not getting autocomplete
all right well then we'll just scope out the documentation
i'm pretty sure that there's like a like a asynchronous sense where i wouldn't
block the response
yeah defer here we go send later that's what it is okay
so you still use a weight and then we will do mail dot
really not sure why i must have imported it wrong import mail from ioc
adonis add-ons mail
hmm
it's probably just a visual studio code actum funky so i will excuse the red squiggly for
right now let's go take a look back at the sun later so mail.send later and it takes in
or a callback that is provided the message and then
we do message.from who we're sending from which usually i put that
in the emv um i think maybe
now i just usually have it at the top okay so message
dot from right don't have intellisense i'm going to
keep checking back to the documentation make sure i get it right
and then it would be two whoever the user's email is so if we have a user
we can get it officially email which should be correct but we'll get it directly off of the user just in
case user.email
and then subject yeah subject
uh
something like that and then html view
and then we reference the path to the edge partial that we just copy and
pasted over from my old project so let's say we got emails off and it should be password reset so
that would be emails off
password reset and i i would assume i have to pass something in there it would it'll definitely be
the signed url take a look take a look and see what else i require for that
the user okay so just a user in the signed url is all i'm using for that so we will pass just those into user
and signed url and
we can get rid of that line there now
and we can comment that one out because that one was being used by the logger
it's really irrelevant anyway i should take that out of the try right up here um
okay so i don't know whether that is let me try reloading
visual studio code maybe that will fix how do you reload it it was one of these
man it's no big deal um so we'll go ahead and try it let's see if it works
i kind of have a feeling that since the social authentication environment variables got overwritten that these will too but we'll try
so let's see uh well no i had it all going to mail trap so it
won't actually send to somebody so okay i'm gonna say i don't own test user one at gmail.com but
it'll send to mailchimp if it does work so all right
provided email username or password oh the action i never updated the action for
the page so no not post collapse it down so i
don't go to it not layouts not emails
but password reset so i still have this go into the sign in so instead we need to do auth
password reset
well i'm going to change the name
oh wait a minute yeah there i was falling into that trap again i'm calling this password reset
this forgot password so this one should be forgot
and then this one would be auth password forgot sent and then this one would be
off password forgot send and then now this one will be off password reset
and this one will be auth password reset store
okay i don't like how long the name is but but it'll work for now and then i think i referenced those names in the
actual controller too so let me take a look at that real quick
yeah so auth password forgot sent
and that should now that should be it okay
so let's go ahead and update the action now with the actual correct post route
so auth password reset send
so now that should send off to the correct action should not try to sign us in so let's
try that again oh cannot find routes off password reset
oh it's forgot isn't it keep doing that
it's a surefire sign that i named that poorly all right
missing method forgot password send on auth controller
i didn't yeah i didn't update the controller for these either
okay let's go ahead and update it for all of them
okay i'm gonna go ahead and line them up too like i have all the others
there we go
and i'll go back first
all right so we got redirected back to the same page so something went awry
let's take a look password reset controller
so it really could have been anything don't have the error logging out so
let's just console.log the error real quick
try it again and see what we get
find by expects a value received undefined oh i
copied this from login so this is uh uid not email so we need to update that
sorry about that reset yep right there so just change that from
uid to email and there we go oh hey mr uh yes this video will be
available on demand after the stream test user one
okay try this again so still no joy
definitely a different error cannot find route uh reset password
so um
see so that was the old error which means that we got to here that was the update so i changed that so
then this is the actual error so cannot find a route so where am i trying to go to the route
reset password
or is that something that's referenced on this page
it doesn't look like it just search for the word route no
so it must be something that i'm trying to get to in the controller oh yeah no it's the
make signed url um all right so what did i have called
reset password i think that would now be the off
password reset which would be the page
that the user would be taken to once clicking the email link that they are sent to the signed url link
so that would be this guy right here so yeah off password
reset okay let's try this again
slowly but surely uh why are you trying to download
i'll allow it let's see what we get sent
okay i got a file called sent
that's interesting what did i do wrong to get a file called sent
don't think i necessarily need to return from that
it's probably the render page right oh yeah i'm not doing anything with it
all right auth password
uh sent that's going to need a name change
but we'll roll with it for right now so let's try this again fingers crossed it does not download a
file again there we go okay cool so now we got a page saying check your email um
see if it actually sent uh what's the name of that service was it was it mailtrap or was
that a mail testing service
is that right i don't think that's right
smtp testing
service i guess it is mailchimp okay i guess they changed their ui up
massively it doesn't look familiar at all
yeah sure enough that's it so no we did not get the email for it
all right so let's see
it could be an env issue um i guess i'll crunch this up and just
take a peek at it real quick
yeah okay i don't think mail traps credentials are too bad to type in let
me give that a go
let's see so
pardon me for just a second
okay
not that these credentials would be massively horrible if they got out but
okay should be good now for that i did save here it
yeah i did save okay so let's try that one more time
so we'll go back to the forgot password page
send it off see if we got something
still looking like i know let's see if maybe i need to refresh yeah it's still looking like a now
so let's get rid of the scent later and it's got to be something with the fact
that auto complete doesn't want to work for this [Music]
let me double check make sure i did install and configure it correctly
so package.json adonisjs mail
let me verify that i configured it let me double check and see what all it was supposed to configure and i'll verify that those are actually there
config mail contracts mail this should be good i just did the emv ts config could be the t well that would
be the typescript portion um the actual functionality portion would be within the adonis rc within the commands
and the provider so we'll take a look at the src and make sure that it's in there make sure that i actually did run it i
didn't get the command wrong or anything yeah so there's the provider and then we
haven't used anything with the command and that wouldn't break anything with actually sending but there's the command right there so it is
configured on that portion mail
mailers smtp all right let me go ahead and take a look at the typescript
ts config.json under types it should be a downstream mail
yep it's there too so the red squiggly i really have a feeling that it's just visual studio code um
so i was kind of testing out sublime um a little bit seeing whether or not i'd
be interested in upgrading to their version four so let me try opening it up in here and seeing if this
comes up with air as well on that or whether or not autocomplete works in this
so let's see code right here
okay and then we were within app app controllers http
and we were within password reset controller
and yeah so that seems to work let's see if autocomplete works so male dot
yeah autocomplete works so the red squiggly does appear to just be a visual studio code
so that's good to know yeah don't save
so that should be working but let's go ahead and try not send later but send immediately
see whether or not we get any difference with that or i guess we could check and see
whether or not we actually get back an error or anything oh yeah okay i think right here is the
reason why it's not sending amp url is not a function
oh well if i look at the blue unable to deliver email
yeah okay so let's take a look at what i'm trying to do with app url
it's probably something that that's within the um email itself
this is going to be within here so let me see if i'm referencing app url in here yeah right there all right
so i must have a global sorry i just realized that that's
large there we go so within my old project i probably have
a global edge function defined within my provider so
let's go take a look and see what exactly i have going on there
providers providers providers this is the right project right yeah
app provider ignore all the ugliness so
okay so i have app url as an actual environment variable which i don't have currently in this project but
we could piecemeal it together so this is the main portion that it's looking for is right here
so we'll just apply that over here because i don't want to break anything with the emails i don't want to touch it right now
and we need to import this is within boot we need to also import view so that we can
actually apply the global so we'll do cost view equals uh this
can um this app container
what is it
what is it what is it what is it what is it
my goodness i gotta go take a look at one of my old tutorials and see exactly what i need to call here
nope all right um let's see which one do i cover that in
this would be one
oh use my gosh use
app uh core route so there we go we should now
have the view so we should be able to view dots global
and then app url we don't quite have i don't remember off the top of my head what all
is immediately within the environment variables so since i don't want to look on stream um
i'm just going to go ahead and hard code it fill it in later
so please forgive me and then the path i remember right should start with a slash
so that should do it okay so let's try that one more time now that we have that global variable
defined
okay that's fair okay
that's fair too oh what the heck tom come on i'm sorry
all sorts of out of it all today
donna's core route
is it not global anymore or no i don't want route i want view
that is that the right path for view yeah downloads core view there we go okay so now we can try it again
i didn't provide an email just provided a username but we'll see if it's sent
i'm not seeing anything that said that it didn't send so that's a good sign
let's go take a look nope
still looks like a no
it could be that i provided a username and not an email because it doesn't really have any emails too that would be
a use case where i need to handle that better um yeah so find by email there so if you
can't find a user it should return back something
so that should really be fined by or fail and there we go that should take care of that so we can test that make
sure that that's working so instead of appearing to succeed if we provide an email now it should
kick it yeah kicks it back i don't have a error message on this page yet we can actually should be able to
i'll take care of that later long story short if we provide an email let's see what happens
okay so now let's see
all right so that's the old error from whenever i tried to use a username and then we switched to finder fail
so down here i don't see anything saying that it didn't send yeah there we go okay so now it's sent
and we got back the reset password link so now if we go
to just copy the after portion of this
we don't have a page for this yet um but we can go ahead and take care of that real quick
so we have a whole hour before that token expires
so let's see what do we what that so i don't think we have a controller function actually rendering that out yet
no we do not what does my route say that that should be called reset password
so this is the form or this is the page that will handle that signed url and it will display
the password field and yeah it'll be just a simple password
form so we can copy
and collapse down my components we can copy the
password reset page
oh i'm looking at my old project sorry sorry about that
uh wasn't gonna say that looks like it's already done so that's the email
so we can copy the reset page is what i meant to do and
i think i need to rename this forgot don't i i've been doing that thing where i get those two mixed up i really need
to clean the name up for that so i don't do that yeah let me make more sense if that's
forgot and then these would be reset that we do so we'll go ahead since it'll be the same
page paste this one in and we'll rename this one i forgot
okay so we have our forgot page and then we just need to change the reset page too instead of displaying
the email um just be a password so i'm going to go into the sign in page
and well actually the sign up page because it needs to have
the behave similar to a sign up instead of
login and we will replace this component with that
component okay and then
i guess the button works um reset your password and get rid of this
it doesn't make much sense to have that there all right and we need to update the form
so instead of doing auth password reset or forgot set this would be just off password reset i think
yes
off password reset store okay
don't judge me i'll go back and clean those names up later they work for now all right
so there we go yeah
yeah that that'll work in which case we need to now define both
of those controller functions so we have forgot password forgot password sent
forgot password send now we need public async
reset password
and we're going to need to validate that the signature is valid
uh we can either do that in this handler or we can do it within a middleware
either way would really work
okay so there's that one i'm gonna go ahead and stub this one so i don't forget about it
um what reset password and then the other
one is for the actual mutation of the password so that would be reset password store okay
all right so i'm gonna i'm gonna cheat and take a peek and see what i was doing for these
i don't exactly remember all right so reset password and testing whether or not it has a valid
signature getting the email so that's all that i was doing there
really no no error handling or anything like that must be doing it within the view
i don't oh yeah okay yeah so i was
okay so if it's valid render the form if it's not then tell them that their link's invalid and they need to try
again that's fair that works
that's simple enough just copy and paste it
so instead of response we'll need view and params
okay and then for the actual store that would just uh
probably want to validate it too so let's see i'm going to copy and paste
this because several things going on here when i remembered them all
all right so what do we got going on here so first and foremost we're getting the email and the password so
that tells me that we also need to on our reset page add the email in as a
hidden field so let me do that up here
and just replace token with email
second that tells me that on the page that renders this we need to oh we are passing the email in so that's
fair okay
and then third we need to do an if
here if that should really be is um
signature valid
so if it's valid and we'll render the form otherwise i'm just going to copy what i had over here
which was something simple
okay and then we can get rid of that href and actually do a route here so route
auth passwords forgot to take them back to the forgot password
page so that they can start to flow over again since their token expired and
i think that should do it and then we need to finish up our controller so
let's take a look at the auth controller because we're going to want to validate that their password's valid again
so we'll do that we'll go ahead and validate the email too so instead of getting it this way
why don't we just do a full validation so we'll import
schema as schema and rules from
the validator and we don't need username
um instead of a unique check on the email instead it should be in exists
well would you want that to come back with that error that the email doesn't exist is the thing
really it never should reach that point unless the user deletes their account in between trying to reset their
password so i'll think about that more but for right now that'll work okay
so if it exists
um all right so then we just need to validate so const
email password equals await request validate
our schema um and then we'll do a user find by the
email to get the user and then we'll update their password and then the
hook that we have on save on our user we'll check whether or not the password's changed if it has it will rehash it so we don't need to worry
about that here and then we have an emission going out saying that your password has been
successfully reset something like that if you haven't reset your password click
here to undo it or something like that i think
so we'll comment that out for right now we'll take care of that here in a second because we'll use the same approach that we had for this one where we'll just
send it directly within here since there is that sent later and then we will automatically log the
user in since we know that they have access to their email so it's good enough
especially for our app where you're not dealing with anything financial so and then
clear the bad attempts out of the auth attempt service since you just reset your password
you get a clean slate there and that
is giving me a red squiggly which not sure why
oh the odd off attempt to purpose that needs to be off attempt
service those look so similar for some reason all right and then we also need our session
which we really haven't taken much thought into um displaying messages at
this point but uh we'll take care of that later on and then we don't have this is a custom
logger so i'll log that out and log that out
or um comment sorry not log and
we need auth as well so that we can actually log the user in okay
so everything here should be good so far except for sending the user a message saying your password has actually been
reset you're sending these are an email saying that your password's been reset so
this should be fairly similar to this so i'm going to copy this
we already have the user because we're doing a find buyer fail so
we can again just use this directly off of the user we don't need to worry about assigned url at this point
i am going to check and make sure the old project was still using the user there i would assume it
would be but i'm going to check let me take a look at my start events
what was that event called password reset okay
oh right here okay yeah it's just using the user and that was using the view so i'm just
gonna copy this whole line since i copy and paste it over the actual email templates
so this will render the email out using edge so it works just like edge so i'll have a user available within that
edge kind of page and then this is the actual edge page that it will render which is a full html
email so yeah that should be okay
so we can go ahead and test it out if i didn't forget anything
and then if it all works we should get another email here saying i didn't change the actual email verbiage
um your password has been
successfully reset all right so we'll get that
email actually we need to go to this page copy the
signed url okay paste that in
go to it and what do we got going on here so it cannot find the page or the edge
file password reset dot edge that's very fair because it does not exist
we change that so instead of password reset
this would be password slash reset
there we go oh no
oh yeah so we changed in the template to be expecting is signature valid instead of
just is valid so we need to also change that within the controller so is signature valid
and is signature valid now we try it again there we go now we got a password field
so same rules apply as to when you're logging in so [Music]
let's try this out all right we got logged in so at least
that portion worked uh let's verify see whether or not we got an email there we go your password's
been successfully reset there we go so that seems to be working
all right so we got the password reset flow in our application now so if we log out
run through it all again once more so we forgot our password
i'm going to say our username oh that needs to just be email
i guess that could be username or email
but uh we'll stick it to email for right now so let's see
forgot change the label to just email
change the place order to enter the email
okay i need to alter the placeholder text color on that that's horrible
test user one reset the password
get a page i'll fix the padding on that i'm saying that you're good to go check your email
we get the email it has um
our assigned url if we click on that we're taken to a page where we can now reset our password
where we can reset our password and upon successfully doing so we get logged in
so cool so let's see if we can mark that done
and we can mark mark that done
and we can mark that done and then
i think we'll stop there for today and then in time for the next stream i will fix
my uh tokens for the social authentication and i'll see whether or not i'm still getting that same error
if i am i will take a look into it and have an answer for you next week
so thank you all for watching i appreciate your support thank you for being here and have a great day
Join The Discussion! (6 Comments)
Please sign in or sign up for free to join in on the dicussion.
MrAvantiC
Hey there,
this video brought up a question to me as someone learning the framework at the moment - I hope I'm not annoying you yet. :)
While the Adonis-Documentation is very good and comprehensive, I could not find much material about how exactly services are supposed to be used. It seems like there is no "convention over configuration" when it comes to them.
Now, when testing around in my small project, I found 2 possibilities:
1. I could simply import the service where I needed it - for example in a controller - from "App/Services/MyService". Of course, this would require me to instantiate the service every time before using it, so not that nice.
2. I followed this tutorial https://medium.com/@shemsiu/ioc-container-and-dependency-injection-in-adonis-v5-216774c2a476 -> as far as I understand the essential part with this is to instantiate the service in "providers/AppProvider.ts" and register it to be used application wide. Afterwards, I was able to import it in my controller from "@ioc:Namespace/MyService".
Now, apparently there is another method "@inject" that you used in your video.
However, what is the magic behind this? Does it automatically instantiante (and bind it to the controller?) your service when you reference it in the controller's constructor?
What is the difference between this method and option 2) described above? When would you use the AppProvider?
Please sign in or sign up for free to reply
tomgobich
Hi MrAvantiC!
First off, both of the above are valid approaches to services, however, there are different instances in which you’d want to use approach 2 over 1.
Approach 1
This approach is great for services that are going to be used specifically for controllers. For example, typically if you’d have a
PostsController
you’d use aPostService
to compliment the controller. You can then either make that controller:Filled with static methods, so you don’t need to instantiate anything
Filled with non-static methods, so you need to instantiate the service to use it’s methods
Have a mix-and-match of both static and non-static depending on the method
As for
@inject
, you’re absolutely correct. So, without inject, to use a service with non-static methods it’d look something like this:However,
@inject
will instantiate and bind the service for me to my controller, so instead I can do this:This also works within services as well!
Approach 2
With approach 1, the service is instantiated for the controller for each request. Meaning, you know the service instance is specific to your user’s individual request. With approach 2, since they’re using
singleton
the service is instantiated once when the server is booted. So, a service instance is shared by all users and all their requests. This makes approach 2 great if you need to make a service that maintains connections, maintains a third-party package instance, performs actions shared by multiple users, or if it’s just your preference.Which approach you use is a personal/company preference. I personally feel more comfortable knowing the service instance is specific to a single user’s individual request. It eliminates the worry of having cross-user side effects. So, I only use approach 2 when I specifically need to have a long-lived service.
Please sign in or sign up for free to reply
MrAvantiC
Hey Tom, thanks for your response!
So, when you have methods that do not depend on the user/request/context, we could simply define these methods as
static
, import the service and use it without getting an instance of the class first, got it!However, in case I do need an instance of the service because context is required (like your
BaseHttpService
for example), what is the difference between using theAppProvider
:…and using
inject()
?Is it personal preference only again?
I get that when you need a singleton the
AppProvider
approach is more useful but what about usingbind
? This would again create one instance per request, right?Please sign in or sign up for free to reply
tomgobich
My understanding is that using
this.app.container.bind
is similar to doing:In that, if you import the service at the top-level of your controller it’ll only be instantiated once for that controller on the first request that controller handles, then that same instances will be continuously used for all requests that controller handles.
You can test this by adding the following to your AppProvider’s register method.
Then, import it within two controllers and call the
testing
method in one method on each controller.Then request the pages and refresh each page. The console output will be something like this, depending on the order you refresh.
So, it’s getting instantiated once per controller and keeping that instance for subsequent requests for that controller.
Please sign in or sign up for free to reply
MrAvantiC
I see, so basically we have:
AppProvider
+singleton()
=> One shared instance for all importsAppProvider
+bind()
=> One instance per import (e.g. controller) which will be re-used for subsequent requestsinject()
=> One instance per request which is great if you rely on request-specific data (e.g. HttpContext)Appreciate your help in understanding all of this! :)
Please sign in or sign up for free to reply
tomgobich
Exactly!! Anytime, happy to have been able to help! :)
Please sign in or sign up for free to reply