How To: Secure OpenAPI resources using JWTs and RBAC

This guide demonstrates the usage of JWT scopes to control access to paths of an OpenAPI described API.

Table of Contents

Resources

Membrane API Gateway GitHub
Curl Homepage

Configuration

Paste the following configuration into your proxies.xml file to set up:

<router>
  <api port="2000" name="Token Server">
    <request>
      <template>{
        "sub": "user@example.com",
        "aud": "shop",
        "scp": "inventory"
      }</template>
      <jwtSign>
        <jwk location="jwk.json"/>
      </jwtSign>
    </request>
    <return />
  </api>

  <api port="2010" name="Protected API">
    <openapi location="secure-shop-api.yml" validateSecurity="yes" />
    <!-- This performs and enforces JWT auth,
         meaning we will require a global security requirement
         in the OpenAPI description -->
    <jwtAuth expectedAud="shop">
      <jwks>
        <jwk location="jwk.json" />
      </jwks>
    </jwtAuth>
    <openapiValidator/>
  </api>
</router>

Prerequisites

  1. OpenAPI Description: Download the secure-shop-api.yaml and place it in the same directory as your proxies.xml file.

  2. JWK Generation: Generate a JSON Web Key file for JWT signing:

    ./membrane.sh generate-jwk -o ./jwk.json

    Place the generated jwk.json file in the same directory as your proxies.xml.

Authentication Flow

  1. Start Membrane:

    ./membrane.sh
  2. Obtain a JWT Token:


    TOKEN=$(curl localhost:2000)
  3. Access Protected Resources:

    Without token (will be denied):

    curl localhost:2010/shop/v2/products

    With token (authorized):

    curl localhost:2010/shop/v2/products -H "Authorization: Bearer $TOKEN"

The JWT token is generated with the scope "inventory", which permits operations on products but restricts access to vendor management.

Authorization with Role-Based Access Control

Your JWT contains specific scopes that determine which operations you can perform.

Example 1: Creating a Product (Allowed with "inventory" scope)

curl localhost:2010/shop/v2/products \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Mango+", "price": 2.79}'

Expected response: 201 Created

Example 2: Creating a Vendor (Restricted to "management" scope)

curl localhost:2010/shop/v2/vendors \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Fresh Fruits from France Ltd."}'

Expected response: Error - "Caller is not in scope management"

This operation fails because your token only has the "inventory" scope, which allows product creation but not vendor management.

<template>{
  "sub": "user@example.com",
  "aud": "shop",
  "scp": "inventory"
}</template>
<jwtSign>
  <jwk location="jwk.json"/>
</jwtSign>

To generate a token with different scopes, modify the scp value in the proxies.xml file. For full access, use "scp": "inventory management". Alternatively, this list can be formatted as a JSON array as well.

OpenAPI Description Set-Up

The OpenAPI description (secure-shop-api.yaml) defines the security requirements.
Here's how the security is configured:

Global Security Requirement

security:
  - jwt: []

Placing this at the root level specifies that all endpoints require JWT authentication by default.

Endpoint-Specific Scope Requirements

Certain operations require specific scopes to be present in the JWT:

paths:
  /products:
    post:
      # ...
      security:
        - jwt: [inventory]  # Requires "inventory" scope

  /vendors:
    post:
      # ...
      security:
        - jwt: [management]  # Requires "management" scope

Security Scheme Definition

The JWT security scheme is defined in the components section:

components:
  securitySchemes:
    # This is an arbitrary name, solely used for reference purposes
    jwt:
      type: oauth2 # Here we set the OAuth2 type, as it allows JWT auth.
                     Although we are not performing actual OAuth2.

With this setup, Membrane's <openapi validateSecurity="yes" /> configuration automatically enforces the scope requirements defined in the OpenAPI specification, ensuring that requests have the appropriate authorization.