To interact with the JS SDK, we have to generate JWTs from our server code as a means of authorizing which resources the client side application can access. On the server side, we then authenticate a request by
checking that the signature of the JWT is signed by the AuthToken
of the provided AccountSid
.
These allow us a mechanism of authenticating and authorizing your application's API request without exposing your AuthToken on the client side for all to see.
JWTs consist of three hashed sections:
Let's first go over the two smaller sections of the JWT.
The first part of our JWT token is the header which contains 2 parts:
1{2"typ": "JWT",3"alg": "HS256"4}
This tells us that this is indeed a JWT and the hashing algorithm used is HS256.
The third and final part of our JSON Web Token is going to be the signature. This signature is made up of a hash of the following components:
In formula terms this is:
hash((hash(header) + hash(payload)), 'secret')
The secret is your AuthToken.
The payload of the JWT is what defines the access policies for resources.
Access Policy Objects are a set of rules that can be applied to an HTTP REST request, authorizing whether this request is allowed to access the API resources. The policy defines which resources and sub-resources are allowed to be accessed, what parameters are expected and allowed, and whether or not the resources and sub-resources are outright forbidden. Access policies will be represented as JSON objects.
The Access Policy Object is a JSON object with the following fields.
Key | Description |
---|---|
account_sid | The account sid this access policy is acting on behalf. |
version | The version of the access policy document format being used |
friendly_name | An optional human readable description string |
policies | An array of policy rule objects (See Policy Rule Object) |
The "policies" array contains policy rule objects.
Key | Description |
---|---|
url | The URL pattern of the rule. |
method | The HTTP method this rule matches. |
allow | true or false. Does this rule allow access? Defaults to false. |
post_filter | An object describing the www-form-encoded parameters and values that must be present to match this rule. If undefined, all parameters and values match. |
query_filter | An object describing the URL query parameters and values that must be present to match this rule. If undefined, all parameters and values match. |
The url
key defines a literal or pattern that will be matched against the request's URL.
This field should contain the URL without query parameters and may either be a literal string or end in a wildcard string.
Literal strings must exactly match the request URL for this rule to match.
There are two types of wildcard strings:
Immediate child wildcards cause a rule to match the next step in the path, but no further.
For example:
https://taskrouter.twilio.com/v1/Workspaces/*
Matches:
https://taskrouter.twilio.com/v1/Workspaces/WSxxx
But not these URLs:
https://taskrouter.twilio.com/v1/Workspaces/
https://taskrouter.twilio.com/v1/Workspaces/WSxxx/TaskQueues
Recursive wildcards cause a URL to match any child URL of this path.
For example:
https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**
Matches:
https://taskrouter.twilio.com/v1/Workspaces/WSxxx/TaskQueues
https://taskrouter.twilio.com/v1/Workspaces/WSxxx/TaskQueues/WQxxx
https://taskrouter.twilio.com/v1/Workspaces/WSxxx/Workers/WKxxx/Statistics
https://taskrouter.twilio.com/v1/Workspaces/WSxxx/Statistics
But not:
https://taskrouter.twilio.com/v1/Workspaces/WSxxxx
https://taskrouter.twilio.com/v1/Workspaces
Multiple Rule Matching
The HTTP method this rule applies to: GET
, POST
, DELETE
Allow may have a value of true or false. If the matching rule has an allow value of "true", the request is authorized. If the allow value is false, the request is explicitly rejected. The default for all rules is "false".
The post_filter
and query_filter
fields are used to match parameters provided in either the query string or www-form-post-params
.
The value is an object whose keys are the parameter names, and values are either a string literal that must be matched, or a matcher object that describes how the key is matched in more detail.
Both post and query filters use the same format.
String Literal Example
1"post_filter": {2"FriendlyName": "Alice"3}
This would match a request that contained a single www-form-encoded parameter FriendlyName with the value Alice. If the request contained any other form parameters, it would not match this rule.
Matcher Object Example
1"post_filter": {2"FriendlyName": {"required": true},3"Status": {"required": false},4"Foo": {"required": false, "value": "bar"}5}
Matcher objects allow for more complex matching rules. In the above example, the field FriendlyName is required, but will match any provided value. The field Status is optional, meaning this rule will match if its provided or not. If it is provided, any value will be allowed. The field "Foo" is optional as well, but if it is provided, it must have the value "bar".
Matcher Object
Key | Description |
---|---|
required | is this param required, true or false |
valueoptional. | if present, the value is a string literal that the value of the field must match. If not present, the field will match any value. |
In addition to our list of policies, we need to define the following in the payload:
If you are defining a Worker, you will also need to define the following:
If you are defining a Workspace, you will also need to define the following:
Two required policies in your JWT include the ability to connect to a web-socket channel and post messages on that web-socket to hit the REST API.
These are required since we want to authenicate and authorize the opening a websocket in the first place.
Subsequent events to GET
, POST
or DELETE
to other APIs will utilize this websocket.
For example:
1{2"url": "https://event-bridge.twilio.com/v1/wschannels/AccountSid/Channel",3"method": "GET",4"allow": true5},6{7"url": "https://event-bridge.twilio.com/v1/wschannels/AccountSid/Channel",8"method": "POST",9"allow": true10}
Let's take an example using the helper library and see what this breaks down to as a policy object:
1<?php2$workspaceCapability = new Services_Twilio_TaskRouter_Capability($accountSid, $authToken, $workspaceSid, $workspaceSid);3$workspaceCapability->allowFetchSubresources();4$workspaceCapability->allowDeleteSubresources();5$workspaceCapability->allowUpdatesSubresources();6$workspaceToken = $workspaceCapability->generateToken();
The policy object would look like the following:
1{2"url": "https://event-bridge.twilio.com/v1/wschannels/ACxxx/WSxxx",3"method": "GET",4"allow": true5},6{7"url": "https://event-bridge.twilio.com/v1/wschannels/ACxxx/WSxxx",8"method": "POST",9"allow": true10},11{12"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx",13"method": "GET",14"allow": true15},16{17"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**",18"method": "GET",19"allow": true20},21{22"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**",23"method": "POST",24"allow": true25},26{27"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**",28"method": "DELETE",29"allow": true30}
In total, the JWT payload would decode to:
1{2"version": "v1",3"friendly_name": "WSxxx",4"policies": [5{6"url": "https://event-bridge.twilio.com/v1/wschannels/ACxxx/WSxxx",7"method": "GET",8"allow": true9},10{11"url": "https://event-bridge.twilio.com/v1/wschannels/ACxxx/WSxxx",12"method": "POST",13"allow": true14},15{16"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx",17"method": "GET",18"allow": true19},20{21"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**",22"method": "GET",23"allow": true24},25{26"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**",27"method": "DELETE",28"allow": true29},30{31"url": "https://taskrouter.twilio.com/v1/Workspaces/WSxxx/**",32"method": "POST",33"allow": true34}35],36"iss": "ACxxx",37"exp": 1432251317,38"account_sid": "ACxxx",39"channel": "WSxxx",40"workspace_sid": "WSxxx"41}