Skip to content

Instantly share code, notes, and snippets.

@freele
Created May 4, 2023 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save freele/923dd195b4a2dce203b8d924989c3261 to your computer and use it in GitHub Desktop.
Save freele/923dd195b4a2dce203b8d924989c3261 to your computer and use it in GitHub Desktop.
import { TelecomServerInterface } from '../../../telecom-server-interface';
import { twilioEnv } from '../environment';
import { jwt } from 'twilio';
const AccessToken = jwt.AccessToken;
const { VoiceGrant, VideoGrant, TaskRouterGrant } = AccessToken;
const MAX_ALLOWED_SESSION_DURATION = 14400;
const util = jwt.taskrouter.util;
const TaskRouterCapability = jwt.taskrouter.TaskRouterCapability;
const Policy = TaskRouterCapability.Policy;
// To set up environmental variables, see http://twil.io/secure
const accountSid = twilioEnv.TWILIO_ACCOUNT_SID;
const authToken = twilioEnv.TWILIO_AUTH_TOKEN;
const workspaceSid = twilioEnv.TWILIO_TR_WORKSPACE_SID;
const TASKROUTER_BASE_URL = 'https://taskrouter.twilio.com';
const version = 'v1';
const generateTaskRouterToken = function (input: { workerSid: string }) {
const capability = new TaskRouterCapability({
accountSid: accountSid,
authToken: authToken,
workspaceSid: workspaceSid,
channelId: input.workerSid,
});
// Helper function to create Policy
function buildWorkspacePolicy(options?: {
resources?: string[];
method?: string;
}) {
const resources = options?.resources || [];
const urlComponents = [
TASKROUTER_BASE_URL,
version,
'Workspaces',
workspaceSid,
];
return new Policy({
url: urlComponents.concat(resources).join('/'),
method: options?.method || 'GET',
allow: true,
});
}
// Event Bridge Policies
const workerEventBridgePolicies = util.defaultEventBridgePolicies(
accountSid,
input.workerSid
);
const workspaceEventBridgePolicies = util.defaultEventBridgePolicies(
accountSid,
workspaceSid
);
const updateActivityPolicy = new Policy({
url: util.workersUrl(workspaceSid, input.workerSid),
method: 'POST',
postFilter: { ActivitySid: { required: true } },
allow: true,
});
// Worker Policies
const workerPolicies = util.defaultWorkerPolicies(
version,
workspaceSid,
input.workerSid
);
const workspacePolicies = [
// Workspace fetch Policy
buildWorkspacePolicy(),
// Workspace subresources fetch Policy
buildWorkspacePolicy({ resources: ['**'] }),
// Workspace Activities Update Policy
buildWorkspacePolicy({ resources: ['Activities'], method: 'POST' }),
// Workspace Activities Worker Reservations Policy
buildWorkspacePolicy({
resources: ['Workers', input.workerSid, 'Reservations', '**'],
method: 'POST',
}),
// Workspace accept reservation
buildWorkspacePolicy({
resources: ['Tasks', '**'],
method: 'POST',
}),
// Workspace Activities Worker Reservations Policy
// buildWorkspacePolicy({
// resources: ['Workers', input.workerSid, 'Activities'],
// method: 'POST',
// }),
];
workerEventBridgePolicies
.concat(workspaceEventBridgePolicies)
.concat(updateActivityPolicy)
.concat(workerPolicies)
.concat(workspacePolicies)
.forEach(function (policy) {
capability.addPolicy(policy);
});
return capability.toJwt();
};
const generateTaskRouterWorkspaceToken = function () {
const capability = new TaskRouterCapability({
accountSid: accountSid,
authToken: authToken,
workspaceSid: workspaceSid,
channelId: workspaceSid,
});
// Helper function to create Policy
function buildWorkspacePolicy(options?: {
resources?: string[];
method?: string;
}) {
options = options || {};
const resources = options.resources || [];
const urlComponents = [
TASKROUTER_BASE_URL,
version,
'Workspaces',
workspaceSid,
];
return new Policy({
url: urlComponents.concat(resources).join('/'),
method: options.method || 'GET',
allow: true,
});
}
// Event Bridge Policies
const eventBridgePolicies = util.defaultEventBridgePolicies(
accountSid,
workspaceSid
);
const workspacePolicies = [
// Workspace Policy
buildWorkspacePolicy(),
// Workspace subresources fetch Policy
buildWorkspacePolicy({ resources: ['**'] }),
// Workspace resources update Policy
buildWorkspacePolicy({ resources: ['**'], method: 'POST' }),
// Workspace resources delete Policy
// buildWorkspacePolicy({ resources: ['**'], method: 'DELETE' }),
];
eventBridgePolicies.concat(workspacePolicies).forEach((policy) => {
capability.addPolicy(policy);
});
return capability.toJwt();
};
const generateVideoToken = function (input: {
userId: string;
organisationId: string;
userType: string;
userProfileId?: string;
profileFirstName?: string;
profileLastName?: string;
profileTimeZone?: string;
}) {
const token = new AccessToken(
twilioEnv.TWILIO_ACCOUNT_SID,
twilioEnv.TWILIO_API_KEY_SID,
twilioEnv.TWILIO_API_KEY_SECRET,
{
ttl: MAX_ALLOWED_SESSION_DURATION,
}
);
// @todo find another way to store identity
// as we have limit or 128 characters https://www.twilio.com/docs/video/programmable-video-limits
token.identity = [
input.userType,
input.organisationId,
input.userId,
`${input.profileFirstName} ${input.profileLastName}` ?? 'no name',
input.profileTimeZone ?? 'empty',
input.userProfileId,
].join('__');
// Should never happen, but just in case. As soon as we get here, we need to fix the identity
if (token.identity.length > 128) {
throw new Error('Identity is too long. Max length is 128 characters');
}
console.log('token.identity:', token.identity);
// Add video grant to token
token.addGrant(new VideoGrant());
token.addGrant(new VoiceGrant());
token.addGrant(
new TaskRouterGrant({
role: 'worker',
})
);
// Return token
return token.toJwt();
};
export const getTelecomClientCredentials: TelecomServerInterface['getTelecomClientCredentials'] =
function (input) {
return {
videoToken: generateVideoToken(input),
taskRouterWorkspaceToken:
input.workerSid && generateTaskRouterWorkspaceToken(),
taskRouterToken:
input.workerSid &&
generateTaskRouterToken({ workerSid: input.workerSid }),
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment