This guide demonstrates the usage of JWT scopes to control access to paths of an OpenAPI described API.
Membrane API Gateway | GitHub |
Curl | Homepage |
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>
OpenAPI Description: Download the secure-shop-api.yaml and place it in the same directory as your proxies.xml
file.
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
.
Start Membrane:
./membrane.sh
Obtain a JWT Token:
TOKEN=$(curl localhost:2000)
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.
Your JWT contains specific scopes that determine which operations you can perform.
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
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.
The OpenAPI description (secure-shop-api.yaml) defines the security requirements.
Here's how the security is configured:
security:
- jwt: []
Placing this at the root level specifies that all endpoints require JWT authentication by default.
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
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.