- Its been almost a week or so learning the phoenix framework and elixir. I’ve gotten a bit of understanding on how the framework uses the MVC architecture to make building web applications easier, and how elixir’s functional programming capabilities make functional programing great again 😜
- This post will focus on how to set up Google OAuth as a third-party login option for user authentication into the app built with the phoenix web framework.
[ Assumption is that one already has a decent understanding of how both the language(elixir) and framework(phoenix) work.]
- For every phoenix project created, a
mix.exsfile is generated. Inside this file is a function
deps. This function contains all the dependencies that come already installed in the phoenix framework.
Step 1️⃣ Add Ueberauth and Google Dependencies
- We want to add a new dependency that allows for 3rd party authentication. For our app we will use
ueberauth:here is the Github repo. Also we add the specific 3rd party auth_source to use, in this case we using google oauth. The
depsfunction now has two dependencies added, as shown below in lines 14 and 15
- Also, inside the same
mix.exsfile is another function,
applicationthat executes everytime the phoenix app starts. We have add the two dependencies as well.
applicationfunction now looks like this, below
Step 2️⃣ Run command to install the dependencies
- After adding these dependencies, we then run the command in our terminal that executes and installs these dependencies in our app.
$ mix deps.get
which should show the entire list of dependencies including the ones we just added.
Step 3️⃣ Update config.exs file
- Now, we navigate to the
config.exsfile where we need to add this line of code at the end to configure the “providers” . providers are the 3rd party login apps, like Google, Github or Facebook …etc
Step 4️⃣ Create a google client ID
- Use this link to create a google api project, that will generate two values [
client_secret] that we need for our phoenix app.
- Add those two values to the
config.exsfile as below
- Once the above configuration steps are all done, now we can add
codethat shows how to create new controller that handles authentication, and also appropriate routes for signing in and out of the application.
Step 5️⃣ Add routes for signing in.
- Inside the
router.exsdefine a new “scope” that handles requests starting with this path
/auth. Add the routes for sign in as below.
- line 6. specifies a
/:providerwill be a google, or any other 3rd party login option.
AuthControlleris name of controller we will create.
:requestis a pre-defined function.
- line 7. specifies a
/:provider/callbackfor redirecting from the google api with user profile.
:callbackis a function that we will implement.
Step 6️⃣ Create user model and add database migrations.
- Assumption is, PostgreSql is already installed as a database of use. Phoenix uses
Ectofor migrations and
Reponamespace for communication to the PostgreSql database.
user.exas a model in the
modelsfolder. Code below defines a User module with schema that specifies the type of each field in the database.
- Run this command that adds a migration file to your project.
$ mix ecto.gen.migrations add_users
- This generates a migration file under the
/priv/repo/migrationsfolder. this file an empty function
change().Add the following code to that empty function that creates a table with its columns specified as in the schema.
- Finally run this command to create the actual table in the postgres database.
$ mix ecto.migrate
Step 7️⃣ Create Controller for handling Authentication.
- Inside the
controllersfolder, create a new controller file, for example,
auth_controller.exs. add logic for the callback function, and sign_in functionality. below is code that handles a user logging in with a 3rd party option, and redirection to index page of our app, plus adding user info pulled for google api into our db.
- line 2: we use pattern matching to extract the
authobject from the
- line 3: still using pattern matching to extract the
user_paramsinclude the user_token, email, and provider.
- line 4: creates
changesetobject that now contains the the values we need.
- line 6: we then pass that
changesetobject to the signin function.
signin()checks to see if that user exists in the database, if not it inserts the new user into the database, or else it just logs them back into the app.
Step 8️⃣ Create plug that checks user session:
- Once the user has been signed-in, we need to find a way to keep track of the user session. We accomplish this buy creating a plug. a plug is basically a function or module that gets a
connobject, makes a tiny modification to this object and then returns a “modified”
- In our case, we will create a module plug, since it will be used in different sections of our code where we would prefer to know if a user is logged in.
Create a new folder
plugsinside of the
controllerdirectory. then create a file inside the
plugsfolder. our module plug is
user.exdefined below. (check # comments in code for each line explanation)
- Now, inside the
router.exfile, add this module plug to the list of pipeline plugs that get executed wherever a request is sent from a user. as seen below on line 10 (line 10 contains plug we just created)
Step 9️⃣ Add sign-in button on layout_view page.
Since the sign_in and sign_out feature have to be on every page, we should put the html/css design in the generic layout page. this code below adds the link for sign in and also shows with controller function to access whenever a user clicks the sign-in link.
- line 4: checks the
connobject that contains the user details, and checks if it exits, and if so, it displays the option for the to signout.
- line 10: is for when the user is not logged(meaning the
@conn.assigns[:user]returns nil), then show them the sign in button.
Step 🔟 Add functionality for signing out
- This should easier since we already have the auth_controller, and the logic for handling sessions for a user when signed in.
- Just add this sign_out function to the auth_controller
- line 5: sets the drop key to true, thereby closing the session for user
- line 6: then redirects user to the index function of the controller with
🙌 END 🙌