Skip to main content

Quick start

From an empty project to a list of tours on screen. It assumes your issuer is already registered — a correctly signed token from an unregistered issuer is still rejected.

1. Install

npm install @thenoughtyfox/noughty-tours-web-sdk

2. Mint tokens from your backend

The SDK never sees your signing key. Your backend signs a short-lived JWT and hands it to the browser, behind your own session check:

your backend
import { SignJWT } from "jose";

const TTL_SECONDS = 900;

app.post("/api/auth-token", requireSession, async (req, res) => {
const { key, kid } = await getSigningKey();

const token = await new SignJWT({})
.setProtectedHeader({ alg: "ES256", kid, typ: "JWT" })
// Taken from the authenticated session, never from the request body.
.setSubject(req.session.organisationId)
.setIssuer("https://your-backend.com")
.setAudience("https://api.dev.sdk.thenoughtyfox.com")
.setIssuedAt()
.setExpirationTime(Math.floor(Date.now() / 1000) + TTL_SECONDS)
.sign(key);

res.json({ access_token: token, token_type: "Bearer", expires_in: TTL_SECONDS });
});

The full recipe, including publishing your JWK Set, is in Authentication.

3. Create a client

src/client.ts
import { NoughtyToursController } from "@thenoughtyfox/noughty-tours-web-sdk";

export const NTController = new NoughtyToursController({
getBearerToken: async () => {
const res = await fetch("/api/auth-token", { method: "POST" });
const { access_token } = await res.json();
return access_token;
}
});

4. Read some data

const tours = await NTController.tours.list();
const tour = await NTController.tours.get(tours[0].id);

for (const floor of tour.floors) {
for (const room of floor.rooms) {
console.log(room.name, room.scans.length);
}
}

That is a working integration. Stop here if you are building your own interface.

5. Or render a ready-made screen

Import the stylesheet once, wrap your tree in the two providers, and drop a component in:

src/App.tsx
import {
NoughtyToursProvider,
I18nProvider,
ToursList
} from "@thenoughtyfox/noughty-tours-web-sdk";

import "@thenoughtyfox/noughty-tours-web-sdk/style.css";
import { NTController } from "./client";

export function App() {
return (
<NoughtyToursProvider client={NTController}>
<I18nProvider defaultLocale="en">
<ToursList onSelect={tour => navigate(`/tours/${tour.id}`)} />
</I18nProvider>
</NoughtyToursProvider>
);
}

Each component is a page-level screen sized to the full window height, so give each one its own route rather than embedding it in an existing layout.

Where next