The app kit (@arc/app-kit)
The app kit has what an app needs to talk to ARC safely, with no runtime dependencies (Node 18+):
| Export | Use it to |
|---|---|
verifyWebhook | Check a webhook's signature and timestamp, and read its event and environment. |
verifySessionToken | Check the session token an embedded admin extension gets from App Bridge. |
APP_EVENT_TOPICS, AppEvent, isTopic | Type the events you receive. |
APP_MANIFEST_JSON_SCHEMA | Validate your manifest before you publish a version. |
createArcAppHandler | Serve the two routes every app needs (webhooks, session tokens) on a Node http server. See the CLI. |
The kit also ships the arc CLI (arc app init | dev | deploy): see The arc app CLI. The API's
conformance suite checks the kit against ARC's own signers.
Verify a webhook
Hash the raw request body. A re-serialised copy of the parsed JSON has different bytes and fails.
import { verifyWebhook, isTopic } from '@arc/app-kit';
app.post('/arc/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const result = verifyWebhook({
rawBody: req.body, // a Buffer
headers: req.headers,
secret: process.env.ARC_CLIENT_SECRET!,
});
if (!result.ok) return res.status(401).end(); // result.reason says why
if (alreadyHandled(result.event.id)) return res.status(200).end(); // dedupe on the event id
if (isTopic(result.event, 'app.uninstalled')) {
// forget the installation
}
res.status(200).end();
});
- The signature is
v1=<hex HMAC-SHA256(secret, "<X-ARC-Timestamp>.<raw body>")>. During a client secret rotation ARC sends twov1=values; passsecret: [current, previous]to accept both. - A timestamp more than 300 seconds from your clock is refused (
toleranceSecondschanges it). result.environmentistestfor a test-mode organization (every dev tenant), otherwiselive.- Branch on
event.type. An event can come from a newer API version than your kit knows, so treat an unknowntypeas "ignore".
Verify a session token
An admin extension gets a 60-second token from App Bridge and sends it to your backend:
import { verifySessionToken } from '@arc/app-kit';
const claims = verifySessionToken(token, {
secret: process.env.ARC_CLIENT_SECRET!,
clientId: process.env.ARC_CLIENT_ID!,
});
if (!claims) return res.status(401).end();
// claims.dest = the organization, claims.sub = the user,
// claims.scopeRepIds = the reps whose records this user may see (null = all)
Only HS256 tokens signed with your client secret are accepted; alg: none and every other algorithm are
refused. Reject a jti you have already seen.
Validate a manifest
APP_MANIFEST_JSON_SCHEMA is JSON Schema (2020-12), generated from ARC's own manifest schema. Use any
validator, or point your editor at it. ARC validates again when you publish a version: scope and topic
names and redirect-URI rules are checked there.