From 603ba9e068cbf99d5e1e86158c4d5a094545004c Mon Sep 17 00:00:00 2001 From: ckoegel Date: Mon, 2 Nov 2026 15:52:12 -0500 Subject: [PATCH] DX-3359 Update Sample --- .github/CODEOWNERS | 2 +- .github/dependabot.yml | 19 ++++ .github/workflows/test.yml | 15 +++ LICENSE | 2 +- Program.cs | 182 +++++++++++++++++++++---------------- README.md | 42 ++++----- mfa-cli-csharp.csproj | 6 +- 7 files changed, 166 insertions(+), 102 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..c05d9b2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 + +updates: + - package-ecosystem: nuget + 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..d0fd1bd --- /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/csharp.yml@main + with: + sample: mfa-cli diff --git a/LICENSE b/LICENSE index c85e706..ddc77f4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 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/Program.cs b/Program.cs index aeb3de6..3b21d8c 100644 --- a/Program.cs +++ b/Program.cs @@ -1,76 +1,106 @@ -using Bandwidth.Standard.Api; -using Bandwidth.Standard.Client; -using Bandwidth.Standard.Model; - -string BW_USERNAME; -string BW_PASSWORD; -string BW_MESSAGING_APPLICATION_ID; -string BW_ACCOUNT_ID; -string BW_VOICE_APPLICATION_ID; -string BW_NUMBER; - -//Setting up environment variables -try -{ - BW_USERNAME = Environment.GetEnvironmentVariable("BW_USERNAME"); - BW_PASSWORD = Environment.GetEnvironmentVariable("BW_PASSWORD"); - BW_MESSAGING_APPLICATION_ID = Environment.GetEnvironmentVariable("BW_MESSAGING_APPLICATION_ID"); - BW_ACCOUNT_ID = Environment.GetEnvironmentVariable("BW_ACCOUNT_ID"); - BW_VOICE_APPLICATION_ID = System.Environment.GetEnvironmentVariable("BW_VOICE_APPLICATION_ID"); - BW_NUMBER = System.Environment.GetEnvironmentVariable("BW_NUMBER"); -} -catch (Exception) -{ - Console.WriteLine("Please set the environmental variables defined in the README"); - throw; -} - - -Configuration configuration = new Configuration(); -configuration.Username = BW_USERNAME; -configuration.Password = BW_PASSWORD; - -MFAApi mfaApiInstance = new MFAApi(configuration); - -Console.WriteLine("Enter phone number in E164 format (+15554443333): "); -string phoneNumber = Console.ReadLine(); - -Console.WriteLine("Enter MFA request method (voice/messaging). Default is messaging:"); -string method = Console.ReadLine().ToLower(); - -CodeRequest codeRequest = new CodeRequest( - to: phoneNumber, - from: BW_NUMBER, - applicationId: method == "voice" ? BW_VOICE_APPLICATION_ID : BW_MESSAGING_APPLICATION_ID, - scope: "scope", - digits: 6, - message: "Your temporary {NAME} {SCOPE} code is {CODE}" -); - -if (method == "voice") -{ - mfaApiInstance.GenerateVoiceCode(BW_ACCOUNT_ID, codeRequest); -} -else -{ - mfaApiInstance.GenerateMessagingCode(BW_ACCOUNT_ID, codeRequest); -} - -Console.WriteLine("Enter the code you received: "); -string code = Console.ReadLine(); - -VerifyCodeRequest verifyCodeRequest = new VerifyCodeRequest( - to: phoneNumber, - scope: "scope", - expirationTimeInMinutes: 3, - code: code -); - -if (mfaApiInstance.VerifyCode(BW_ACCOUNT_ID, verifyCodeRequest).Valid) -{ - Console.WriteLine("Code is valid"); -} -else -{ - Console.WriteLine("Code is invalid"); -} +using System.Text.RegularExpressions; +using Bandwidth.Standard.Api; +using Bandwidth.Standard.Client; +using Bandwidth.Standard.Model; + +//Setting up environment variables +string? BW_ACCOUNT_ID = Environment.GetEnvironmentVariable("BW_ACCOUNT_ID"); +string? BW_CLIENT_ID = Environment.GetEnvironmentVariable("BW_CLIENT_ID"); +string? BW_CLIENT_SECRET = Environment.GetEnvironmentVariable("BW_CLIENT_SECRET"); +string? BW_NUMBER = Environment.GetEnvironmentVariable("BW_NUMBER"); +string? BW_VOICE_APPLICATION_ID = Environment.GetEnvironmentVariable("BW_VOICE_APPLICATION_ID"); +string? BW_MESSAGING_APPLICATION_ID = Environment.GetEnvironmentVariable("BW_MESSAGING_APPLICATION_ID"); + +if (BW_ACCOUNT_ID == null || BW_CLIENT_ID == null || BW_CLIENT_SECRET == null || BW_NUMBER == null + || BW_VOICE_APPLICATION_ID == null || BW_MESSAGING_APPLICATION_ID == null) +{ + Console.WriteLine("Please set the environmental variables defined in the README"); + Environment.Exit(1); + return; +} + +Configuration configuration = new Configuration(); +configuration.OAuthClientId = BW_CLIENT_ID; +configuration.OAuthClientSecret = BW_CLIENT_SECRET; + +const string SCOPE = "sample"; + +string Prompt(string text) +{ + Console.Write(text); + string? line = Console.ReadLine(); + if (line == null) + { + Environment.Exit(1); + } + return line.Trim(); +} + +string phoneNumber = Prompt("Enter the phone number to send the code to (E.164, e.g. +15554443333): "); +if (!Regex.IsMatch(phoneNumber, @"^\+[1-9]\d{1,14}$")) +{ + Console.Error.WriteLine("Phone number must be in E.164 format, e.g. +15554443333"); + Environment.Exit(1); +} + +string method = Prompt("Send the code by messaging or voice? [messaging]: ").ToLower(); +if (method == "") +{ + method = "messaging"; +} +if (method != "messaging" && method != "voice") +{ + Console.Error.WriteLine("Method must be messaging or voice"); + Environment.Exit(1); +} + +MFAApi mfaApi = new MFAApi(configuration); + +CodeRequest codeRequest = new CodeRequest( + to: phoneNumber, + from: BW_NUMBER, + applicationId: method == "voice" ? BW_VOICE_APPLICATION_ID : BW_MESSAGING_APPLICATION_ID, + scope: SCOPE, + message: "Your temporary {NAME} {SCOPE} code is {CODE}", + digits: 6 +); + +Console.WriteLine($"Sending a code by {method} to {phoneNumber}"); +try +{ + if (method == "voice") + { + await mfaApi.GenerateVoiceCodeAsync(BW_ACCOUNT_ID, codeRequest); + } + else + { + await mfaApi.GenerateMessagingCodeAsync(BW_ACCOUNT_ID, codeRequest); + } +} +catch (Exception e) +{ + Console.Error.WriteLine($"Failed to send code: {e.Message}"); + Environment.Exit(1); +} + +string code = Prompt("Enter the code you received: "); + +bool valid = false; +try +{ + VerifyCodeRequest verifyRequest = new VerifyCodeRequest( + to: phoneNumber, + scope: SCOPE, + expirationTimeInMinutes: 3, + code: code + ); + valid = (await mfaApi.VerifyCodeAsync(BW_ACCOUNT_ID, verifyRequest)).Valid; +} +catch (Exception e) +{ + Console.Error.WriteLine($"Failed to verify code: {e.Message}"); + Environment.Exit(1); +} + +Console.WriteLine(valid ? "Code verified" : "Code not verified"); +Environment.Exit(valid ? 0 : 1); diff --git a/README.md b/README.md index 6465393..eb5d44f 100644 --- a/README.md +++ b/README.md @@ -4,44 +4,44 @@ Multi-Factor Auth About Page - # Table of Contents +# Table of Contents -- [Multi-Factor Auth CLI](#multi-factor-auth-cli) -- [Table of Contents](#table-of-contents) -- [Description](#description) -- [Pre-Requisites](#pre-requisites) -- [Environmental Variables](#environmental-variables) -- [Running the Application](#running-the-application) +* [Description](#description) +* [Pre-Requisites](#pre-requisites) +* [Running the Application](#running-the-application) +* [Environmental Variables](#environmental-variables) # 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 that the application will be used for (this sample app uses a messaging application). +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. -# Environmental Variables +# Running the Application -The sample app uses the below environmental variables. +Use the following command to run the application: ```sh -BW_ACCOUNT_ID # Your Bandwidth Account Id -BW_USERNAME # Your Bandwidth API Username -BW_PASSWORD # Your Bandwidth API Password -BW_NUMBER # The Bandwidth phone number involved with this application -BW_VOICE_APPLICATION_ID # Your Voice Application Id created in the dashboard -BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard +dotnet run ``` -# Running the Application +# Environmental Variables -Use the following command to run the application: +The sample app uses the below environmental variables. ```sh -dotnet run +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/mfa-cli-csharp.csproj b/mfa-cli-csharp.csproj index 78ac0ee..4f0912b 100644 --- a/mfa-cli-csharp.csproj +++ b/mfa-cli-csharp.csproj @@ -2,14 +2,14 @@ Exe - net7.0 + net8.0 mfa_cli_csharp enable enable - - + +