Build a passcode protected conference line with Twilio and Ruby
We've explored building a conference line with Ruby on Rails before, but the result there was a conference call that anyone could join. If you want to keep your calls a little more private you will want to protect your conference lines with a passcode.
In this post we will take the Rails application we previously developed and add a stage where we ask for a passcode, only allowing callers into the call if they enter it correctly.
What you'll need
In order to build this conference application you will need:
- Ruby and Bundler installed
- A Twilio account (if you don't have one yet, sign up for a new Twilio account here and receive $10 credit when you upgrade)
- A Twilio phone number that can receive incoming calls
- ngrok for testing webhooks with our local application
Once you've got all of that, we'll get started.
Getting up and running with Rails
git clone https://github.com/philnash/conference-calls-on-rails.git -b open-conference-line
Install the dependencies:
Start the server to make sure everything is working as expected:
bundle exec rails server
While the server is running you can make a
POST request to our existing conference call webhook endpoint to see it working. It should look a bit like this:
$ curl --data "" localhost:3000/calls <Response> <Say voice="alice">Welcome to the conference call, let's dial you in right away.</Say> <Dial> <Conference>Thunderdome</Conference> </Dial> </Response>
This response is TwiML and tells Twilio what to do with a call. If you set a phone number's incoming call webhook to the
/calls endpoint in this application then anyone who calls will be welcomed and then entered into the conference call.
Protecting a conference line
Instead of directing a caller straight into a conference call, this time we want to greet the caller and ask them for a code. When they enter the code we will check it against a code we set and if they match they will be allowed to join the conference call.
To achieve this we will use
<Gather> to receive their input. Let's see how that works.
First, we will need a few new actions, so generate a new controller to contain them.
bundle exec rails generate controller protected_calls
app/controllers/protected_calls_controller.rb, we'll add our new actions here.
For this webhook controller we need to skip the cross-site request forgery (CSRF) protection. The controller is not intended to respond to requests from users, it is for incoming webhooks from Twilio, so CSRF protection is unnecessary. Instead, check out this post on how to protect webhook endpoints in Ruby applications by validating the request signature using Rack middleware.
To skip the protection, add this to the top of the controller class:
class ProtectedCallsController < ApplicationController skip_before_action :verify_authenticity_token end
Building our actions
Next, let's add an action that welcomes the user and asks them for the code. The Twilio Ruby gem is already installed in this application, so we can use the TwiML helpers to build up our response.
Call the first action
welcome and create a new
Twilio::TwiML::VoiceResponse object to build up the response. We are gathering input from the caller, so we'll start using <Gather> and then nest a <Say> within it. TwiML is a subset of XML, so at the end of the action we render the result as XML.
class ProtectedCallsController < ApplicationController skip_before_action :verify_authenticity_token def welcome twiml = Twilio::TwiML::VoiceResponse.new twiml.gather(action: protected_calls_verify_path) do |gather| gather.say(voice: "alice", message: "Please enter the code to enter the conference followed by the hash.") end render xml: twiml end end
<Gather> will listen for the user input once the message has been read out. Once the user enters the code and presses the # key Twilio will then make a request to the
action attribute that we added. Above I used the URL
protected_calls_verify_path which doesn't exist yet. When Twilio makes the request to the action it sends the digits that the user pressed in the
Digits parameter of the request.
We need to build another action that will check the digits against a code we decided on before the call and use that to decide whether the caller can join the conference or if they got it wrong, direct them to enter the code again. If they can join the conference we will return TwiML with
<Conference> to drop them into the call. Otherwise we will read out another message with
<Redirect> them back to the welcome action.
For the purposes of this application, we will add the code as an environment variable called
Add the following code to your controller:
class ProtectedCallsController < ApplicationController skip_before_action :verify_authenticity_token def welcome twiml = Twilio::TwiML::VoiceResponse.new twiml.gather(action: protected_calls_verify_path) do |gather| gather.say(voice: "alice", message: "Please enter the code to enter the conference followed by the hash.") end render xml: twiml end def verify twiml = Twilio::TwiML::VoiceResponse.new digits = params["Digits"] if digits == ENV["CONFERENCE_PIN"] twiml.say(voice: "alice", message: "Thank you, joining the conference now.") twiml.dial do |dial| dial.conference("Vault") end else twiml.say(voice: "alice", message: "Sorry, that code is incorrect.") twiml.redirect(protected_calls_welcome_path) end render xml: twiml end end
We're almost done here, but we haven't sorted our routes out just yet. Open up
config/routes.rb and add the following:
Rails.application.routes.draw do resources :calls, only: [:create] post 'protected_calls/welcome', to: 'protected_calls#welcome' post 'protected_calls/verify', to: 'protected_calls#verify' end
POST routes to the 2 actions in the controller that we just created.
Testing it all out
That's all the code we need, the next thing to do is test it. We need to start up the application with a PIN in the environment, on the command line run:
CONFERENCE_PIN=382302 bundle exec rails server
You can use whatever PIN code you like.
Next, we will need to use ngrok to open a tunnel and give us a public URL to this application. The server should be running on localhost on port 3000, so run the following command in another terminal window:
ngrok http 3000 --host-header "localhost:3000"
This will start ngrok and show you a URL you can use, it should look like
https://RANDOM_SUBDOMAIN.ngrok.io. Grab that URL and open up your Twilio console. We need to set our phone number's incoming call webhook to this URL plus the welcome path we just created. The whole URL we will use will be:
Find your phone number in the Twilio console, edit it and add the URL as the incoming webhook for voice calls.
Now, dial your phone number. You will be greeted with your welcome message, enter the incorrect code and you will be denied access and asked again. Enter the correct code and you will be dropped into a conference call, waiting for anyone else who knows the code to join you.
What else could you build?
In this post you have seen how to take user input on a phone call in order to secure a conference line. If you want to see more things you can do with conference calls, check out the Conference TwiML docs to learn how to join a Conference on mute, silently bridge calls, or build a moderated conference call.
If you want to check out the complete code for this application you can find it on GitHub. I'd love to hear what you're building with Twilio too, so do hit me up on Twitter at @philnash or drop me an email at email@example.com.