How to Build Passwordless Auth With Twilio Verify in PHP
Time to read:
How to Build Passwordless Auth With Twilio Verify in PHP
Typing or generating a unique password for every new account or website is a hassle. Although password managers help, passwordless auth (authentication) offers a more streamlined and secure alternative.
In this tutorial, you will learn about passwordless auth, and build a PHP app that uses Twilio Verify to implement it.
Prerequisites
Before you begin, ensure you have the following:
- A free Twilio account. Click here to create a free account if you are new to Twilio.
- PHP (ideally, version 8.5)
- A phone that can receive SMS
- Your favourite text editor or IDE (such as neovim or Visual Studio Code)
Architecture
This application will be a small API with four routes:
- A GET and a POST route with the path "/". These display the sign-in form where users can enter their mobile phone number to receive the OTP code, and validate submissions of that form. If the form successfully validates, then an OTP code will be sent to the phone number provided.
- A GET and a POST route with the path "/verify". These display a form to validate the OTP code that the user received, and validate submissions of that form. After submission of the form, the user will see if the code was valid or not.
What is passwordless auth?
Passwordless auth is an authentication method where the user does not need a password in order to log into an app or system. Rather, the user's mobile device receives a one-time code. In this authentication method, users are authenticated using other unique and more secure alternatives like one-time passwords (OTP), SMS, Passkeys, Silent Network Authentication (SNA), Voice, or email notification.
Here's a breakdown of how it works, using SMS:
- User initiation: When a user attempts to log in, instead of entering a password, they provide their phone number as their unique identifier and are redirected to a form where they can validate the OTP code that they will receive.
- OTP generation and delivery: The server initiates an OTP generation request with Twilio Verify. Twilio Verify then dispatches a unique, temporary code via SMS to the user.
- User verification: The user receives the OTP on their mobile device and input this code into the validation form.
- Code validation: Using Twilio Verify, the server verifies the entered OTP against the one that was sent, earlier. If the codes match, the user is successfully authenticated and granted access.
This approach significantly enhances security by leveraging the inherent security features of the user's mobile device as a second factor in the authentication process. It also eliminates the need for them to memorize passwords, thereby simplifying the authentication process and enhancing user convenience.
Passwordless authentication has additional advantages, including the following:
- Enhanced security: Reduces the risk of password-related breaches.
- Convenience: Eliminates the need for users to remember and manage multiple passwords.
- Reduced friction: Streamlines the login process, improving the user experience.
- Scalability: Easily scalable with Twilio's infrastructure.
Build the app
Step 1: Set up the project
Set up a new project using the Twilio / Slim Base Project, by running the following command, where you store your PHP projects:
Step 2: Install the required dependencies
Next, install the required dependencies, by running the following:
In case you're not familiar with them, here's a short description of the dependencies that you just installed:
- PHP dotenv: Loads environment variables from .env into
getenv(),$_ENVand$_SERVERautomagically. - Slim Session: Simple middleware for Slim Framework 4, that allows managing PHP
- built-in sessions
- Slim Framework Twig View: This is a Slim Framework view helper built on top of the Twig templating component.
- Twilio's PHP Helper Library: This simplifies integrating with Twilio in PHP
Now, open the project directory in your preferred text editor or IDE.
Step 3: Set the required environment variables
Dotenv files (commonly named .env) are used to store the configuration information that your app needs during development, separate from the application's code. For this tutorial, it will be your Twilio credentials (i.e., your Twilio Account SID and Auth Token) and a Verify Service SID.
In your project's top-level directory, create a file named .env. Then, in .env, add the three variables below.
With placeholders for the environment variables set in .env, you next need to retrieve the credentials to set as their values. To do that, sign into the Twilio Console. There, click the black and white up arrow at the bottom of the page, and you should see your Account SID and Auth Token in the Workbench; as shown in the image below.
Copy these values and paste them into .env as the values for TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN.
Next, navigate to Products and Services > Verify > Services. On this page, click Create new. Then, fill out the initial form with the configuration values shown in the screenshot below, and click Continue.
In the next step, leave Enable Fraud Guard set to "Yes" and click Continue to finish creating the service.
After creating the service, copy the Service SID and paste it into .env as the value of TWILIO_VERIFY_SERVICE_SID.
Step 4: Build the base PHP application
The next thing to do is to load the environment variables. To do that, paste the code, below, into public/index.php:
The updated code, above, loads environment variables from the variables defined in .env, using PHP dotenv, ensuring that TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_VERIFY_SERVICE_SID have been set and are not empty. Then, it adds session and Twig template support to the application, along with a Twilio Rest Client object to the application's DI container; which will be used when sending and verifying OTP codes.
With that done, update src/Application.php's constructor to match the following:
Then, add the following private class variables to the class:
And after that, add the following use statement to the top of the file:
The revised constructor adds the session support that was initialised in public/index.php to the class.
Step 5: Add the ability to send an OTP
Now, you'll add the first of two features: the ability to send an OTP code when requested by the user. To do that, add the following function to src/Application.php.
The showSignInForm() function uses Twig to render templates/sign-in.html.twig. This function will be called in response to GET requests to the application's default route "/".
Now, in templates, create a new file named sign-in.html.twig, and in that file, paste the code below:
As you can see from the HTML above, it is a simplistic HTML page with a form, with a single field named "phone". Note that the field's type and inputmode attributes are set to "tel". This hints to browsers on mobile devices to render a virtual keyboard most appropriate for entering telephone data.
Then, back in src/Application.php, add the following code after the showSignInForm() function.
The sendOtpCode() function processes requests to the sign-in form. It retrieves the phone number parameter from the request and, if it's available, stores it in the current session. If it's not available, it redirects the user back to the sign in form.
Then, it attempts to send an OTP code to the user's phone number via SMS using Twilio Verify. If the code was successfully sent, the user is redirected to the "Verify OTP code" form, which you'll build next.
Now, in src/Application.php, update the setupRoutes() function with the code below:
The code adds the GET and POST variants of the default route ("/") to the application's routing table; the GET version is handled by showSignInForm(), and the POST version by sendOtpCode().
Now, add the following to the use statements at the top of the file:
Step 6: Add the ability to verify an OTP
You'll now add the functionality to verify the OTP codes the user receives. Start by adding the code below to the end of src/Application.php.
The showVerifyOtpForm() method renders templates/verify.html.twig, providing the form for the user to enter to validate their OTP code.
The verifyOtpCode() function retrieves the phone number from the session. If it's not present or empty, the user is redirected to the sign in form. Otherwise, it attempts to parse the request data and retrieve the "code" parameter. If the "code" parameter wasn't present or was empty, the user is redirected to the verify OTP code form.
If the parameter was present, it uses the Twilio Rest Client to validate that the code supplied was the same as the one that was sent to the user's phone number. If the code was valid:
- The user's phone number is deleted from the current session
- templates/verification-status.html.twig is rendered as the function's response, along with two template variables:
statusset totrueandmessageset to "Verification was successful".
If the code was not valid, templates/verification-status.html.twig will be rendered with status set to false and message set to "Verification failed".
With that done, in the templates directory create a new file named verify.html.twig and paste the code below into the file.
The HTML renders another, simplistic, form with a single field for the received OTP code. The field uses the pattern and maxlength field attributes to ensure that the only valid input is a 6-digit code. It also sets the inputmode attribute to "numeric" to have the browser display a keyboard appropriate for entering digits, making it easier for the user to only enter numeric input.
Now, create another file in templates named verification-status.html.twig and paste the code below into the file.
The HTML renders the message in the Message template variable, and styles it as either a success or error, based on the value of the Success template variable.
With the templates created, the final thing to do is to add the final two route definitions to the application's routing table. Do that by updating setupRoutes() in src/Application.php to the following:
These add the GET and POST forms of the "/verify" route, handled by the showVerifyOtpForm() and verifyOtpCode() functions, respectively.
Step 7: Download the application's CSS file
The last step in the process is to download the application's CSS file from the project's GitHub repository, to the project's assets/css directory, naming it styles.css.
Test that the application works
Finally, it's time to test that the code works as expected. Start the application by running the following command.
Your server will start on port 8080, as shown by terminal output similar to the following.
You can now navigate to http://localhost:8080 to test your passwordless auth flow.
Enter your phone number and click Send OTP.
You will be redirected to the Verify OTP Code form. After you receive the code via SMS enter it into the form and submit the form. Whether the code was or not, you'll see a confirmation printed in the browser.
That's the essentials of implementing passwordless authentication in PHP using Twilio Verify
Passwordless authentication using PHP and Twilio Verify presents a compelling alternative to traditional password-based systems. It offers a blend of enhanced security and user convenience, making it an attractive option for modern applications. While passwordless auth introduces some extra complexity to your application, the benefits — especially regarding security and user experience — are worth the investment.
Matthew Setter is a PHP, Go, and Rust Editor in the Twilio Dev Content team. He’s also the author of Mezzio Essentials and Deploy with Docker Compose. You can find him at msetter@twilio.com. He's also on LinkedIn and GitHub.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.