From 8f9812e52c0023225d3271803701973ccea7a777 Mon Sep 17 00:00:00 2001 From: ckoegel Date: Mon, 2 Nov 2026 15:46:38 -0500 Subject: [PATCH] DX-3359 Update Sample --- .github/CODEOWNERS | 2 +- .github/dependabot.yml | 19 +++++++++ .github/workflows/test.yml | 15 +++++++ Gemfile | 3 +- LICENSE | 2 +- README.md | 26 ++++++------ app.rb | 84 ++++++++++++++++++++++++++------------ 7 files changed, 108 insertions(+), 43 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/test.yml 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..8350171 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 + +updates: + - package-ecosystem: bundler + 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..b5d780c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +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/ruby.yml@main + with: + sample: mfa-cli diff --git a/Gemfile b/Gemfile index 9147bd1..2b7f173 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,3 @@ source 'https://rubygems.org' -gem 'bandwidth-sdk', '11.0.0' -gem 'sinatra', '3.1.0' +gem 'bandwidth-sdk', '18.3.1' diff --git a/LICENSE b/LICENSE index 0bb21bd..ddc77f4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 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 e9b5f80..5a94c62 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,24 @@ Multi-Factor Auth About Page - # Table of Contents +# Table of Contents * [Description](#description) * [Pre-Requisites](#pre-requisites) * [Running the Application](#running-the-application) * [Environmental Variables](#environmental-variables) -* [Callback URLs](#callback-urls) - * [Ngrok](#ngrok) # Description -This app creates a simple CLI tool used to create and verify multi-factor auth codes using Bandwidth's Multi-Factor Auth API. The app will prompt the user for their phone number, followed by their preferred method of code delivery; either messaging or voice. The app will then text or call the phone number provided with a 6 digit MFA code that the user can enter back into the CLI to verify. +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 application at the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and create API tokens. +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 our [Account Credentials](https://dev.bandwidth.com/docs/account/credentials) page. @@ -40,12 +40,14 @@ ruby app.rb ``` # 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_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.rb b/app.rb index 3422c80..1b04739 100644 --- a/app.rb +++ b/app.rb @@ -2,8 +2,8 @@ begin BW_ACCOUNT_ID = ENV.fetch('BW_ACCOUNT_ID') - BW_USERNAME = ENV.fetch('BW_USERNAME') - BW_PASSWORD = ENV.fetch('BW_PASSWORD') + BW_CLIENT_ID = ENV.fetch('BW_CLIENT_ID') + BW_CLIENT_SECRET = ENV.fetch('BW_CLIENT_SECRET') BW_NUMBER = ENV.fetch('BW_NUMBER') BW_VOICE_APPLICATION_ID = ENV.fetch('BW_VOICE_APPLICATION_ID') BW_MESSAGING_APPLICATION_ID = ENV.fetch('BW_MESSAGING_APPLICATION_ID') @@ -12,42 +12,72 @@ exit(-1) end -Bandwidth.configure do |config| # Configure Basic Auth - config.username = BW_USERNAME - config.password = BW_PASSWORD +Bandwidth.configure do |config| + config.client_id = BW_CLIENT_ID + config.client_secret = BW_CLIENT_SECRET end -mfa_api_instance = Bandwidth::MFAApi.new +$stdout.sync = true -puts 'Enter phone number in E164 format (+15554443333): ' -recipient_phone_number = gets.chomp +E164_PATTERN = /\A\+[1-9]\d{1,14}\z/ +SCOPE = 'sample'.freeze -puts 'Enter MFA request method (voice/messaging). Default is messaging: ' -delivery_method = gets.chomp +def prompt(text) + print text + line = $stdin.gets + exit(1) if line.nil? + line.strip +end + +phone_number = prompt('Enter the phone number to send the code to (E.164, e.g. +15554443333): ') +unless phone_number.match?(E164_PATTERN) + warn 'Phone number must be in E.164 format, e.g. +15554443333' + exit(1) +end + +method = prompt('Send the code by messaging or voice? [messaging]: ').downcase +method = 'messaging' if method.empty? +unless %w[messaging voice].include?(method) + warn 'Method must be messaging or voice' + exit(1) +end + +mfa_api = Bandwidth::MFAApi.new -code_request_body = Bandwidth::CodeRequest.new( - to: recipient_phone_number, +code_request = Bandwidth::CodeRequest.new( + to: phone_number, from: BW_NUMBER, - application_id: delivery_method == 'voice' ? BW_VOICE_APPLICATION_ID : BW_MESSAGING_APPLICATION_ID, - scope: 'scope', + 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 delivery_method == 'voice' - mfa_api_instance.generate_voice_code(BW_ACCOUNT_ID, code_request_body) -else - mfa_api_instance.generate_messaging_code(BW_ACCOUNT_ID, code_request_body) +puts "Sending a code by #{method} to #{phone_number}" +begin + if method == 'voice' + mfa_api.generate_voice_code(BW_ACCOUNT_ID, code_request) + else + mfa_api.generate_messaging_code(BW_ACCOUNT_ID, code_request) + end +rescue StandardError => e + warn "Failed to send code: #{e.message}" + exit(1) end -puts 'Please enter your received code: ' -code = gets.chomp - -verify_request_body = Bandwidth::VerifyCodeRequest.new( - to: recipient_phone_number, - scope: 'scope', - code: code, - expiration_time_in_minutes: 3 +verify_request = Bandwidth::VerifyCodeRequest.new( + to: phone_number, + scope: SCOPE, + expiration_time_in_minutes: 3, + code: prompt('Enter the code you received: ') ) -mfa_api_instance.verify_code(BW_ACCOUNT_ID, verify_request_body).valid ? puts('Success!') : puts('Failure') +begin + valid = mfa_api.verify_code(BW_ACCOUNT_ID, verify_request).valid +rescue StandardError => e + warn "Failed to verify code: #{e.message}" + exit(1) +end + +puts valid ? 'Code verified' : 'Code not verified' +exit(valid ? 0 : 1)