Eine Express-API absichern
Diese Schnellstartanleitung sichert eine Express-5-API mit Zugriffstoken von KeenAuth ab. Sie fügen die expressAuth-Middleware des Node.js-SDKs hinzu, verlangen einen Scope für eine Route und testen die API offline mit dem Test-Kit des SDKs. Express ist keine Abhängigkeit des SDKs; die Middleware funktioniert auch mit Express 4.
Die API hat drei Endpunkte:
| Endpunkt | Erfordert |
|---|---|
GET /api/public |
nichts |
GET /api/private |
ein gültiges Zugriffstoken für Ihre API |
GET /api/messages |
ein gültiges Zugriffstoken mit dem messages:read-Scope |
Vor dem Start
- Eine KeenAuth-Umgebung. Ihr Aussteller sieht aus wie
https://your-environment.eu.keenauth.app. - Eine API, die in der Umgebung registriert ist (API anlegen). Ihr
identifier, zum Beispielhttps://api.example.com, ist die Zielgruppe (Audience) der Token, die Ihre API akzeptiert. - Der
messages:read-Scope für diese API, gewährt der Anwendung, die sie aufruft. - Node.js 22 oder neuer.
1. SDK installieren
npm install @keenauth/node express@5.2.1
Das SDK ist noch nicht veröffentlicht. Bis dahin verlinkt das Beispiel auf den SDK-Quellcode in seinem package.json.
2. Konfigurieren
Die API liest ihre Konfiguration ausschließlich aus Umgebungsvariablen. Legen Sie sie in einer .env-Datei ab, die npm start mit node --env-file-if-exists=.env lädt, und committen Sie diese Datei niemals.
| Variable | Erforderlich | Bedeutung |
|---|---|---|
KEENAUTH_ISSUER |
ja | Der Aussteller Ihrer Umgebung |
KEENAUTH_AUDIENCE |
ja | Der Bezeichner Ihrer API |
PUBLIC_BASE_URL |
ja | Die URL, die Clients aufrufen (siehe Hinter einem Proxy) |
KEENAUTH_WEBHOOK_SECRET |
empfohlen | Das idp_whsec_-Signaturgeheimnis des Webhook-Endpunkts, der token.revoked zustellt (zwei, durch Komma getrennt, während Sie es rotieren). Ohne dieses erfährt die API nie von widerrufenen Token |
PORT |
nein | Listen-Port, Standard 8080 |
3. Routen absichern
createApp erstellt den Schutz und die Routen. expressAuth(protector) verlangt ein gültiges Token; mit { scopes } verlangt es zusätzlich Scopes. Der Handler liest die geprüften Claims aus req.auth.
export function createApp({ verifier, publicBaseUrl, revocation, webhookSecrets = [] }) {
const protector = createProtector(verifier, {
publicBaseUrl,
revocation,
// The error never contains the token or claim values; never log the Authorization or DPoP header.
onError: (err) => console.warn(`rejected: ${err.message}`),
});
const app = express();
app.disable("x-powered-by");
// No token needed.
app.get("/api/public", (req, res) => {
res.json({ message: "Hello from a public endpoint." });
});
// Any valid access token for this API.
app.get("/api/private", expressAuth(protector), (req, res) => {
res.json({ message: "Hello from a private endpoint.", sub: req.auth.subject });
});
// A valid access token that also carries the messages:read scope.
app.get("/api/messages", expressAuth(protector, { scopes: ["messages:read"] }), (req, res) => {
res.json({ messages: ["Hello from a scoped endpoint."] });
});
// token.revoked deliveries from KeenAuth; the handler reads the raw body itself, so no JSON parser runs before it.
if (webhookSecrets.length > 0) {
app.post(WEBHOOK_PATH, ...createWebhookHandler({ secrets: webhookSecrets, revocation }));
} else {
console.warn("KEENAUTH_WEBHOOK_SECRET is not set: token.revoked events are not received, so revoked tokens stay valid until they expire");
}
return app;
}
server.js erstellt aus der Konfiguration den echten Verifier. Der Verifier lädt die Signaturschlüssel von Ihrer Umgebung, sobald er das erste Token sieht.
const issuer = process.env.KEENAUTH_ISSUER;
const audience = process.env.KEENAUTH_AUDIENCE;
// The URL clients call: DPoP proofs are checked against it, never against the Host header.
const publicBaseUrl = process.env.PUBLIC_BASE_URL;
if (!issuer || !audience || !publicBaseUrl) {
console.error("KEENAUTH_ISSUER, KEENAUTH_AUDIENCE and PUBLIC_BASE_URL are required");
process.exit(1);
}
// createVerifier refuses an issuer under the reserved .invalid domain, so a test token can never pass here.
const verifier = createVerifier({ issuer, audience });
const revocation = createMemoryRevocationCache();
// The signing secret (idp_whsec_...) of the webhook endpoint that delivers token.revoked; comma separated while rotating.
const webhookSecrets = (process.env.KEENAUTH_WEBHOOK_SECRET ?? "").split(",").map((s) => s.trim()).filter(Boolean);
const app = createApp({ verifier, publicBaseUrl, revocation, webhookSecrets });
const port = Number(process.env.PORT || 8080);
app.listen(port, () => console.log(`listening on port ${port}`));
4. Ausführen und API aufrufen
npm start
Für einen ersten Aufruf holen Sie ein Token mit dem Client-Credentials-Grant einer Machine-to-Machine-Anwendung, die den messages:read-Scope auf Ihrer API hat:
ACCESS_TOKEN=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d resource=https://api.example.com \
-d scope=messages:read \
https://your-environment.eu.keenauth.app/oauth2/token | jq -r .access_token)
curl -i http://localhost:8080/api/messages -H "Authorization: Bearer $ACCESS_TOKEN"
5. Ohne Netzwerk testen
@keenauth/node/testing erzeugt Token, die mit einem öffentlichen Testschlüssel signiert sind, und createTestVerifier akzeptiert nur diese Token. Die Tests übergeben diesen Verifier an createApp und rufen die App auf einem lokalen Port auf, daher benötigen sie weder Netzwerk noch ein KeenAuth-Konto. Siehe Offline-Test-Kit.
test("scope check: 403 insufficient_scope without messages:read, 200 with it", async () => {
await expectProblem(await get("/api/messages", bearer(mintAccessToken(AUDIENCE))), 403, "insufficient_scope", /scope="messages:read"/);
const ok = await get("/api/messages", bearer(mintAccessToken(AUDIENCE, { claims: { scope: "profile messages:read" } })));
assert.equal(ok.status, 200);
});
npm test
Die Tests des Beispiels prüfen, dass ein gültiges Token akzeptiert wird und dass ein fehlendes, abgelaufenes oder für eine andere Zielgruppe ausgestelltes Token, ein fehlender Scope, ein Token in der URL-Abfrage und ein widerrufenes Token mit den unten stehenden Statuscodes abgelehnt werden. Sie senden außerdem ein DPoP-gebundenes Token mit einem Nachweis für PUBLIC_BASE_URL und prüfen, dass server. The tests also send signed token.revokeddeliveries: a valid one revokes the token, a forged, tampered or stale one gets400 and revokes nothing, and a repeated one has one effect.js mit dem Test-Aussteller den Start verweigert.
Antworten
| Anfrage | Status | code im Body |
|---|---|---|
Kein Authorization-Header |
401 |
missing_token |
| Abgelaufenes Token, Token für eine andere API, ungültige Signatur, widerrufenes Token | 401 |
invalid_token |
| Token ohne den erforderlichen Scope | 403 |
insufficient_scope |
Token in der URL-Abfrage (?access_token=) |
400 |
invalid_request |
Der Body ist application/problem+json, und der WWW-Authenticate-Header enthält die Standard-Challenge. Die vollständige Tabelle steht unter API mit Middleware absichern.
Hinter einem Proxy
Wenn Clients DPoP verwenden, prüft die Middleware, dass jeder Nachweis für die URL erstellt wurde, die der Client aufgerufen hat. Setzen Sie PUBLIC_BASE_URL auf die externe URL, die Clients aufrufen, zum Beispiel https://api.example.com, auch hinter einem Proxy oder Load Balancer. Die Variable ist erforderlich und muss https verwenden (http nur für localhost, 127.0.0.1 oder [::1]); ohne sie startet die Anwendung nicht. Die Middleware vertraut X-Forwarded-*-Headern niemals, unabhängig von der Einstellung trust proxy in Express.
Widerrufene Token
Ein Zugriffstoken bleibt gültig, bis es abläuft. Daher teilt KeenAuth Ihrer API mit, wenn Token widerrufen werden. Das Beispiel übergibt dem Schutz einen In-Memory-Widerrufscache und stellt unter POST /webhooks/keenauth (webhook.js) einen token.revoked-Webhook bereit, der diesen Cache füttert.
- Legen Sie im Dashboard einen Webhook-Endpunkt mit der URL
https://<your host>/webhooks/keenauthan, dertoken.revokedabonniert, und exportieren Sie sein Signaturgeheimnis alsKEENAUTH_WEBHOOK_SECRET. - Der Handler prüft die Signatur mit dem SDK auf dem rohen Body und antwortet mit
400, wenn sie ungültig ist oder der Zeitstempel mehr als fünf Minuten abweicht. Er ordnet den Selektor des Ereignisses (access_token,session,user,client,org,org_user,client_user) einer Widerrufsmarkierung zu und antwortet mit204; die Middleware lehnt danach jedes passende Token ab, das vornot_beforeausgestellt wurde. - KeenAuth liefert mindestens einmal aus, daher wendet der Handler jedes
webhook-idgenau einmal an. Kann die Markierung nicht gespeichert werden, antwortet er mit503, und KeenAuth liefert das Ereignis erneut. Andere Ereignistypen und Selektoren, auf die kein Zugriffstoken passen kann (refresh_family,api), ignoriert er.
Der Handler protokolliert die Ereignis-ID, den Selektortyp und den Grund, nie den Body, eine Signatur oder das Geheimnis. Jeder Prozess hat seinen eigenen Cache; bei mehreren Instanzen legen Sie die Markierungen in einen gemeinsamen Speicher hinter derselben revoke-Methode. Siehe Widerrufscache.
Nächste Schritte
- API mit Middleware absichern: Fastify, Step-up-Anforderungen, DPoP und alle Optionen.
- Token prüfen: der Verifier unter der Middleware.
- Offline-Test-Kit.