Keycloak can secure your Node.js Express API by acting as an OAuth 2.0 authorization server, issuing JWTs that your API validates.

Let’s see this in action. Imagine a simple Express app that needs to protect a /users endpoint.

const express = require('express');
const jwt = require('express-jwt');
const jwksClient = require('jwks-rsa');

const app = express();

// Middleware to validate JWTs
const checkJwt = jwt({
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksUri: 'http://localhost:8080/realms/myrealm/.well-known/openid-configuration/jwks'
  }),
  audience: 'my-api-client',
  issuer: 'http://localhost:8080/realms/myrealm',
  algorithms: ['RS256']
});

// Protected route
app.get('/users', checkJwt, (req, res) => {
  res.json({ message: 'Hello from protected users endpoint!' });
});

app.listen(3000, () => {
  console.log('API listening on port 3000');
});

Here’s how it works under the hood:

  1. Keycloak Setup: You’ll need a Keycloak instance running. Inside Keycloak, create a realm (e.g., myrealm). Within that realm, create a client (e.g., my-api-client). This client represents your Node.js application. Configure it with a "Confidential" access type and set a "Valid Redirect URIs" (e.g., http://localhost:3000/*). Keycloak will generate signing keys for your realm.
  2. Node.js Integration:
    • Install necessary packages: npm install express express-jwt jwks-rsa.
    • The express-jwt middleware is the core of the protection. It intercepts incoming requests to protected routes.
    • jwks-rsa is used to fetch the public keys from Keycloak’s JWKS (JSON Web Key Set) endpoint. These keys are what express-jwt uses to verify the signature of the JWT.
    • secret: This is configured to use jwksRsa.expressJwtSecret which dynamically fetches the public keys from the JWKS URI provided by Keycloak. The cache and rateLimit options are crucial for performance and security in production.
    • audience: This should match the resource setting for your client in Keycloak. It ensures the token was intended for this specific API.
    • issuer: This must match the Keycloak realm URL. It verifies that the token was issued by the expected Keycloak instance.
    • algorithms: Specifies the signing algorithm used by Keycloak (typically RS256).
  3. Client Authentication: A separate client (e.g., a web app or mobile app) will authenticate with Keycloak using OAuth 2.0 flows (like Authorization Code Flow). Upon successful authentication, Keycloak issues an ID Token and an Access Token (both JWTs) to the client.
  4. API Request: The client then includes the Access Token in the Authorization header of requests to your protected Node.js API, usually as a Bearer token: Authorization: Bearer <access_token>.
  5. Token Validation: When your Node.js API receives a request to /users, the checkJwt middleware runs. It extracts the Bearer token, fetches Keycloak’s public keys using jwks-rsa, and verifies the JWT’s signature, expiration, audience, and issuer. If all checks pass, the middleware allows the request to proceed to your route handler. If not, it returns a 401 Unauthorized or 403 Forbidden error.

The most surprising thing about this setup is that your Node.js API doesn’t need to store any secrets or keys related to user authentication. All cryptographic verification is done against Keycloak’s public keys, which are readily available and regularly rotated by Keycloak itself. This dramatically simplifies secret management for your API services.

The next step is to handle different user roles and permissions, which are also encoded within the JWT claims.

Want structured learning?

Take the full Keycloak course →