Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Global rule:
* @Bandwidth-Samples/dx
* @Bandwidth-Samples/band-swi @Bandwidth-Samples/band-devx
19 changes: 19 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: 2

updates:
- package-ecosystem: composer
directory: /
schedule:
interval: monthly
open-pull-requests-limit: 5
ignore:
- dependency-name: "bandwidth/sdk"
update-types:
- version-update:semver-minor
- version-update:semver-patch
groups:
dependencies:
patterns:
- "*"
exclude-patterns:
- "bandwidth/sdk"
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Test

on:
pull_request:
branches:
- main
schedule:
- cron: "0 9 * * 1"

jobs:
contract-test:
name: Contract Test
uses: Bandwidth-Samples/sample-app-tooling/.github/workflows/php.yml@main
with:
sample: mfa-cli
start-command: php app.php
36 changes: 6 additions & 30 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Compiled class file
*.class
/target
/out
/dist
/bin
/obj
# macOS System file
.DS_Store

# Environment file
.env

# IntelliJ
.idea/
Expand All @@ -13,30 +11,8 @@
# Vs Code
.vscode/

# Node
/node_modules

# PHP
**/vendor/*
*.phar
/vendor/

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Bandwidth Samples
Copyright (c) 2026 Bandwidth Samples

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
64 changes: 40 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,53 @@
# 2FA-CLI-PHP
<a href="http://dev.bandwidth.com"><img src="https://s3.amazonaws.com/bwdemos/BW-VMP.png"/></a>
</div>
# Multi-Factor Auth CLI

# Table of Contents
<a href="https://dev.bandwidth.com/docs/mfa/">
<img src="./icon-mfa.svg" title="Multi-Factor Auth About Page" alt="Multi-Factor Auth About Page"/>
</a>

<!-- TOC -->
# Table of Contents

- [2FA-CLI-PHP](#2FA-CLI-PHP)
- [Description](#description)
- [Bandwidth](#bandwidth)
- [Environmental Variables](#environmental-variables)
- [Callback URLs](#callback-urls)
- [Ngrok](#ngrok)

<!-- /TOC -->
* [Description](#description)
* [Pre-Requisites](#pre-requisites)
* [Running the Application](#running-the-application)
* [Environmental Variables](#environmental-variables)

# Description
A PHP CLI application that allows you to test the Bandwidth 2 Factor Authentication API by sending a code to a PSTN number via phone call or SMS, entering that code into the CLI, and returning `TRUE` or `FALSE`.

# Bandwidth
In order to use the Bandwidth API users need to set up the appropriate application at the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and create API tokens.
This sample app is a command line tool that sends and verifies multi-factor auth codes using Bandwidth's Multi-Factor Auth API.

The app asks for a phone number in E.164 format and whether to send the code by `messaging` or `voice` (`messaging` if left blank). It then texts or calls that number with a 6 digit code, asks you to enter the code, and checks it. The app exits with status `0` if the code is verified and `1` otherwise.

# Pre-Requisites

In order to use the Bandwidth API users need to set up the appropriate applications at the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and create API tokens.

To create an application log into the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and navigate to the `Applications` tab. Fill out the **New Application** form selecting the service (Messaging or Voice) that the application will be used for. All Bandwidth services require publicly accessible Callback URLs, for more information on how to set one up see [Callback URLs](#callback-urls).
This app needs two applications, one for each delivery method. To create an application log into the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and navigate to the `Applications` tab. Fill out the **New Application** form once selecting Messaging, and once selecting Voice. This app doesn't receive any callbacks, so the callback URLs can be left as placeholders.

For more information about API credentials see [here](https://dev.bandwidth.com/guides/accountCredentials.html#top)
For more information about API credentials see our [Account Credentials](https://dev.bandwidth.com/docs/account/credentials) page.

# Running the Application

To install the required packages for this app, run the command:

```sh
composer install
```

Use the following command to run the application:

```sh
php app.php
```

# Environmental Variables

The sample app uses the below environmental variables.

```sh
BW_ACCOUNT_ID # Your Bandwidth Account Id
BW_USERNAME # Your Bandwidth API Token
BW_PASSWORD # Your Bandwidth API Secret
BW_PHONE_NUMBER # Your The Bandwidth Phone Number
BW_VOICE_APPLICATION_ID # Your Voice Application Id created in the dashboard
BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard
BW_ACCOUNT_ID # Your Bandwidth Account Id
BW_CLIENT_ID # Your Bandwidth API OAuth Client Id
BW_CLIENT_SECRET # Your Bandwidth API OAuth Client Secret
BW_NUMBER # The Bandwidth phone number the code is sent from
BW_VOICE_APPLICATION_ID # Your Voice Application Id created in the dashboard
BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard
```
89 changes: 89 additions & 0 deletions app.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

use Bandwidth\Api\MFAApi;
use Bandwidth\Configuration;
use Bandwidth\Model\CodeRequest;
use Bandwidth\Model\VerifyCodeRequest;
use GuzzleHttp\Client;

require __DIR__ . '/vendor/autoload.php';

$missing = array_filter(
['BW_ACCOUNT_ID', 'BW_CLIENT_ID', 'BW_CLIENT_SECRET', 'BW_NUMBER', 'BW_VOICE_APPLICATION_ID', 'BW_MESSAGING_APPLICATION_ID'],
fn ($name) => getenv($name) === false
);
if ($missing) {
fwrite(STDERR, "Please set the environmental variables defined in the README\n");
exit(1);
}

$BW_ACCOUNT_ID = getenv('BW_ACCOUNT_ID');
$BW_NUMBER = getenv('BW_NUMBER');
$BW_VOICE_APPLICATION_ID = getenv('BW_VOICE_APPLICATION_ID');
$BW_MESSAGING_APPLICATION_ID = getenv('BW_MESSAGING_APPLICATION_ID');

$config = Configuration::getDefaultConfiguration()
->setClientId(getenv('BW_CLIENT_ID'))
->setClientSecret(getenv('BW_CLIENT_SECRET'));
$mfaApi = new MFAApi(new Client(), $config);

const SCOPE = 'sample';

function prompt(string $text): string
{
echo $text;
$line = fgets(STDIN);
if ($line === false) {
exit(1);
}
return trim($line);
}

$phoneNumber = prompt('Enter the phone number to send the code to (E.164, e.g. +15554443333): ');
if (!preg_match('/^\+[1-9]\d{1,14}$/', $phoneNumber)) {
fwrite(STDERR, "Phone number must be in E.164 format, e.g. +15554443333\n");
exit(1);
}

$method = strtolower(prompt('Send the code by messaging or voice? [messaging]: ')) ?: 'messaging';
if ($method !== 'messaging' && $method !== 'voice') {
fwrite(STDERR, "Method must be messaging or voice\n");
exit(1);
}

echo "Sending a code by {$method} to {$phoneNumber}\n";
try {
$codeRequest = new CodeRequest([
'to' => $phoneNumber,
'from' => $BW_NUMBER,
'application_id' => $method === 'voice' ? $BW_VOICE_APPLICATION_ID : $BW_MESSAGING_APPLICATION_ID,
'scope' => SCOPE,
'message' => 'Your temporary {NAME} {SCOPE} code is {CODE}',
'digits' => 6,
]);
if ($method === 'voice') {
$mfaApi->generateVoiceCode($BW_ACCOUNT_ID, $codeRequest);
} else {
$mfaApi->generateMessagingCode($BW_ACCOUNT_ID, $codeRequest);
}
} catch (Throwable $e) {
fwrite(STDERR, 'Failed to send code: ' . $e->getMessage() . "\n");
exit(1);
}

$code = prompt('Enter the code you received: ');
try {
$verifyRequest = new VerifyCodeRequest([
'to' => $phoneNumber,
'scope' => SCOPE,
'expiration_time_in_minutes' => 3,
'code' => $code,
]);
$valid = $mfaApi->verifyCode($BW_ACCOUNT_ID, $verifyRequest)->getValid() === true;
} catch (Throwable $e) {
fwrite(STDERR, 'Failed to verify code: ' . $e->getMessage() . "\n");
exit(1);
}

echo $valid ? "Code verified\n" : "Code not verified\n";
exit($valid ? 0 : 1);
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"name": "bandwidth-samples/mfa-cli-php",
"description": "Bandwidth Sample App for Multi-Factor Authentication",
"type": "project",
"license": "MIT",
"require": {
"bandwidth/sdk": "^2.14"
"php": "^8.1",
"bandwidth/sdk": "14.0.1"
}
}
Loading
Loading