Skip to content

Instantly share code, notes, and snippets.

@adophilus
Created March 5, 2025 22:43
Show Gist options
  • Save adophilus/5f3cd676e7c7aeadad6d75a70a05bf70 to your computer and use it in GitHub Desktop.
Save adophilus/5f3cd676e7c7aeadad6d75a70a05bf70 to your computer and use it in GitHub Desktop.
// This is the class for the `AccessToken`. The main purpose is to take in the claims and call the `toJwt` method to create a JWT.
var AccessToken = class {
apiKey;
roomId;
role;
/**
* Permissions for the token
*/
permissions;
/**
* Token Options object
*
* - `ttl`: Time to live for the token or expiration time. can be a number of seconds or a string describing a time span zeit/ms
* @example 6 * 60 * 60, "2 days", "10h", "7d"`
* @default 4h
*
* - `maxPeersAllowed`: Maximum number of peers allowed in the room (Optional)
*/
options = {
ttl: "4h"
};
/**
* custom app data for the peer
*/
metadata;
constructor(data) {
if (typeof document !== "undefined") {
logger.error(
"You should not include your API secret in your web client bundle.\n\nYour web client should request a token from your backend server which should then use "
);
}
if (!data.apiKey) {
throw new Error("api-key required");
}
if (!data.roomId) {
throw new Error("roomId required");
}
if (data.options?.metadata && estimateSize(data.options?.metadata) > MAX_METADATA_SIZE) {
throw new Error("Metadata size exceeds the limit of 5kb");
}
this.apiKey = data.apiKey;
this.roomId = data.roomId;
this.metadata = data.options?.metadata;
if (data.options?.ttl) this.options.ttl = data.options?.ttl;
if (data.options?.maxPeersAllowed !== void 0) {
if (typeof data.options.maxPeersAllowed !== "number" || data.options.maxPeersAllowed <= 0) {
throw new Error("maxPeersAllowed must be a positive number");
}
}
if (data.options?.maxPeersAllowed) {
this.options.maxPeersAllowed = data.options?.maxPeersAllowed;
}
if ("role" in data) {
if (isDefaultRole(data.role) && !("permissions" in data)) {
this.role = data.role;
this.permissions = ROLE_PERMISSIONS[data.role];
} else if (isDefaultRole(data.role) && "permissions" in data) {
this.role = data.role;
this.permissions = {
...ROLE_PERMISSIONS[data.role],
...data.permissions
};
} else if (typeof data.role === "string" && "permissions" in data) {
if (data.role.length > 20) {
throw new Error(`Custom role exceeds the limit of ${20} characters.`);
}
this.role = data.role;
this.permissions = {
...DEFAULT_PERMISSIONS,
...data.permissions
};
} else {
throw new Error(
`Permissions must be provided for custom role: ${data.role}.`
);
}
} else if ("permissions" in data) {
this.permissions = {
...DEFAULT_PERMISSIONS,
...data.permissions
};
} else {
throw new Error("Either a role or permissions must be provided.");
}
}
set updatePermissions(permissions) {
this.permissions = permissions;
}
set updateMetaData(data) {
if (data && estimateSize(data) > MAX_METADATA_SIZE) {
throw new Error("Metadata size exceeds the limit of 5kb");
}
this.metadata = data;
}
/**
* Generate a JWT token
* @returns JWT token
* @example
* ```typescript
* const accessToken = new AccessToken({...})
* accessToken.toJwt()
* ```
*/
async toJwt() {
const payload = {
roomId: this.roomId,
permissions: this.permissions,
role: this.role,
metadata: JSON.stringify(this.metadata)
};
console.log(payload)
if (this.options?.maxPeersAllowed) {
payload.options = {
maxPeersAllowed: this.options.maxPeersAllowed
};
}
const resp = await fetch(`${INFRA_URL}/api/v2/sdk/create-peer-token`, {
method: "POST",
body: JSON.stringify(payload),
headers: {
"Content-Type": "application/json",
"x-api-key": this.apiKey,
"Cache-Control": "no-store, max-age=0",
Pragma: "no-cache",
"x-sdk-version": SDK_VERSION
}
});
if (resp.status === 401) {
throw new Error("API key missing or invalid");
}
if (resp.status === 404) {
throw new Error("Room not found to be associated with the given API key");
}
const { token } = await resp.json();
return token;
}
};
// Here's a sample code of what's generated
const accessToken = new AccessToken({
apiKey,
roomId,
//available roles: Role.HOST, Role.CO_HOST, Role.SPEAKER, Role.LISTENER, Role.GUEST - depending on the privileges you want to give to the user
role: Role.SPEAKER,
//custom permissions give you more flexibility in terms of the user privileges than a pre-defined role
permissions: {
// admin: true,
canConsume: true,
canProduce: true,
canProduceSources: {
cam: true,
mic: true,
screen: true,
},
canRecvData: true,
canSendData: true,
canUpdateMetadata: true,
},
options: {
// metadata: {
// // you can add any custom attributes here which you want to associate with the user
// walletAddress: "mizanxali.eth"
// },
},
});
// Here's the generated JWT
// eyJhbGciOiJSUzI1NiJ9.eyJwZWVySWQiOiJwZWVySWQtRXl1TGY5YzZ2Z0xZZ1huVTNrMUFpIiwicHVycG9zZSI6IlNESyIsInJvb21JbmZvIjp7InJvb21UeXBlIjoiVklERU8iLCJyb29tTG9ja2VkIjp0cnVlLCJtdXRlT25FbnRyeSI6ZmFsc2UsInZpZGVvT25FbnRyeSI6ZmFsc2V9LCJyb29tSWQiOiJ5dGstcWR4dS1xZWsiLCJtdXRlT25FbnRyeSI6ZmFsc2UsInZpZGVvT25FbnRyeSI6ZmFsc2UsInJvb21UeXBlIjoiVklERU8iLCJyb2xlIjoic3BlYWtlciIsInBlcm1pc3Npb25zIjp7ImFkbWluIjpmYWxzZSwiY2FuQ29uc3VtZSI6dHJ1ZSwiY2FuUHJvZHVjZSI6dHJ1ZSwiY2FuUHJvZHVjZVNvdXJjZXMiOnsiY2FtIjp0cnVlLCJtaWMiOnRydWUsInNjcmVlbiI6dHJ1ZX0sImNhblNlbmREYXRhIjp0cnVlLCJjYW5SZWN2RGF0YSI6dHJ1ZSwiY2FuVXBkYXRlTWV0YWRhdGEiOnRydWV9LCJpYXQiOjE3NDEyMTM1MDcsImlzcyI6Ikh1ZGRsZTAxIiwiZXhwIjoxNzQxMjI0MzA3fQ.OYBcyMK507caFjTS7mVpaviV3DqoMc18kNUpJfjTVCtJ1VtK0r5xSlARwsje0vIyIosqIqMFQaonI0fESvQ_ISihFvtvJc - Gq9qMpU - tDzfF5V2v14MvD7w456TiKfFri_ZscPHJ2TJjzVn - a80tQuqggb31R7qqcWbb3LE211WbqkTrlvECOI0vyUETbh3sOMbrtdrkoVw4zNBC3EIhXCD_mD7cScJoGhmsknuQB2D9lGP8RzaEZk - ps - 7MnvztSFpC9tTGsEt - 2ohWobxzngryOtjnb - pdmCe6zwEefKIj6Ib0Xa4YLcqloVhp1YxfjaQlKwvHul4WSxtvgDkdnRiLMDV - 6NS56MXfpcd7sayx6_HtDw9ZpcgDUZw9jvbHv1WkbNmsWJdT95zvu1yOpntF8aSfmbTGFcZsug5s_sETXmjA6tW9PsEn_aagQclBfVB - s3Vty6sRbyCFxcYn6b0U5Nndf5nO3HygVxuysHpMC_BdeX2 - PjaZ2yhBZRnuTpdIiaUp - IhnSy3rEvFp2lrUewCNq5FQofkex - HNDJdw4 - 47cI_v4Lw1qvivpRZDT9i154Y2mGlj_uAK9 - LzVElWfkMQsJLQ_0uDp21FNbha9HwgF6 - lGqifJUhOpAWKvbkM - fWLuYHN1h8J6hYdCG5oYnJdMovj802obO3uv - E
// Here's the payload sent to the endpoint
// {
// roomId: "ytk-qdxu-qek",
// permissions: {
// admin: false,
// canConsume: true,
// canProduce: true,
// canProduceSources: {
// cam: true,
// mic: true,
// screen: true,
// },
// canRecvData: true,
// canSendData: true,
// canUpdateMetadata: true,
// },
// role: "speaker",
// metadata: undefined,
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment