Easy Authentication in Elixir & Phoenix with the pow & pow_assent libraries

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I wanted to think about when you started a new web project maybe the project was for a client or maybe it was a side project a hackathon project or maybe a new work project no I don't know about you but I'm usually pretty excited at the beginning of a new project so why is that well for one thing it's a nice clean slate and all your prior coding sins are forgiven and it's an opportunity for you to create a solution that's hopefully better designed and engineered than previous projects you've worked on and there's all sorts of interesting things to think about and research and there's no technical debt to deal with yet and there's no pending deadlines and so on but here's the thing as soon as I start setting up the new project my excitement fades pretty quickly because in my mind project setup should take a matter of minutes but in reality project setups take much longer than I expect because well it's not something I do every day so I always have to shake off the dust do a bit of research to see if there are any new quote best practices or any new libraries I should look at and so on okay so why am I talking about this well because sometimes we make these initial setups harder than they have to be and I want to look at one particular aspect of setting up a new project that I suspect many of us do the hard way I know I have so let me ask you this how would you handle authentication in a brand new Phoenix project would you roll your own authentication system or would you primarily use a library I bet a lot of you are probably thinking authentication isn't that hard I'll just code it myself now writing your own authentication system isn't necessarily a bad idea especially as a learning exercise for new programmers or programmers that are new to elixir in Phoenix in fact it's an exercise I have my students do in my course elixir in Phoenix for beginners but I'm not sure it should be your first choice for a few different reasons let me explain I've written my own authentication systems many times and almost every single time I did it ended up taking way more time than I expected it would and I got frustrated because I just wanted to get to the interesting stuff and be done with the boilerplate code by the way it's never a good idea to write security-related code when you're annoyed and frustrate by it you're just setting yourself up for bad mistakes have you ever heard of Hofstadter's law it's a law that says things always take longer than you expect even when you take into account Hofstadter's law yeah hostages law haunts me probably because I'm not the best at estimating but that's a separate topic okay so using a good library should obviously save you some time but that's not the only reason you should consider using an authentication library I mean think about it if you're writing your own off code you're writing security related code do you want to take on all that responsibility all on your own I mean is your authentication code going to be thoroughly reviewed scrutinized tested and audited maybe but probably not also do you plan on constantly staying on top of evolving security best practices will you constantly stay on top of new security exploits that require fixes ok I don't know about you but if I can save even a small amount of time using a library and if I can limit the amount of security related responsibilities I have to take on just by using a good library well it sure seems like a win-win no I'm sure many of you have tried awful libraries that left a bad taste in your mouth or maybe they are so bad that you just ditched it and wrote your own I get it that's a crappy experience but you shouldn't let one bad experience overly influenced subsequent decisions you just need to find a good authentication library like the library we'll be looking at in this video so what library are we gonna look at well we're gonna look at the Phoenix authentication library pow in a related library pow assent in fact in this video we'll create a new Phoenix project set up authentication add some simple rap based authorization set up password resets and we'll set up social logins in other words a login with Twitter Facebook or github and we'll do it all in just a matter of minutes using the excellent POW and POW ascent libraries now before I get started I should set some expectations this video targets programmers who already know elixir in Phoenix so if you're new to elixir in Phoenix you might be able to follow along but it'll probably be a real struggle so you might need to come back to this video after you've got a better handle on the basics of elixir in Phoenix okay let's get going I'll start by creating a brand new Phoenix project but using the mix task Phoenix new and I'll name our new project task app next I'll download and install the dependencies and as soon as that's done I'll see D into our newly created task app directory and I'll set up git next I'll open up my editor Adam now before I really get started I want to point something out please don't feel like you need to take any notes on what I'm about to show you because the PAL package has excellent documentation that you can refer to after this video so just sit back and try to synthesize what's going on in the next few minutes knowing that you can always find the exact steps to follow in the docs okay the first thing I'll do is open up the mix file and I'll scroll down to the depths function and I'll add the power package as a new dependency then I'll save the file and I'll fetch our new dependency right King Mix depp's get now I want to point something out before we move on I'm gonna run mix steps compile then I'll run mix help and I want to draw your attention to these mix tasks highlighted right here all of these tasks were just added from the PAL library that we just installed and we'll be using a few of these throughout the video to help us get Pao up and running in fact the very next thing we'll do is run the Pao install task saul key mix pao install and i'll press enter okay so what just happened well this migration directory was created and this database migration file was created which will create a user's table then in the context area of the app the users directory was created and a user source file was created which contains the schema definition for our users now you see all this stuff down here this is basically a description of the configuration we need to perform to get Pao working in our new project but before we follow these instructions I want to show you something in a separate terminal window so from within this project directory I'm gonna run the mix task Phoenix routes just to show you the routes that exist before we do any Pao configuration basically there's only one route the route route shown here I just want you to make a mental note of this for the moment and we'll come back to this terminal throughout the video as we configure Pao and we'll see what new routes get added from the pal package okay so back to the configuration steps here that we need to complete I'll go ahead and highlight and copy this config snippet then I'll open up the config directory and I'll open up the config file then down near the bottom of the file I'll paste the config I just copied so what's going on with this config well basically we're telling Pao what modules use four users here and what module T is for the ecto repo here now I'll give you a quick heads-up we'll be coming back to this config file a few more times throughout the video as we add new author related features okay back in the terminal I'll highlight and copy this plug related snippet which we'll need to paste in the endpoint file between the session plug shown here and the router plug shown here so I'll open up the endpoint file which is in the lib tasks app web then I'll scroll to the bottom of the file so I can find the right place to paste the snippet we just copied which should be right here between the session plug and the router the last bit of config we need to do is in the router file so first I'll copy this line right here use pal Phoenix router then I'll open up the router file and I'll paste the snippet near the top of the file next I'll copy the scope block and I'll head back to the router and I'll paste it just before the existing scope blocks okay by adding this scope right here which includes this call to power outs we've effectively added some new routes to our app so let's go see what new routes have been added by heading back to the terminal and I'll run mix Phoenix routes again and as you can see there are several new routes that our app can handle right here these routes are for registering a new user and these routes are for signing in and signing out I'm gonna head back over to the other terminal window and I'll run the mix task ecto sub app which will both create our database as we haven't done that yet and it will run our new users migration that was automatically generated a few moments ago ok I'll go ahead and start up the Phoenix server then I'll open up the browser and I'll navigate to localhost port four thousand and we see the default home page that all new Phoenix projects include now if we wanted to we could navigate to this route right here registration slash new which would show us a functioning registration page but instead and here's what I want to do I'll modify this page that we're looking at by getting rid of this get started link I'll add some conditional logic that will either show us a sign-in in a registration link when there is no signed in user or if a user is signed in it'll show their email in the sign out link to make this change I'll open up the templates and layout directories then I'll open up the app dot HTML DX layout file next I'll get rid of this line of code here with the get started link and I'll keen the following embedded elixir tag and I'll say if there's a logged in user which you can check by calling a function in the power plug module named current user which takes the connection struct and then I'll start a do block now when this is true I'll display an li tag and I'll embed the current users email then I'll add another Li tag and all Abed a link using the link helper function the text of the link will be sign out and it'll point to this route here which I can get by calling the routes helper function named POW session path and I'll pass in the connection struct and the delete atom and lastly I'll say the method for this link should be delete which effectively allows this link to simulate the HTTP delete method next I'll say else in other words when there isn't a signed in user in which case I'll create another Li tag you know embed a link named register which will point to this route which I can generate by calling the routes helper function pal registration path passing in the connection struct and the new atom lastly I'll add one more Li tag and I'll embed a link name sign in which links to this route which we can generate by calling the routes function named pal session path passing in the connection in the new atom then I'll go ahead and close out the do block by King end okay we should be able to try this out so I'll pull up the browser again and we see the register and sign-in links right here and if I click on the register link we're taken to a page where we can register a new user cool I'll go ahead and enter my email and password a couple of times and I'll click register and it worked I'm now registered and as you can see right here I must be logged in because we see my email and a sign out link if I click on the sign out link you see that I'm logged out and of course I can sign in successfully as well now that we're able to sign in and sign out let's create something in the web app that we can lock down to only be accessible by logged in users so to do this I'll use a generator the Phoenix HTML generator to create a set of cred pages and the associated code Augean tasks for the context module name to use then task for the schema module name and I'll say the database name should be tasks next I'll say our new tasks table and schema should have a description field the type string and a completed field Aibileen okay when I run this we see a bunch of auto-generated source files here and we see that we need to add this snippet to our router file and we need to run our newly created database migration let's handle this bit of code here by opening up the router file then near the bottom of the router file I'll create a new scope which I'll pipe through the browser pipeline then on this line we need to paste in the code snippet you see here so I'll copy this and I'll paste it into our router file now I'll go ahead and apply the migration by running the mix task like don't migrate let's go take another look at the routes in the app again okay now we see all these new routes for our new task resource next I'll start up the server and I'll open up the browser and I want you to notice that I'm not currently signed in now I'll navigate to slash tasks and we see an empty list of tasks so I'll try to create a new task and as you can see it worked but the problem is I really only want to allow signed-in users to create tasks so let's fix this by adding some rudimentary route based authorization or access control okay so back in our router file I'll create a new pipeline named protected and I'll use a plug named power plug require authenticated and I'll pass along an air handler option that specifies the POW Phoenix plug air handler module okay so what do we do with this new protected pipeline well we'll just use it right here in this new scope I created it a few moments ago so instead of typing through just the browser pipeline I'll also pipe through the new protected pipeline and the way I'll handle this is by wrapping the browser atom in a list and adding the protected pipeline onto the end of the list okay so what we just did here should limit access to the tasks resources to only signed in users but let's verify this so I'll start up the server again and I'll poke the browser and notice that I'm not signed in now we'll navigate to slash tasks like I did earlier but notice what happened this time the page was redirected to the sign-in form because the page I was trying to open up can only be accessed by assigned end user all right so I'll enter my credentials and as you can see I was redirected back to the tasks index page and just for good measure I'll create a new task and as you can see it worked now this question that's probably nagging at many of you watching this video which is that you're probably wondering about these registration and sign-in pages and in particular you might be wondering where do they come from and how can you customize these pages if needed well we didn't actually create any viewer template files for these pages essentially these pages are being generated at runtime by the powell library okay but how would you go about customizing these pages you know adding your own labels CSS and so on well it's a handle this you just use a POW generator to create the view and template files which you can then customize let me show you how this is done so to create the view templates I'll key in the mix task POW Phoenix Gen templates and as you can see from the response these two view files were created and these three template files were created which again you can modify as needed I'll make one small modification to one of these templates just to demonstrate what I told you but first I need to add this line right here to the POW config so I'll copy this then in the config file I'll go ahead and paste it onto the end of the POW config next I'll make one small cosmetic change to the registration page by opening up the template file in templates pow registration aimed new HTML eex now I'll just change the h1 tag from register to create a count and I'll start up the server then I'll open up the browser and navigate to the registration page and as you can see here our change is visible okay there is one big thing that's missing from our off implementation can you think of what I'm referring to how about handling password resets when a user forgets their password well password resets isn't the feature that's automatically enabled but it is a feature you can explicitly enable so let's go do that the password reset feature in PAL is in what's called an extension and these are the extensions that are included with the pal library now the first thing you typically need to do to enable any of these extensions is to create a migration for the extension with the command POW extension ecto gen migrations then i'll add the extension option followed by the extension we want to use which in our case is pal reset password this particular extension doesn't actually modify the database so as you can see from the response no migration file was created so technically we could have skipped running this command but I wanted to show it to you because you'll need it for other extensions next I'll pull up the config file and I'll configure power to use the extension power reset password and I'll add a controller callbacks key which points to the module pal extension Phoenix controller callbacks now we need to configure the user module by opening up the user file in the directory tasks app users directory next I'll use the pal extension ecto schema and I'll pass along the extension as a list that includes the power reset password extension ok now I have to add a change set function that takes the user or change set and pipes it to the power change set function passing in the attributes then the resulting change set is piped into the power extension change set function and I'll passing the attributes to this function call as well now I'll open up the router file and I'll use pal extension Phoenix router and I'll pass along the extensions we wish to use which in this case is just the power reset password' extension next I'll scroll down to the first scope block that we added earlier in the video and at the end of the scope block I'll call the function pal extension routes which will add all the necessary routes for a handling password resets the next thing we'll do is generate views and templates for handling password resets which can be done with the following power generator pal extension Phoenix Gen templates and I'll pass in the extension option followed by the name of the extension tile reset password and as you can see from the result one view file was created and two templates so what are the new routes we just added here on this line well let's go take a look ok my terminal is getting a bit messy because my font size is so large but if I scroll up a bit you can see these new password reset routes next I'll open up the Sun in template and I'll add a link to reset the password then I'll start up the server again then I'll head over to the sign-in page and as you can see here there's a forgot password link which takes us to the reset password page where we can enter an email and click Submit actually do you think this will work or are we missing something well let's try ok we got in there and as you can see from the description here seems to be related to not an email setup in other words there's no way for our server to email a password reset link this isn't a big deal we can fix this by adding a new file to the directory task app web and I'll name the new file pal mail or DX then I'll define the task Apple web pal mail and module and in the module I'll use the pal Phoenix mailer module and I'll require the logger which we'll use in a moment here now there's two functions we need to implement in this module first we need to create a cast function which takes a map with a bunch of fields you might want to use an email that's getting sent but in our case I won't actually modify the contents of the mail message per se I'll just return a map that has a two field that's set to the users email and I'll just pass along the default email subject text and HTML the other function we need to create in this file is the process function which is passed an email map which is this map we returned here then in the body of this function you simply send the email using the libraries such as bamboo to your email service provider now for this video I won't actually be sending the email I'll just log the contents of the email and it'll take a look at what gets logged in the terminal next I'll use another power template generator by King Pao extension Phoenix Miller Gen templates and I'll provide the extension option and the name power reset password so let me ask you this what do you think this generator will create any ideas well basically this generator creates a mail or view module as well as an HTML and text template files which you can modify to customize the outbound emails this snippet right here needs to get placed in the task app web file so I'll copy it then I'll open the task app web file and I'll scroll to the end of the file and I'll paste in the snippet next I'll add a bit of config for the mailer by adding a mail or back-end key which I'll set to the pal mail and module we created just a few moments ago okay let's see if the password reset functionality works now so I'll start up the server I'll click the forgot password link then I'll enter my email but before I press enter I'll clear the terminal so we can easily see the email content that should get logged here then I'll submit the form and this time we didn't in there which is a good sign and if we peek at the terminal we see the HTML representation of the outbound email and right here we see the password reset link I'll go ahead and copy this link then in the browser I'll paste in the link and as you can see or add a reset password page so I'll go ahead and enter a new password twice and I'll submit the form and it seems to have worked and I'll verify this by entering my email and my newly changed password and it works cool okay but what if you wanted to add other sign-in options like sign in with Twitter or Facebook or github can that be done with the pal library well yeah it can't be done rather easily with the related library Powis sent let me show you how this is done first I'll open up the mix file and I'll add a new dependency for the Powis and library then I'll fetch the library by King mix steps get next I'll run the mix task palace install which will create a new migration file and it will create a new user identity schema now I'll pull up the user module and I'll use pal assent Actos schema then I'll open up the router file and I'll use pal assent Phoenix router next I'll create a new pipeline almost exactly like the browser pipeline you see here except I'll remove the protect from forgery plug as this is problematic for OAuth I'll call the new pipeline skip CSRF protection and I'll include all the plugs you just saw it in the browser pipeline now I'll create a new scope block and all pipe requests in this scope through our new skip CSRF protection pipeline and I'll add the power sent related routes by calling the following function ok let's see what new routes this function call here added alright the code I just entered added these routes here which will see an action in just a moment next I'll run our new migration to create the user identities table now I'll run the new generator to create a new view and template file so I'll run the next task power sent Phoenix Qin templates and as you can see one view file is created in one template file so you might be wondering what is this template for well when a new user registers by logging into a social connection like Twitter Facebook or github and when they're redirected back to our app we want them to enter an associated email address and this is the view and template file that collects the new users email you'll see an action in just a few moments when we test this thing out okay now we've got power sent setup well except for one thing we need to configure the social providers we want to use and in other words we need to configure the login options such as login with Twitter Facebook github and so on I'm just gonna set up one provider github but keep in mind the process is very similar for all the providers so first I'll never get to this github URL which will let me register a new application I'll give it a meaningful name and in this case I'll say the homepage URL is just local host and lastly I'll add an authorization callback URL which is localhost port 4000 slash auth slash github slash callback which is this route we just looked at a moment ago now after I register this new application we see the client ID in the client secret which we're gonna use in our apps config file in just a moment here so I'll open up our config file and I'll add the new config for power sent and I'll include the providers key which I'll set to a list then I'll include the github key which I'll also set to a list then I'll add the keyword client ID which I'll set to a string then I'll copy the client ID from github and I'll paste it in as the value next I'll add a client secret key and I'll set it to a string then I'll copy the client secret from github and paste it as the value and lastly I'll add a strategy key and i'll set its value to a cent strategy github now we need to add a link to the login and registration pages that allows users to log in with github so to handle this I'll drill into the directories templates pow registration and I'll open up the edit template then down at the bottom of the file I'll use an embedded elixir tag and I'll use a comprehension to get every link from a call to power sent Phoenix view helpers provider links passing in the connection struct then in the do block I'll create a span that includes the link you'll see the link that this creates in just a moment here next I'll add the same link to the new template and I'll add the same link in the session new template ok I'll start up the server then in the browser I'll click the register link and if you look right here you see the sign in with github link cool if I click this link and redirect it to github where I'm shown an authorization prompt I'll click the authorize button then I'm redirected to the OAuth registration page where I can add an email for my new login credential I'll enter a slightly different email address since I've already created an account with my normal email and as you can see up here it worked cool now I'll go ahead and sign out and that worked and I'll try and sign in again and that worked as well nice okay so in just a matter of minutes I was able to create a brand new Phoenix project set up authentication and I created some simple route based authorization and I added the ability to log in with github now think about how long it would take you to roll your own system that did these things yeah you probably shouldn't roll your own you should just use POW and POW ascent so if you end up using these libraries which is a pretty good option in my opinion and if you like them you should at a minimum reach out and think the primary library author Deann Schultz err for all his hard work I don't know how much time he's put into this project but I guess many hundreds of hours so please let him know you appreciate his work and additionally if you can please consider sponsoring Dan's open-source work either personally or through your employer I think open-source authors are truly underappreciated but we can change that if we can all give back just a little bit hey if you like this video please hit the subscribe button thanks for watching and I'll see you next time
Info
Channel: knowthen
Views: 11,566
Rating: 4.9331741 out of 5
Keywords: Elixir, Phoenix, Authentication, pow, pow_assent
Id: hnD0Z0LGMIk
Channel Id: undefined
Length: 26min 18sec (1578 seconds)
Published: Tue Feb 11 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.