Working with Callbacks
Use callbacks to implement custom functionality at key points in the auth flow.
Introduction
Many applications want to perform additional actions at specific points in the authentication flow. For example, you may want to create a user in your own database when the user is registered with Passage or include additional registration fields that Passage does not support. This guide will show you how to easily implement this type of functionality using Passage's Javascript callbacks.
Let's consider the following scenario: You have a Python Flask application that wants to know a user's email address and full name. The email address is their primary identifier for authentication and the full name will be used to customize the user's view when they log in.
Passage now supports custom user fields on registration for this specific use case! Check out our documentation for it here.
Continue following this guide for an example of how to use Passage callbacks.
At a high level you will do the following:
Create the registration input field for the "name" parameter.
Create an endpoint in your web server that creates a user in your database with a name and a Passage User ID field.
Create a Passage
onSuccess
callback that will create a user in your database by hitting the/user
endpoint whenever a user has registered with Passage.
To see a full example of a Python Flask application that implements custom user data, check out the example application on Github.
Setup
This guide starts with a simple Python Flask application. Download this Github repository and start with the 01-Login example. This will give you a basic Flask app with a single page for login and registration using the <passage-auth>
element and will be the starting point for this guide.
If you already have an application ready to go, skip to this section to start adding custom fields.
Setup your virtual environment and install dependencies.
Add your Passage App ID and API Key to the .env
file. If you aren't sure how to get your App ID or API Key, check out this page before continuing. Then confirm the application is running.
Create Separate Registration Page
The first thing you want to do is make a separate registration page, which will use the <passage-register>
element and have other fields for data you want to collect on user registration. Copy the index.html
file to a new file, called register.html
. Change the Passage Elements so that the index file has <passage-login>
and the register page has <passage-register>
.
Add a Database to your Application
For demonstration purposes, you can use the flask-sqlalchemy package to make a simple file-based database.
Create the database model for our user by creating a models.py
file and adding the following code to it.
Add the package to your __init__.py
file and configure the app to use your database and User model.
At this point you should have a working application that is ready for custom data to be added.
Adding Custom Fields to the UI
Adding the form fields to your application is pretty straightforward. Add the following HTML to the register.html
template.
Great! Now you have a form that looks like this.
Responding to the Passage Element UI
This is where it gets a little interesting. The screenshot above looks great - but once a user submits their information, the Passage Element will change its view to no longer show the email input and begin to guide users through the registration process. Passage Elements have multiple views for users depending on whether they are using biometrics or magic links to register for an account on your application. Because of this, you need to add some custom JavaScript to ensure that the name field and other message don't appear on subsequent steps of the login process.
Mutation observers can be used to respond to the updating state of the Passage Register UI. A mutation observer is a built-in feature of JavaScript that observes a DOM element and fires a callback when it detects a change. It can be used to enable changes in the browser UI based on the current state of the Passage element. In this case, you don't want to show the name field or the "Already have an account?" footer once a user has moved past the email input view.
Add the following code in a script tag just after the Passage Register element in register.html
. This code will check if the email input view is active on each mutation, and show or remove certain HTML elements depending on the answer.
The Passage Elements are web components which means the HTML that is rendered by the elements is contained in the shadowDOM of the <passage-auth>
, <passage-login>
, and <passage-register>
tags. To observe mutations in the shadowDOM of the Passage Elements you simply direct the mutation observer to observe the element's shadowRoot
property which returns the root element of the shadowDOM. Now the mutation observer can respond to any changes in the rendered UI of the Passage Element just like it would with any other HTML element.
Submitting and Storing User Data
The final step is to submit the user's name to an endpoint on your web server so that you can store it along side the Passage User ID in your database. If you haven't already, ensure that your database model includes a field for the Passage User ID. It will look like this:
In main.py
update the register
function and add the createUser
function.
Then create an onSuccess
callback that will send user data to the above endpoint once a user has been created successfully. Add the following code to a script tag just after the Passage Register element.
Conclusion
Finally, you can use this name field as intended. The goal was to use the name field to customize the dashboard, so change the template in dashboard.html
to include the following.
Then update the dashboard
function in main.py
. In this function, you have the user's Passage ID because it is authenticated through the middleware. Use Passage ID to look up the user in the database and use the Passage SDK to look up the user's information stored in Passage. You get the user's name from your own database and the user's email from Passage.
Last updated