Getting Started
By the end of this document, you should have an understanding of how to integrate any of the Campfire SDK flows into your application, and be able to send messages and receive events from your chosen flow.
Choosing an import method
The Campfire SDK is available on NPM, which provides a variety of ways to integrate into your application. The package is available under @campfirelearning/lti-tool-consumer
Via <script />
The SDK can be imported via script tag using a service called unpkg. This will expose the SDK via window.CampfireSDK. If you are in a traditional HTML/JS environment, this is the way to go. Our JS SDK is available at the following URL:
https://unpkg.com/@campfirelearning/sdk-lti[@version]/dist/browser/index.js
Omitting @version in the URL above will cause Unpkg to always resolve to the latest version of the SDK. In production, we recommend that you specify a version to ensure that you do not introduce unexpected breaking changes in a newer SDK version.
<!DOCTYPE html>
<html lang="en">
<head>
<script
src="https://unpkg.com/@campfirelearning/sdk-lti/dist/browser/index.js"
>
</head>
<body>
<!-- ... -->
<script>
const service = new window.CampfireSDK.ItemEditor(/* ... */)
</script>
</body>
</html>
Via Package Manager
The SDK is also available directly via NPM, and can be imported. If you are using a framework like React, use this. This method also includes types if you are using TypeScript.
// package.json
{
//...
"dependencies": {
"@campfirelearning/lti-tool-consumer": "0.6.0"
}
}
import { ItemEditor } from "@campfirelearning/lti-tool-consumer";
export const initializeCampfireItemEditor = (/* ... */) => {
const service = new ItemEditor(/* ... */);
return service;
};
🔒 Authenticating
The Campfire SDK uses LTI Authentication. At its core, this method uses a shared application key/secret to sign the parameters we use when launching our application inside of the iframe. The shared secret must NEVER be shared with the browser as it can authorize the creation of new users/content/etc. into the target workspace.
You must implement a mechanism on your server to approve every user launch request. Failing to properly check the launch parameters WILL lead to users having access to content they should not have access to. Please read through LTI Authentication and Authorization to ensure you have covered your bases
🎤 Browser Security Considerations
To ensure the SDK has access to browser-level APIs (microphone, camera,
location, etc.), you will be required to attach an allow property to each
iframe that launches a Campfire SDK component with the features you wish to
allow inside of the component.
<iframe allow="microphone *" />
| Allow Value | Purpose |
|---|---|
microphone | Access to the device's microphone |
camera | Access to the device's camera |
Use * to grant access to all origins, or specify particular origins. You can
combine multiple permissions with spaces, for example: allow="microphone *; camera *"
Currently, Campfire only depends on microphone access being available in Audio
Response question types.
See https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/allow for
documentation related to allow.
🚀 Launching your first experience
Simply instantiate one of our SDK classes (ex: CampfireSDK.ExamEditor) with
the proper user data and credentials, then mount it to an iframe.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My First Campfire Application</title>
</head>
<body>
<div id="app">
<!-- Where the UI will load -->
<iframe id="tool" name="tool"></iframe>
</div>
<script>
// Step 1: Gather user data (from your server)
const user = {
email: "john.deaux@example.com",
firstName: "Jonathan"
lastName: "Deaux",
roles: "Instructor"
}
const APP_ID = "THE_DISTRICT_APP_KEY"
// Step 2: Instantiate
const service = new window.CampfireSDK.ExamEditor({
params: {
oauth_consumer_key: APP_ID,
resource_link_id: "0",
user_id: user.email,
lis_person_contact_email_primary: user.email,
lis_person_name_given: user.firstName,
lis_person_name_family: user.lastName,
lis_person_name_full: `${user.firstName} ${user.lastName}`,
roles: user.roles
},
signingFunction: /* ... */
});
// Step 3: Mount
consumer.mount("tool").then(() => {
console.log("Mounted!");
})
</script>
</body>
</html>
⬅️ Listening for Events
Each Campfire experience emits events that allow you to respond to happenings
inside of them. Events are emitted via the instantiated class and can be
listened to using addEventListener.
const service = new window.CampfireSDK.ExamEditor({
/* ... */
});
service.addEventListener("message", (message) => {
if (message.data.name === "examCreated") {
// do something
}
});
For a list of currently supported events, see Components.
Listening to unsupported events.
Our SDK components may fire events we consider to be unsupported. Either they
aren’t officially part of the SDK spec yet, or they’re being deprecated. In
either case, if you wish to listen for these unsupported events use the
message:unhandled message type.
const service = new window.CampfireSDK.ExamEditor({
/* ... */
});
service.addEventListener("message:unhandled", (message) => {
// do something
});
➡️ Sending Messages to SDK
Send information into the Campfire experience using sendMessage.
const service = new window.CampfireSDK.ExamEditor({
/* ... */
});
/* ... */
service.sendMessage({
type: "message",
name: "setGuidedExamFormData",
data: {
fields: [{ field: "exam:title", value: "My exam!" }],
},
});
For a list of currently supported messages, see Components
Full Example
You can copy and paste this into any code sandbox that runs HTML to launch our
Item Editor component after replacing the CONSUMER_KEY and SHARED_SECRET
with your actual credentials.
<!DOCTYPE html>
<html>
<head>
<title>Campfire LTI Integration</title>
<link
rel="stylesheet"
href="https://unpkg.com/modern-normalize@2.0.0/modern-normalize.css"
/>
<style type="text/css">
iframe {
width: 100%;
height: 90vh;
border: 1px solid #999;
}
div#app {
margin: 0 2rem;
}
</style>
<script src="https://unpkg.com/@campfirelearning/sdk-lti@latest/dist/browser/index.js"></script>
<script src="https://unpkg.com/@campfirelearning/sdk-lti-oauth@latest/dist/browser/index.js"></script>
</head>
<body>
<div id="app">
<h1>Campfire 🔥 SDK Integration</h1>
<iframe id="tool" name="tool"></iframe>
</div>
<script>
const CONSUMER_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const SHARED_SECRET = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const component = new CampfireSDK.ExamEditor({
params: {
oauth_consumer_key: CONSUMER_KEY,
lti_version: "LTI-1p0",
lti_message_type: "basic-lti-launch-request",
tool_consumer_info_product_family_code: "examspark",
tool_consumer_info_version: "cloud",
tool_consumer_instance_name: "cloud",
resource_link_id: "0",
user_id: "kevin@examspark.com",
lis_person_contact_email_primary: "kevin@examspark.com",
lis_person_name_given: "Kevin",
lis_person_name_family: "Campbell",
lis_person_name_full: "Kevin Campbell",
roles: [
"Instructor",
"urn:lti:instrole:ims/lis/Administrator",
"urn:lti:sysrole:ims/lis/SysAdmin",
].join(","),
},
signingFunction: (launchUrl) =>
CampfireOAuth.getUnsafeOauthSigningFunction({
launchUrl: launchUrl,
secretKey: SHARED_SECRET,
}),
});
component.mount("tool").then(() => {
console.log("Mounted!");
});
const debug = (message) => {
console.debug(message.type, ": ", message.data);
if (
message.data?.type === "event" &&
message.data.name === "launched"
) {
options?.onLaunch?.();
}
};
component.addEventListener("message", debug);
component.addEventListener("message:unhandled", debug);
</script>
</body>
</html>