diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index beb9074..12776e1 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,2 +1,2 @@
# Global rule:
-* @Bandwidth-Samples/dx
+* @Bandwidth-Samples/band-swi @Bandwidth-Samples/band-devx
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..ffb69cb
--- /dev/null
+++ b/.github/dependabot.yml
@@ -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"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..fb52bc4
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index f390797..eda4744 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,8 @@
-# Compiled class file
-*.class
-/target
-/out
-/dist
-/bin
-/obj
+# macOS System file
+.DS_Store
+
+# Environment file
+.env
# IntelliJ
.idea/
@@ -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*
diff --git a/LICENSE b/LICENSE
index df9461d..ddc77f4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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
diff --git a/README.md b/README.md
index 3c15a66..5a7d63a 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,53 @@
-# 2FA-CLI-PHP
-
-
+# Multi-Factor Auth CLI
- # Table of Contents
+
+
+
-
+# Table of Contents
-- [2FA-CLI-PHP](#2FA-CLI-PHP)
-- [Description](#description)
-- [Bandwidth](#bandwidth)
-- [Environmental Variables](#environmental-variables)
-- [Callback URLs](#callback-urls)
- - [Ngrok](#ngrok)
-
-
+* [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
```
diff --git a/app.php b/app.php
new file mode 100644
index 0000000..76da0c3
--- /dev/null
+++ b/app.php
@@ -0,0 +1,89 @@
+ 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);
diff --git a/composer.json b/composer.json
index 9a9a4a5..2a063d3 100644
--- a/composer.json
+++ b/composer.json
@@ -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"
}
}
diff --git a/composer.lock b/composer.lock
index 6effb64..5bf2cae 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,89 +4,229 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "ded6fb2484b21ab530c8a6944ebc3b30",
+ "content-hash": "16847c57ae206f335d04712dac9affc8",
"packages": [
{
- "name": "apimatic/jsonmapper",
- "version": "v1.3.1",
+ "name": "bandwidth/sdk",
+ "version": "v14.0.1",
"source": {
"type": "git",
- "url": "https://github.com/apimatic/jsonmapper.git",
- "reference": "0883d385653ebd2a194ec79e66facb7d69441404"
+ "url": "https://github.com/Bandwidth/php-sdk.git",
+ "reference": "2638389c4fef620d2507f2cb6d3f9741f9884f33"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/apimatic/jsonmapper/zipball/0883d385653ebd2a194ec79e66facb7d69441404",
- "reference": "0883d385653ebd2a194ec79e66facb7d69441404",
+ "url": "https://api.github.com/repos/Bandwidth/php-sdk/zipball/2638389c4fef620d2507f2cb6d3f9741f9884f33",
+ "reference": "2638389c4fef620d2507f2cb6d3f9741f9884f33",
"shasum": ""
},
+ "require": {
+ "ext-curl": "*",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "guzzlehttp/guzzle": "^7.4.5",
+ "guzzlehttp/psr7": "^2.0",
+ "php": "^8.1"
+ },
"require-dev": {
- "phpunit/phpunit": "^5 || ^6 || ^7",
- "squizlabs/php_codesniffer": "^3"
+ "friendsofphp/php-cs-fixer": "^3.5",
+ "overtrue/phplint": "^9.0",
+ "phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
- "apimatic\\jsonmapper\\": "src/"
+ "Bandwidth\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "OSL-3.0"
+ "MIT"
],
"authors": [
{
- "name": "Christian Weiske",
- "email": "christian.weiske@netresearch.de",
- "homepage": "http://www.netresearch.de/",
- "role": "Developer"
+ "name": "Bandwidth",
+ "homepage": "https://www.bandwidth.com"
+ }
+ ],
+ "description": "Bandwidth's Communication APIs",
+ "homepage": "https://github.com/Bandwidth/php-sdk",
+ "keywords": [
+ "api",
+ "openapi",
+ "openapi-generator",
+ "openapitools",
+ "php",
+ "rest",
+ "sdk"
+ ],
+ "support": {
+ "issues": "https://github.com/Bandwidth/php-sdk/issues",
+ "source": "https://github.com/Bandwidth/php-sdk/tree/v14.0.1"
+ },
+ "time": "2026-10-13T20:38:29+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.16.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "81755d989b3007ca7f88a069857553cd08e7dc2e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/81755d989b3007ca7f88a069857553cd08e7dc2e",
+ "reference": "81755d989b3007ca7f88a069857553cd08e7dc2e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^2.6",
+ "guzzlehttp/psr7": "^2.13.2",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.5 || ^3.0",
+ "symfony/polyfill-php80": "^1.25"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "guzzle/client-integration-tests": "3.0.3",
+ "guzzlehttp/test-server": "^0.7",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
},
{
- "name": "Mehdi Jaffery",
- "email": "mehdi.jaffery@apimatic.io",
- "homepage": "http://apimatic.io/",
- "role": "Developer"
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
}
],
- "description": "Map nested JSON structures onto PHP classes",
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
"support": {
- "email": "mehdi.jaffery@apimatic.io",
- "issues": "https://github.com/apimatic/jsonmapper/issues",
- "source": "https://github.com/apimatic/jsonmapper/tree/v1.3.1"
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.16.1"
},
- "time": "2020-04-06T20:47:48+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-10-25T21:35:21+00:00"
},
{
- "name": "bandwidth/sdk",
- "version": "v2.14.0",
+ "name": "guzzlehttp/promises",
+ "version": "2.6.0",
"source": {
"type": "git",
- "url": "https://github.com/Bandwidth/php-sdk.git",
- "reference": "1f88cd2965aa059f648d542c0579b06842095b95"
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "3d88c829bd5f13d13847b39067dbf09a60af2154"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Bandwidth/php-sdk/zipball/1f88cd2965aa059f648d542c0579b06842095b95",
- "reference": "1f88cd2965aa059f648d542c0579b06842095b95",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/3d88c829bd5f13d13847b39067dbf09a60af2154",
+ "reference": "3d88c829bd5f13d13847b39067dbf09a60af2154",
"shasum": ""
},
"require": {
- "apimatic/jsonmapper": "~1.3.0",
- "ext-curl": "*",
- "ext-json": "*",
- "ext-mbstring": "*",
- "ext-simplexml": "*",
- "mashape/unirest-php": "~3.0.1",
- "php": ">=5.4.0"
+ "php": "^7.2.5 || ^8.0",
+ "symfony/deprecation-contracts": "^2.5 || ^3.0"
},
"require-dev": {
- "phan/phan": "^1.2",
- "squizlabs/php_codesniffer": "^2.7"
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34"
},
"type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
"autoload": {
"psr-4": {
- "BandwidthLib\\": "src/"
+ "GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -95,84 +235,538 @@
],
"authors": [
{
- "name": "APIMatic SDK Generator",
- "email": "support@apimatic.io",
- "homepage": "https://apimatic.io",
- "role": "API Tool"
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
}
],
- "description": "Bandwidth's set of APIs",
- "homepage": "https://apimatic.io",
+ "description": "Guzzle promises library",
"keywords": [
- "api",
- "bandwidth",
- "sdk"
+ "promise"
],
"support": {
- "issues": "https://github.com/Bandwidth/php-sdk/issues",
- "source": "https://github.com/Bandwidth/php-sdk/tree/v2.14.0"
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.6.0"
},
- "time": "2020-12-09T15:42:09+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-10-18T12:46:59+00:00"
},
{
- "name": "mashape/unirest-php",
- "version": "v3.0.4",
+ "name": "guzzlehttp/psr7",
+ "version": "2.13.2",
"source": {
"type": "git",
- "url": "https://github.com/Mashape/unirest-php.git",
- "reference": "842c0f242dfaaf85f16b72e217bf7f7c19ab12cb"
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "adee536f95101929f262b8c59e8a61472f347d58"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Mashape/unirest-php/zipball/842c0f242dfaaf85f16b72e217bf7f7c19ab12cb",
- "reference": "842c0f242dfaaf85f16b72e217bf7f7c19ab12cb",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/adee536f95101929f262b8c59e8a61472f347d58",
+ "reference": "adee536f95101929f262b8c59e8a61472f347d58",
"shasum": ""
},
"require": {
- "ext-curl": "*",
- "php": ">=5.4.0"
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0",
+ "symfony/deprecation-contracts": "^2.5 || ^3.0",
+ "symfony/polyfill-php80": "^1.25"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
},
"require-dev": {
- "codeclimate/php-test-reporter": "0.1.*",
- "phpunit/phpunit": "~4.4"
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "1.1.0",
+ "jshttp/mime-db": "1.54.0.1",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34"
},
"suggest": {
- "ext-json": "Allows using JSON Bodies for sending and parsing requests"
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
"autoload": {
- "psr-0": {
- "Unirest\\": "src/"
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "description": "Unirest PHP",
- "homepage": "https://github.com/Mashape/unirest-php",
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
- "client",
- "curl",
"http",
- "https",
- "rest"
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
],
"support": {
- "email": "opensource@mashape.com",
- "issues": "https://github.com/Mashape/unirest-php/issues",
- "source": "https://github.com/Mashape/unirest-php/tree/master"
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.13.2"
},
- "time": "2016-08-11T17:49:21+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-10-17T23:49:46+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-05T06:23:12+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.43.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74",
+ "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.43.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-09-25T15:50:05+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
- "platform": [],
- "platform-dev": [],
- "plugin-api-version": "2.0.0"
+ "platform": {
+ "php": "^8.1"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
}
diff --git a/icon-mfa.svg b/icon-mfa.svg
new file mode 100644
index 0000000..1c5ae11
--- /dev/null
+++ b/icon-mfa.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/public/index.php b/public/index.php
deleted file mode 100644
index 8baaf96..0000000
--- a/public/index.php
+++ /dev/null
@@ -1,92 +0,0 @@
- getenv("BW_USERNAME"),
- 'twoFactorAuthBasicAuthPassword' => getenv("BW_PASSWORD"),
- )
-);
-
-$client = new BandwidthLib\BandwidthClient($config);
-$authClient = $client->getTwoFactorAuth()->getClient();
-$BW_ACCOUNT_ID = getenv("BW_ACCOUNT_ID");
-$BW_PHONE_NUMBER = getenv("BW_PHONE_NUMBER");
-$BW_VOICE_APPLICATION_ID = getenv("BW_VOICE_APPLICATION_ID");
-$BW_MESSAGING_APPLICATION_ID = getenv("BW_MESSAGING_APPLICATION_ID");
-
-$recipient_phone_number = readline("Please enter your phone number in E164 format (+15554443333): ");
-
-$delivery_method = readline("Select your method to receive your 2FA request. Please enter 'voice' or 'messaging': ");
-
-
-if (strcmp($delivery_method, "messaging") == 0) {
-
- $fromPhone = $BW_PHONE_NUMBER;
- $toPhone = $recipient_phone_number;
- $applicationId = $BW_MESSAGING_APPLICATION_ID;
- $scope = 'scope';
- $digits = 6;
-
- $body = new BandwidthLib\TwoFactorAuth\Models\TwoFactorCodeRequestSchema();
- $body->from = $fromPhone;
- $body->to = $toPhone;
- $body->applicationId = $applicationId;
- $body->scope = $scope;
- $body->digits = $digits;
- $body->message = "Your temporary {NAME} {SCOPE} code is {CODE}";
-
- $authClient->createMessagingTwoFactor($BW_ACCOUNT_ID, $body);
-
- $code = readline("Please enter your code: ");
-
- $body = new BandwidthLib\TwoFactorAuth\Models\TwoFactorVerifyRequestSchema();
- $body->from = $fromPhone;
- $body->to = $toPhone;
- $body->applicationId = $applicationId;
- $body->scope = $scope;
- $body->code = $code;
- $body->digits = $digits;
- $body->expirationTimeInMinutes = 3;
-
- $response = $authClient->createVerifyTwoFactor($BW_ACCOUNT_ID, $body);
- $strn = "Auth status: " . var_export($response->getResult()->valid, true) . "\n";
- echo $strn;
-
-} elseif (strcmp($delivery_method, "voice") == 0) {
-
- $fromPhone = $BW_PHONE_NUMBER;
- $toPhone = $recipient_phone_number;
- $applicationId = $BW_VOICE_APPLICATION_ID;
- $scope = 'scope';
- $digits = 6;
-
- $body = new BandwidthLib\TwoFactorAuth\Models\TwoFactorCodeRequestSchema();
- $body->from = $fromPhone;
- $body->to = $toPhone;
- $body->applicationId = $applicationId;
- $body->scope = $scope;
- $body->digits = $digits;
- $body->message = "Your temporary {NAME} {SCOPE} code is {CODE}";
-
- $authClient->createVoiceTwoFactor($BW_ACCOUNT_ID, $body);
-
- $code = readline("Please enter your code: ");
-
- $body = new BandwidthLib\TwoFactorAuth\Models\TwoFactorVerifyRequestSchema();
- $body->from = $fromPhone;
- $body->to = $toPhone;
- $body->applicationId = $applicationId;
- $body->scope = $scope;
- $body->code = $code;
- $body->digits = $digits;
- $body->expirationTimeInMinutes = 3;
-
- $response = $authClient->createVerifyTwoFactor($BW_ACCOUNT_ID, $body);
- $strn = "Auth status: " . var_export($response->getResult()->valid, true) . "\n";
- echo $strn;
-
-}
-
-?>