Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 11:22
Authenticating Twilio Requests Using Basic Authentication, SSL, And ColdFusion
<!---
When we check for authorization, there's a number of things
that can go wrong, all of which will indicate that the
incoming request is not authorized. As such, let's wrap this in
a try/catch such that if any part of it fails, we can respond
with a 401 Unauthorized response.
--->
<cftry>
<!--- Get the HTTP request headers. --->
<cfset headers = getHttpRequestData().headers />
<!---
Check to see if the authorization header exists. This is
the value that contains our obfuscated (base64-encoded)
login credentials.
--->
<cfif !structKeyExists( headers, "Authorization" )>
<!--- Throw an error. --->
<cfthrow
type="AuthorizationNotProvided"
message="You must provide authorization credentials to access this system."
/>
</cfif>
<!---
At this point, we know that the client (Twilio Proxy) has
provided authorization headers; now, let's unecode them to
compare them to our valid credentials. This will be in the
form of:
Basic dHJpY2lhOnN1cGVyc2V4eQ==
We need to get the latter part, which a Base64-encoded string
in the form of username:password.
--->
<cfset encodedCredentials = listLast( headers.authorization, " " ) />
<!--- Convert the encoded credentials into a plain string. --->
<cfset credentials = toString( toBinary( encodedCredentials ) ) />
<!---
Check to make sure that the credentials conform to a valid
authorization string, username:password.
--->
<cfif !reFind( "^[^:]+:.+$", credentials )>
<!---
The client (Twilio Proxy) did not provide a valid
credentials string. Throw an error.
--->
<cfthrow
type="MalformedCredentials"
message="You must provide your authorization credentials in the form of [username:password]."
/>
</cfif>
<!---
Now that we know the credentials are in the proper format,
we can parse out the username and password values and compare
them to our internal credentials.
--->
<cfset username = listFirst( credentials, ":" ) />
<cfset password = listLast( credentials, ":" ) />
<!---
Compare the username and password to our list of accepted
credentials.
--->
<cfif !(
(username eq "joanna") &&
(password eq "Slipp3ryWh3nW3t")
)>
<!---
The username and password are not authorized. Throw
an error.
--->
<cfthrow
type="Unauthorized"
message="The credentials that you have provided are not authorized to access this system."
/>
</cfif>
<!--- ------------------------------------------------- --->
<!--- ------------------------------------------------- --->
<!---
If we have made it this far, then the user has provided the
properly formed, authenticated, and authorized credentials.
Allow them to continue on with the page request.
--->
<!--- ------------------------------------------------- --->
<!--- ------------------------------------------------- --->
<!---
For the most part, we are going to generically catch any
errors. However, if the error is that the provided
credentials simply aren't valid, then return a standard
XML response WITHOUT the 401 Unauthorized response. Even if
we provide a TwiML response with our 401 status code, Twilio
will NOT pass the response back to the user (because it
thinks it's still trying to authenticate).
NOTE: I would NOT *really* do this in a live system, this is
just for demo and exploration purposes. After all, if the
SMS end point is NOT authorized, then something is clearly
NOT configured properly in your Twilio phone number.
--->
<cfcatch type="Unauthorized">
<!--- Create an access denied TwiML response. --->
<cfsavecontent variable="responseXML">
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Sms>Access Denied</Sms>
</Response>
</cfsavecontent>
<!--- Return the access denied SMS response. --->
<cfcontent
type="text/xml"
variable="#toBinary( toBase64( trim( responseXML ) ) )#"
/>
</cfcatch>
<!---
Catch any error that is NOT part of the specific username and
password authentication. Any error here will indicate that the
request is not authorized.
--->
<cfcatch>
<!--- Send back an unauthorized status code. --->
<cfheader
statuscode="401"
statustext="Unauthorized"
/>
<!---
Alert the client that we support basic authentication in
the realm of SMS end points. Without this, the client
will not know how to authenticate itself.
--->
<cfheader
name="WWW-Authenticate"
value="basic realm=""SMS"""
/>
<!--- Return the access denied body. --->
<cfcontent
type="text/plain"
variable="#toBinary( toBase64( 'Access Denied' ) )#"
/>
</cfcatch>
</cftry>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
If we have made it this far, then we know this user is
authorized. Let's param the form values and proceeed with
standard usage.
--->
<cfparam name="form.from" type="string" default="" />
<cfparam name="form.body" type="string" default="" />
<!---
Build the response. For our demo purposes, just echo back the
passed-in SMS text message.
--->
<cfset response = "Thanks for the message: #form.body#" />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Convert the message into Twilio XML response. --->
<cfsavecontent variable="responseXml">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Sms>#xmlFormat( response )#</Sms>
</Response>
</cfoutput>
</cfsavecontent>
<!---
Stream XML response to Twilio client. Make sure to TRIM
the XML response such that it is valid XML.
--->
<cfcontent
type="text/xml"
variable="#toBinary( toBase64( trim( responseXml ) ) )#"
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment