| title | Open Policy Agent (opa) | |||||
|---|---|---|---|---|---|---|
| keywords |
|
|||||
| description | The opa plugin integrates with Open Policy Agent, enabling unified policy definition and enforcement for authorization in API operations. |
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
The opa Plugin supports the integration with Open Policy Agent (OPA), a unified policy engine and framework that helps define and enforce authorization policies. Authorization logic is defined in Rego and stored in OPA.
Once configured, the OPA engine will evaluate the client request to a protected Route to determine whether the request should have access to the Upstream resource based on the defined policies.
| Name | Type | Required | Default | Valid values | Description |
|---|---|---|---|---|---|
| host | string | True | Address of the OPA server. | ||
| policy | string | True | Policy path to evaluate. The plugin appends this value to /v1/data/<policy>, so it must point to an OPA document/decision whose result is an object containing an allow field. The returned object can also include reason, headers, and status_code. For example, use a path such as rbac only if querying /v1/data/rbac returns an object with this structure, rather than a bare boolean result. |
||
| ssl_verify | boolean | False | true | If true, verify the OPA server's SSL certificate. | |
| timeout | integer | False | 3000 | [1, 60000] | Timeout for the HTTP call in milliseconds. |
| keepalive | boolean | False | true | If true, keep the connection alive for multiple requests. | |
| keepalive_timeout | integer | False | 60000 | >= 1000 | Idle time in milliseconds after which the connection is closed. |
| keepalive_pool | integer | False | 5 | >= 1 | The number of idle connections. |
| with_route | boolean | False | If true, send information of the current Route. | ||
| with_service | boolean | False | If true, send information of the current Service. | ||
| with_consumer | boolean | False | If true, send information of the current Consumer. Note that the Consumer information may include sensitive information such as the API key. Only set this option to true if you are sure it is safe to do so. |
||
| send_headers_upstream | array[string] | False | >= 1 item | List of header names to forward from the OPA response to the Upstream service when the request is allowed. |
The JSON below shows the data sent to the OPA service by APISIX:
{
"type": "http",
"request": {
"scheme": "http",
"path": "\/get",
"headers": {
"user-agent": "curl\/7.68.0",
"accept": "*\/*",
"host": "127.0.0.1:9080"
},
"query": {},
"port": 9080,
"method": "GET",
"host": "127.0.0.1"
},
"var": {
"timestamp": 1701234567,
"server_addr": "127.0.0.1",
"server_port": "9080",
"remote_port": "port",
"remote_addr": "ip address"
},
"route": {},
"service": {},
"consumer": {}
}Each of these keys are explained below:
typeindicates the request type (httporstream).requestis used when thetypeishttpand contains the basic request information (URL, headers etc).varcontains the basic information about the requested connection (IP, port, request timestamp etc).route,serviceandconsumercontains the same data as stored in APISIX and are only sent if theopaPlugin is configured on these objects.
The JSON below shows the response from the OPA service to APISIX:
{
"result": {
"allow": true,
"reason": "test",
"headers": {
"an": "header"
},
"status_code": 401
}
}The keys in the response are explained below:
allowis indispensable and indicates whether the request is allowed to be forwarded through APISIX.reason,headers, andstatus_codeare optional and are only returned when you configure a custom response.
:::note
You can fetch the admin_key from conf/config.yaml and save to an environment variable with the following command:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g'):::
Before proceeding, you should have a running OPA server. Start one using Docker or deploy it to Kubernetes:
<Tabs groupId="opa-setup" defaultValue="docker" values={[ {label: 'Docker', value: 'docker'}, {label: 'Kubernetes', value: 'kubernetes'} ]}>
docker run -d --name opa-server -p 8181:8181 openpolicyagent/opa:1.6.0 run --server --addr :8181 --log-level debugTo verify that the OPA server is installed and the port is exposed properly, run:
curl http://127.0.0.1:8181 | grep VersionYou should see a response similar to the following:
Version: 1.6.0
Create a Deployment and Service for OPA in your cluster:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: opa
spec:
replicas: 1
selector:
matchLabels:
app: opa
template:
metadata:
labels:
app: opa
spec:
containers:
- name: opa
image: openpolicyagent/opa:1.6.0
args:
- run
- --server
- --addr=:8181
- --log-level=debug
ports:
- containerPort: 8181
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: opa
spec:
selector:
app: opa
ports:
- port: 8181
targetPort: 8181Apply the configuration to your cluster:
kubectl apply -f opa-server.yamlWait for the OPA pod to be ready. Once ready, the OPA server will be available within the cluster at http://opa.aic.svc.cluster.local:8181. To push policies to it from outside the cluster, set up a port-forward:
kubectl port-forward -n aic svc/opa 8181:8181 &The following example implements a basic authorization policy in OPA to allow only GET requests.
Create an OPA policy that only allows HTTP GET requests:
curl "http://127.0.0.1:8181/v1/policies/getonly" -X PUT \
-H "Content-Type: text/plain" \
-d '
package getonly
default allow = false
allow if {
input.request.method == "GET"
}'Create a Route with the opa Plugin:
<Tabs groupId="api" defaultValue="admin-api" values={[ {label: 'Admin API', value: 'admin-api'}, {label: 'ADC', value: 'adc'}, {label: 'Ingress Controller', value: 'aic'} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "opa-route",
"uri": "/anything",
"plugins": {
"opa": {
"host": "http://127.0.0.1:8181",
"policy": "getonly"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'Update host to your OPA server address. The policy is set to getonly.
services:
- name: opa-service
routes:
- name: opa-route
uris:
- /anything
plugins:
opa:
host: "http://127.0.0.1:8181"
policy: getonly
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1Update host to your OPA server address. The policy is set to getonly.
Synchronize the configuration to the gateway:
adc sync -f adc.yaml<Tabs groupId="k8s-api" defaultValue="gateway-api" values={[ {label: 'Gateway API', value: 'gateway-api'}, {label: 'APISIX Ingress Controller', value: 'apisix-ingress-controller'} ]}>
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: opa-plugin-config
spec:
plugins:
- name: opa
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: getonly
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: opa-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: opa-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80Apply the configuration to your cluster:
kubectl apply -f opa-ic.yamlapiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: opa-route
spec:
ingressClassName: apisix
http:
- name: opa-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: opa
enable: true
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: getonlyApply the configuration to your cluster:
kubectl apply -f opa-ic.yamlTo verify the policy, send a GET request to the Route:
curl -i "http://127.0.0.1:9080/anything"You should receive an HTTP/1.1 200 OK response.
Send another request to the Route using PUT:
curl -i "http://127.0.0.1:9080/anything" -X PUTYou should receive an HTTP/1.1 403 Forbidden response.
The following example helps you understand the data and the format APISIX pushes to OPA to support authorization logic writing. The example continues with the policy and the Route in the last example.
Now, update the Plugin on the previously created Route to include Route information:
<Tabs groupId="api" defaultValue="admin-api" values={[ {label: 'Admin API', value: 'admin-api'}, {label: 'ADC', value: 'adc'}, {label: 'Ingress Controller', value: 'aic'} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes/opa-route" -X PATCH \
-H "X-API-KEY: ${admin_key}" \
-d '{
"plugins": {
"opa": {
"with_route": true
}
}
}'Update adc.yaml to add with_route: true:
services:
- name: opa-service
routes:
- name: opa-route
uris:
- /anything
plugins:
opa:
host: "http://127.0.0.1:8181"
policy: getonly
with_route: true
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1Synchronize the configuration to the gateway:
adc sync -f adc.yaml<Tabs groupId="k8s-api" defaultValue="gateway-api" values={[ {label: 'Gateway API', value: 'gateway-api'}, {label: 'APISIX Ingress Controller', value: 'apisix-ingress-controller'} ]}>
Update opa-ic.yaml to add with_route: true:
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: opa-plugin-config
spec:
plugins:
- name: opa
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: getonly
with_route: true
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: opa-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: opa-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80Apply the updated configuration to your cluster:
kubectl apply -f opa-ic.yamlUpdate opa-ic.yaml to add with_route: true:
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: opa-route
spec:
ingressClassName: apisix
http:
- name: opa-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: opa
enable: true
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: getonly
with_route: trueApply the updated configuration to your cluster:
kubectl apply -f opa-ic.yamlSend a request to the Route:
curl -i "http://127.0.0.1:9080/anything"In the OPA server log (with --log-level debug), the req_body will now include Route information in addition to the request and var fields.
The following example demonstrates how you can return a custom response code and message when the request is unauthorized.
Create an OPA policy that only allows HTTP GET requests and returns 302 with a custom message when the request is unauthorized:
curl "http://127.0.0.1:8181/v1/policies/customresp" -X PUT \
-H "Content-Type: text/plain" \
-d '
package customresp
default allow = false
allow if {
input.request.method == "GET"
}
reason := "The resource has temporarily moved. Please follow the new URL." if {
not allow
}
headers := {
"Location": "http://example.com/auth"
} if {
not allow
}
status_code := 302 if {
not allow
}
'Create a Route with the opa Plugin:
<Tabs groupId="api" defaultValue="admin-api" values={[ {label: 'Admin API', value: 'admin-api'}, {label: 'ADC', value: 'adc'}, {label: 'Ingress Controller', value: 'aic'} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "opa-route",
"uri": "/anything",
"plugins": {
"opa": {
"host": "http://127.0.0.1:8181",
"policy": "customresp"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'services:
- name: opa-service
routes:
- name: opa-route
uris:
- /anything
plugins:
opa:
host: "http://127.0.0.1:8181"
policy: customresp
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1Synchronize the configuration to the gateway:
adc sync -f adc.yaml<Tabs groupId="k8s-api" defaultValue="gateway-api" values={[ {label: 'Gateway API', value: 'gateway-api'}, {label: 'APISIX Ingress Controller', value: 'apisix-ingress-controller'} ]}>
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: opa-customresp-plugin-config
spec:
plugins:
- name: opa
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: customresp
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: opa-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: opa-customresp-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80Apply the configuration to your cluster:
kubectl apply -f opa-ic.yamlapiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: opa-route
spec:
ingressClassName: apisix
http:
- name: opa-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: opa
enable: true
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: customrespApply the configuration to your cluster:
kubectl apply -f opa-ic.yamlSend a GET request to the Route:
curl -i "http://127.0.0.1:9080/anything"You should receive an HTTP/1.1 200 OK response.
Send a POST request to the Route:
curl -i "http://127.0.0.1:9080/anything" -X POSTYou should receive an HTTP/1.1 302 Moved Temporarily response:
HTTP/1.1 302 Moved Temporarily
...
Location: http://example.com/auth
The resource has temporarily moved. Please follow the new URL.
The following example demonstrates how to implement authentication and RBAC using the jwt-auth and opa Plugins. You will be implementing RBAC logics such that:
- An
userrole can only read the Upstream resources. - An
adminrole can read and write the Upstream resources.
Create an OPA policy for RBAC of two example Consumers, where john has the user role and jane has the admin role:
curl "http://127.0.0.1:8181/v1/policies/rbac" -X PUT \
-H "Content-Type: text/plain" \
-d '
package rbac
# Assign roles to users
user_roles := {
"john": ["user"],
"jane": ["admin"]
}
# Map permissions to HTTP methods
permission_methods := {
"read": "GET",
"write": "POST"
}
# Assign role permissions
role_permissions := {
"user": ["read"],
"admin": ["read", "write"]
}
# Get JWT authorization token
bearer_token := t if {
t := input.request.headers.authorization
}
# Decode the token to get role and permission
token := {"payload": payload} if {
[_, payload, _] := io.jwt.decode(bearer_token)
}
# Normalize permission to a list
normalized_permissions := ps if {
ps := token.payload.permission
not is_string(ps)
}
normalized_permissions := [ps] if {
ps := token.payload.permission
is_string(ps)
}
# Implement RBAC logic
default allow = false
allow if {
# Look up the list of roles for the user
roles := user_roles[input.consumer.username]
# For each role in that list
r := roles[_]
# Look up the permissions list for the role
permissions := role_permissions[r]
# For each permission
p := permissions[_]
# Check if the permission matches the request method
permission_methods[p] == input.request.method
# Check if the normalized permissions include the permission
p in normalized_permissions
}
'Create two Consumers john and jane in APISIX and configure their jwt-auth Credentials:
<Tabs groupId="api" defaultValue="admin-api" values={[ {label: 'Admin API', value: 'admin-api'}, {label: 'ADC', value: 'adc'}, {label: 'Ingress Controller', value: 'aic'} ]}>
curl "http://127.0.0.1:9180/apisix/admin/consumers" \
-X PUT \
-H "X-API-KEY: ${admin_key}" \
-H "Content-Type: application/json" \
-d '{
"username": "john"
}'curl "http://127.0.0.1:9180/apisix/admin/consumers" \
-X PUT \
-H "X-API-KEY: ${admin_key}" \
-H "Content-Type: application/json" \
-d '{
"username": "jane"
}'Configure the jwt-auth Credentials for the Consumers, using the default algorithm HS256:
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "cred-john-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "john-key",
"secret": "john-hs256-secret-that-is-very-long"
}
}
}'curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "cred-jane-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jane-key",
"secret": "jane-hs256-secret-that-is-very-long"
}
}
}'consumers:
- username: john
credentials:
- name: cred-john-jwt-auth
type: jwt-auth
config:
key: john-key
secret: john-hs256-secret-that-is-very-long
- username: jane
credentials:
- name: cred-jane-jwt-auth
type: jwt-auth
config:
key: jane-key
secret: jane-hs256-secret-that-is-very-longSynchronize the configuration to the gateway:
adc sync -f adc.yaml<Tabs groupId="k8s-api" defaultValue="gateway-api" values={[ {label: 'Gateway API', value: 'gateway-api'}, {label: 'APISIX Ingress Controller', value: 'apisix-ingress-controller'} ]}>
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: john
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: cred-john-jwt-auth
config:
key: john-key
secret: john-hs256-secret-that-is-very-long
---
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jane
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: cred-jane-jwt-auth
config:
key: jane-key
secret: jane-hs256-secret-that-is-very-longApply the configuration to your cluster:
kubectl apply -f opa-consumers-ic.yamlWhen using the Ingress Controller, APISIX prefixes Consumer names with the Kubernetes namespace. For example, a Consumer named john in the aic namespace becomes aic_john. Update the OPA RBAC policy to use the prefixed names accordingly.
The ApisixConsumer CRD has a known issue where private_key is incorrectly required during the configuration. This issue will be addressed in a future release. At the moment, the example cannot be completed with APISIX CRDs.
Create a Route and configure the jwt-auth and opa Plugins:
<Tabs groupId="api" defaultValue="admin-api" values={[ {label: 'Admin API', value: 'admin-api'}, {label: 'ADC', value: 'adc'}, {label: 'Ingress Controller', value: 'aic'} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "opa-route",
"methods": ["GET", "POST"],
"uris": ["/get","/post"],
"plugins": {
"jwt-auth": {},
"opa": {
"host": "http://127.0.0.1:8181",
"policy": "rbac",
"with_consumer": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'Update adc.yaml to add the Route with jwt-auth and opa Plugins:
consumers:
- username: john
credentials:
- name: cred-john-jwt-auth
type: jwt-auth
config:
key: john-key
secret: john-hs256-secret-that-is-very-long
- username: jane
credentials:
- name: cred-jane-jwt-auth
type: jwt-auth
config:
key: jane-key
secret: jane-hs256-secret-that-is-very-long
services:
- name: opa-service
routes:
- name: opa-route
uris:
- /get
- /post
methods:
- GET
- POST
plugins:
jwt-auth: {}
opa:
host: "http://127.0.0.1:8181"
policy: rbac
with_consumer: true
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1Synchronize the configuration to the gateway:
adc sync -f adc.yaml<Tabs groupId="k8s-api" defaultValue="gateway-api" values={[ {label: 'Gateway API', value: 'gateway-api'}, {label: 'APISIX Ingress Controller', value: 'apisix-ingress-controller'} ]}>
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: opa-rbac-plugin-config
spec:
plugins:
- name: jwt-auth
config:
_meta:
disable: false
- name: opa
config:
host: "http://opa.aic.svc.cluster.local:8181"
policy: rbac
with_consumer: true
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: opa-rbac-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /get
method: GET
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: opa-rbac-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
- matches:
- path:
type: Exact
value: /post
method: POST
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: opa-rbac-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80Apply the configuration to your cluster:
kubectl apply -f opa-route-ic.yamlThe ApisixConsumer CRD has a known issue where private_key is incorrectly required during the configuration. This issue will be addressed in a future release. At the moment, the example cannot be completed with APISIX CRDs.
To issue a JWT for john, you could use JWT.io's JWT encoder or other utilities. If you are using JWT.io's JWT encoder, do the following:
- Fill in
HS256as the algorithm. - Update the secret in the Valid secret section to be
john-hs256-secret-that-is-very-long. - Update payload with role
user, permissionread, and Consumer keyjohn-key; as well asexpornbfin UNIX timestamp.
Your payload should look similar to the following:
{
"role": "user",
"permission": "read",
"key": "john-key",
"nbf": 1729132271
}Copy the generated JWT and save to a variable:
export john_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsInBlcm1pc3Npb24iOiJyZWFkIiwia2V5Ijoiam9obi1rZXkiLCJuYmYiOjE3MjkxMzIyNzF9.rAHMTQfnnGFnKYc3am_lpE9pZ9E8EaOT_NBQ5Ss8pk4
Send a GET request to the Route with the JWT of john:
curl -i "http://127.0.0.1:9080/get" -H "Authorization: Bearer ${john_jwt_token}"You should receive an HTTP/1.1 200 OK response.
Send a POST request to the Route with the same JWT:
curl -i "http://127.0.0.1:9080/post" -X POST -H "Authorization: Bearer ${john_jwt_token}"You should receive an HTTP/1.1 403 Forbidden response.
Similarly, to issue a JWT for jane, you could use JWT.io's JWT encoder or other utilities. If you are using JWT.io's JWT encoder, do the following:
- Fill in
HS256as the algorithm. - Update the secret in the Valid secret section to be
jane-hs256-secret-that-is-very-long. - Update payload with role
admin, permission["read","write"], and Consumer keyjane-key; as well asexpornbfin UNIX timestamp.
Your payload should look similar to the following:
{
"role": "admin",
"permission": ["read","write"],
"key": "jane-key",
"nbf": 1729132271
}Copy the generated JWT and save to a variable:
export jane_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJwZXJtaXNzaW9uIjpbInJlYWQiLCJ3cml0ZSJdLCJrZXkiOiJqYW5lLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.meZ-AaGHUPwN_GvVOE3IkKuAJ1wqlCguaXf3gm3Ww8s
Send a GET request to the Route with the JWT of jane:
curl -i "http://127.0.0.1:9080/get" -H "Authorization: Bearer ${jane_jwt_token}"You should receive an HTTP/1.1 200 OK response.
Send a POST request to the Route with the same JWT:
curl -i "http://127.0.0.1:9080/post" -X POST -H "Authorization: Bearer ${jane_jwt_token}"You should also receive an HTTP/1.1 200 OK response.