End of Sale Notice:
F5 NGINX is announcing the End of Sale (EoS) for NGINX Management Suite API Connectivity Manager Module, effective January 1, 2024.
F5 maintains generous lifecycle policies that allow customers to continue support and receive product updates. Existing API Connectivity Manager Module customers can continue to use the product past the EoS date. License renewals are not available after September 30, 2024.
See our End of Sale announcement for more details.
OAS Security Schemes
F5 NGINX Management Suite API Connectivity Manager lets you secure APIs with OpenAPI Spec (OAS) security schemes. This tutorial provides step-by-step instructions for setting up basic authentication and API key authentication security schemes to secure your APIs and control access to authorized users or applications.
Overview
The OpenAPI Specification (OAS) allows you to specify authentication and authorization requirements, or security schemes, for your APIs, ensuring that only authorized users or applications can access them. These security schemes are applied globally, meaning they are enforced for all APIs within the OAS.
Note:
When configuring security schemes, keep in mind that path-level security requirements will not be applied and are ignored.
Listed below are the OAS security schemes that API Connectivity Manager supports.
OAS Authorization Schemes | ACM Supported Schemes |
---|---|
Basic Authentication | Supported APIs with this scheme expect credentials to be included in the HTTP Authorization request header. |
API Key Authentication | Supported The API Key security scheme uses a unique API key to authenticate requests. With this scheme, the API expects the API key to be passed as a query parameter or header in the HTTP request. |
OAuth2 JWT Assertion | Not supported |
OAuth2 Token Introspection | Not supported |
Before You Begin
To complete the instructions in this guide, you need the following:
- API Connectivity Manager is installed, licensed, and running
- One or more Infra and Service workspaces
- One or more Environments
Basic Authentication Security Scheme
Basic Authentication is a security scheme that is commonly used to authenticate HTTP requests. The request contains a header field in the form of Authorization: Basic <credentials>
, where <credentials>
is the Base64-encoded username and password joined by a single colon.
With Basic Authentication, API owners can restrict access to their APIs by requiring usernames and passwords; API access is granted only after a username and password are validated.
Security Consideration
We recommend using Basic Authentication only over encrypted channels such as HTTPS in order to minimize security risks.
Basic Authentication Scheme Definition
Field | Type | Required | Valid Value(s) | Description | Default |
---|---|---|---|---|---|
type |
string |
True | http |
The type of the security scheme. | N/A |
description |
string |
False | Of type string |
A short description for the security scheme. CommonMark syntax can be used for rich text representation. | N/A |
scheme |
string |
True | basic |
The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235. | N/A |
x-credentialForward |
bool |
False | one of [true, false] |
This field defines whether the basic authentication credential is proxy-forwarded to the backend service in the HTTP request Authorization header. | false |
x-errorReturnConditions. notSupplied.returnCode |
int |
False | In range 400-599 |
The error code that needs to be used by the NGINX data plane when basicAuth is not supplied. |
401 |
x-errorReturnConditions. noMatch.returnCode |
int |
False | In range 400-599 |
The error code that needs to be used by the NGINX data plane when invalid basicAuth is supplied . |
403 |
x-labels.targetPolicyName |
string |
False | Of type string |
The required target policy name set in policy metadata.labels.targetPolicyName . |
default |
Configure Basic Authentication Scheme
To configure a basic authentication scheme, take the following steps:
-
Create an API spec with the basic security scheme configured. In the following example, take a look at how
ExampleBasicAuth
is configured incomponent.securitySchemes
.POST https://{{NMS-FQDN}}/api/acm/v1/services/workspaces/{{proxyworkspacename}}/api-docs
Example POST/PUT JSON request: Create Basic Authentication API Spec
{ "openapi": "3.0.2", "info": { "title": "Swagger Petstore - OpenAPI 3.0", "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" }, "version": "1.0.17" }, "security": [ { "ExampleBasicAuth": [] } ], "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" }, "servers": [ { "url": "https://petstore3.swagger.io/api/v3" } ], "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } }, { "name": "user", "description": "Operations about user" } ], "paths": { "/pet": { "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "Update an existing pet by Id", "operationId": "updatePet", "requestBody": { "description": "Update an existent pet in the store", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } } }, "required": true }, "responses": { "200": { "description": "Successful operation", "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } } }, "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "Add a new pet to the store", "operationId": "addPet", "requestBody": { "description": "Create a new pet in the store", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } } }, "required": true }, "responses": { "200": { "description": "Successful operation", "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "405": { "description": "Invalid input" } } } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": false, "explode": true, "schema": { "type": "string", "default": "available", "enum": [ "available", "pending", "sold" ] } } ], "responses": { "200": { "description": "successful operation", "content": { "application/xml": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } }, "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } }, "400": { "description": "Invalid status value" } } } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": false, "explode": true, "schema": { "type": "array", "items": { "type": "string" } } } ], "responses": { "200": { "description": "successful operation", "content": { "application/xml": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } }, "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } }, "400": { "description": "Invalid tag value" } } } }, "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "successful operation", "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } } }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "schema": { "type": "integer", "format": "int64" } }, { "name": "name", "in": "query", "description": "Name of pet that needs to be updated", "schema": { "type": "string" } }, { "name": "status", "in": "query", "description": "Status of pet that needs to be updated", "schema": { "type": "string" } } ], "responses": { "405": { "description": "Invalid input" } } }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "parameters": [ { "name": "api_key", "in": "header", "description": "", "required": false, "schema": { "type": "string" } }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "400": { "description": "Invalid pet value" } } } } }, "components": { "securitySchemes": { "ExampleBasicAuth": { "type": "http", "description": "Basic Authentication Scheme", "scheme": "basic", "x-credentialForward": true, "x-errorReturnConditions": { "notSupplied": { "returnCode": 401 }, "noMatch": { "returnCode": 403 } }, "x-labels": { "targetPolicyName": "default" } } }, "schemas": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64", "example": 1 }, "name": { "type": "string", "example": "Dogs" } }, "xml": { "name": "category" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "tag" } }, "Pet": { "required": [ "name", "photoUrls" ], "type": "object", "properties": { "id": { "type": "integer", "format": "int64", "example": 10 }, "name": { "type": "string", "example": "doggie" }, "category": { "$ref": "#/components/schemas/Category" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "$ref": "#/components/schemas/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "pet" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } }, "xml": { "name": "##default" } } }, "requestBodies": { "Pet": { "description": "Pet object that needs to be added to the store", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } } } } } } }
-
Create a proxy with
specRef
referencing the spec from step 1. Since the referenced spec uses Basic Authentication, you need to include the necessary basic authentication credentials within the body of the POST/PUT request.POST https://{{NMS-FQDN}}/api/acm/v1/services/workspaces/{{proxyworkspacename}}/proxies
Example POST/PUT JSON request: Create Proxy referencing OAS spec with Basic Authentication scheme
{ "name": "petstore-proxy", "version": "v1", "specRef": "swagger-petstore-openapi-3-0-1-0-17", "proxyConfig": { "hostname": "{{environmentHostname}}", "ingress": { "basePath": "/api/v3", "basePathVersionAppendRule": "NONE" }, "backends": [ { "serviceName": "petstore-svc", "serviceTargets": [ { "hostname": "petstore3.swagger.io", "listener": { "enableTLS": true, "port": 443, "transportProtocol": "HTTP" } } ] } ], "policies": { "proxy-request-headers": [ { "action": { "proxyHeaders": { "proxyDefaultHeadersToBackend": false, "proxyCustomHeadersToBackend": [ { "key": "Host", "value": "stringValue.petstore3.swagger.io" } ] } } } ], "apikey-authn": [ { "data": [ { "apiKey": "<API key>", "clientID": "userA" } ] } ] } } }
Verify the GET request for the proxy. In the JSON response, you should see
policies.basic-authn
configured in theproxyConfig
section.GET https://{{NMS-FQDN}}/api/acm/v1/services/workspaces/{{proxyworkspacename}}/proxies/petstore-proxy?hostname={{environmentHostname}}&version=v1&includes=sensitivedata
Example GET JSON response: Proxy with basic-authn policy configured
{ "configs": [ { "configState": { "proxy": { "configState": { "action": "DEPLOY", "createTime": "2023-06-26T17:03:57Z", "instanceGroupRef": "test-ig", "jobID": "b777b4ed-579e-4070-af9c-aedeb78fb778", "jobLink": "/api/acm/v1/services/workspaces/service-ws/proxies/petstore-proxy/jobs/b777b4ed-579e-4070-af9c-aedeb78fb778", "status": "SUCCESS", "updateTime": "2023-06-26T17:03:59Z" }, "id": 1 } }, "metadata": { "kind": "proxy", "link": { "rel": "/api/acm/v1/services/workspaces/service-ws/proxies/petstore-proxy" }, "ref": "/api/acm/v1/services/workspaces/service-ws", "tags": [] }, "name": "petstore-proxy", "proxyConfig": { "backends": [ { "enableSRVRecordLookUp": false, "label": { "targetName": "default" }, "serviceContextRoot": "/", "serviceName": "petstore-svc", "serviceTargets": [ { "failTimeout": "10s", "hostname": "petstore3.swagger.io", "listener": { "enableTLS": true, "port": 443, "transportProtocol": "HTTP" }, "maxConnections": 0, "maxFails": 1, "serverDown": false } ] } ], "hostname": "apim-devenv-agent", "ingress": { "basePath": "/api/v3", "basePathVersionAppendRule": "NONE", "matchRule": "PREFIX", "routes": [ { "description": "Update an existing pet by Id", "httpMethod": "PUT", "parameters": [], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet" }, { "description": "Add a new pet to the store", "httpMethod": "POST", "parameters": [], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet" }, { "description": "Multiple status values can be provided with comma separated strings", "httpMethod": "GET", "parameters": [ { "description": "Status values that need to be considered for filter", "in": "QUERY", "name": "status", "required": false, "schema": { "type": "STRING" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/findByStatus" }, { "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "httpMethod": "GET", "parameters": [ { "description": "Tags to filter by", "in": "QUERY", "name": "tags", "required": false, "schema": { "type": "ARRAY" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/findByTags" }, { "description": "", "httpMethod": "DELETE", "parameters": [ { "description": "", "in": "HEADER", "name": "api_key", "required": false, "schema": { "type": "STRING" } }, { "description": "Pet id to delete", "in": "PATH", "name": "petId", "required": true, "schema": { "type": "INTEGER" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/{petId}" }, { "description": "Returns a single pet", "httpMethod": "GET", "parameters": [ { "description": "ID of pet to return", "in": "PATH", "name": "petId", "required": true, "schema": { "type": "INTEGER" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/{petId}" }, { "description": "", "httpMethod": "POST", "parameters": [ { "description": "ID of pet that needs to be updated", "in": "PATH", "name": "petId", "required": true, "schema": { "type": "INTEGER" } }, { "description": "Name of pet that needs to be updated", "in": "QUERY", "name": "name", "required": false, "schema": { "type": "STRING" } }, { "description": "Status of pet that needs to be updated", "in": "QUERY", "name": "status", "required": false, "schema": { "type": "STRING" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/{petId}" } ], "stripBasePathVersion": false }, "policies": { "basic-authn": [ { "action": { "credentialForward": true, "errorReturnConditions": { "notSupplied": { "grpcStatusCode": 0, "returnCode": 401 } } }, "data": [ { "clientID": "user1-clientid", "password": "secret", "source": "ACM", "username": "user1" } ], "metadata": { "labels": { "targetPolicyName": "default" } }, "systemMetadata": { "appliedOn": "inbound", "context": "proxy" } } ], "proxy-request-headers": [ { "action": { "proxyHeaders": { "proxyCustomHeadersToBackend": [ { "isSensitive": false, "key": "Host", "value": "stringValue.petstore3.swagger.io" } ], "proxyDefaultHeadersToBackend": false } }, "metadata": { "labels": { "targetPolicyName": "default" } }, "systemMetadata": { "appliedOn": "backend" } } ] } }, "specRef": "swagger-petstore-openapi-3-0-1-0-17", "status": "LATEST", "version": "v1", "_links": [ { "hostname": "apim-devenv-agent", "href": "/api/acm/v1/services/workspaces/service-ws/proxies/petstore-proxy?hostname=apim-devenv-agent", "rel": "SELF", "runtime": "GATEWAY-PROXY", "type": "API-Proxy" } ] } ], "metadata": { "pagination": { "links": { "next": {}, "prev": {} }, "pageCount": 1, "pageToken": "1687856101", "totalItems": 1 } } }
-
Pass traffic to the endpoints. The following example sends a request through a proxy to the Pet Store server, using Basic Authentication. A successful request will return a
200
status response.curl -X GET -u user1:secret -H "Content-Type: application/json" http://<ip address>/api/v3/pet/4 {"id":4,"category":{"id":1,"name":"{{$$randomFirstName}}"},"name":"<name>","photoUrls":["http://example.com/640/480/cats"],"tags":[{"id":0,"name":"string"}],"status":"available"}
In contrast, if the request lacks proper authentication, the response is “Unauthorized” with a status code of
401
curl -X GET -H "Content-Type: application/json" http://<ip address>/v1/pet { "message": "Unauthorized", "status": "401" }
API Key Authentication Security Scheme
The API Key Authentication security scheme uses a unique API key to authenticate requests. The API key is usually a long, randomly generated string provided to the client by the API owner. To access the API, the client must include this key as a query parameter or in the request header. The API owner can use this key to identify the client and authorize access to the API.
API Key Scheme Definition
Field | Type | Required | Valid Value(s) | Description | Default |
---|---|---|---|---|---|
type |
string |
True | apiKey |
The type of the security scheme. | N/A |
description |
string |
False | Of type string |
A short description for the security scheme. CommonMark syntax can be used for rich text representation. | N/A |
name |
string |
False | Of type |
The name of the header or query parameter to be used. | apiKey |
in |
string |
False | one of ["header", "query"] |
The location of the API key. | header |
x-credentialForward |
bool |
False | one of [true, false] |
This field defines whether the apiKey credential is proxy-forwarded to the backend service in the HTTP request header. |
false |
x-errorReturnConditions. notSupplied.returnCode |
int |
False | In range 400-599 |
The error code that needs to be used by the NGINX data plane when apiKey is not supplied. |
401 |
x-errorReturnConditions. noMatch.returnCode |
int |
False | In range 400-599 |
The error code that needs to be used by the NGINX data plane when an invalid apiKey is supplied . |
403 |
x-labels.targetPolicyName |
string |
False | Of type string |
The required target policy name set in policy metadata.labels.targetPolicyName . |
default |
Configure API Key Authentication Scheme
To configure the API Key Authentication security scheme, take the following steps:
-
Create an API spec with the
apiKey
security scheme configured. In the following example, take a look at howExampleApiKeyAuth
is configured incomponent.securitySchemes
.POST https://{{NMS-FQDN}}/api/acm/v1/services/workspaces/{{proxyworkspacename}}/api-docs
Example POST/PUT JSON request: Create API Key Authentication API Spec
{ "openapi": "3.0.2", "info": { "title": "Swagger Petstore - OpenAPI 3.0", "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" }, "version": "1.0.17" }, "security": [ { "ExampleApiKeyAuth": [] } ], "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" }, "servers": [ { "url": "https://petstore3.swagger.io/api/v3" } ], "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } }, { "name": "user", "description": "Operations about user" } ], "paths": { "/pet": { "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "Update an existing pet by Id", "operationId": "updatePet", "requestBody": { "description": "Update an existent pet in the store", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } } }, "required": true }, "responses": { "200": { "description": "Successful operation", "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } } }, "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "Add a new pet to the store", "operationId": "addPet", "requestBody": { "description": "Create a new pet in the store", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } } }, "required": true }, "responses": { "200": { "description": "Successful operation", "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "405": { "description": "Invalid input" } } } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": false, "explode": true, "schema": { "type": "string", "default": "available", "enum": [ "available", "pending", "sold" ] } } ], "responses": { "200": { "description": "successful operation", "content": { "application/xml": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } }, "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } }, "400": { "description": "Invalid status value" } } } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": false, "explode": true, "schema": { "type": "array", "items": { "type": "string" } } } ], "responses": { "200": { "description": "successful operation", "content": { "application/xml": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } }, "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } }, "400": { "description": "Invalid tag value" } } } }, "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "successful operation", "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } } }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "schema": { "type": "integer", "format": "int64" } }, { "name": "name", "in": "query", "description": "Name of pet that needs to be updated", "schema": { "type": "string" } }, { "name": "status", "in": "query", "description": "Status of pet that needs to be updated", "schema": { "type": "string" } } ], "responses": { "405": { "description": "Invalid input" } } }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "parameters": [ { "name": "api_key", "in": "header", "description": "", "required": false, "schema": { "type": "string" } }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "400": { "description": "Invalid pet value" } } } } }, "components": { "securitySchemes": { "ExampleApiKeyAuth": { "type": "apiKey", "description": "API Key Authn security scheme", "name": "X-API-Key", "in": "header", "x-credentialForward": true, "x-errorReturnConditions": { "notSupplied": { "returnCode": 401 }, "noMatch": { "returnCode": 403 } }, "x-labels": { "targetPolicyName": "default" } } }, "schemas": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64", "example": 1 }, "name": { "type": "string", "example": "Dogs" } }, "xml": { "name": "category" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "tag" } }, "Pet": { "required": [ "name", "photoUrls" ], "type": "object", "properties": { "id": { "type": "integer", "format": "int64", "example": 10 }, "name": { "type": "string", "example": "doggie" }, "category": { "$ref": "#/components/schemas/Category" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "$ref": "#/components/schemas/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "pet" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } }, "xml": { "name": "##default" } } }, "requestBodies": { "Pet": { "description": "Pet object that needs to be added to the store", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } } } } } } }
-
Create a proxy with
specRef
referencing the spec from step 1. See the following example for how your proxy request body should look withapikey-authn
configured on the policy.Note, since the referenced spec uses the API Key Authentication security scheme, you need to include the required API key within the POST/PUT request body.
POST https://{{NMS-FQDN}}/api/acm/v1/services/workspaces/{{proxyworkspacename}}/proxies
Example POST/PUT JSON request: Create Proxy referencing OAS spec with API Key Authentication scheme
{ "name": "petstore-proxy", "version": "v1", "specRef": "swagger-petstore-openapi-3-0-1-0-17", "proxyConfig": { "hostname": "{{environmentHostname}}", "ingress": { "basePath": "/api/v3", "basePathVersionAppendRule": "NONE" }, "backends": [ { "serviceName": "petstore-svc", "serviceTargets": [ { "hostname": "petstore3.swagger.io", "listener": { "enableTLS": true, "port": 443, "transportProtocol": "HTTP" } } ] } ], "policies": { "proxy-request-headers": [ { "action": { "proxyHeaders": { "proxyDefaultHeadersToBackend": false, "proxyCustomHeadersToBackend": [ { "key": "Host", "value": "stringValue.petstore3.swagger.io" } ] } } } ], "apikey-authn": [ { "data": [ { "apiKey": "<API key>", "clientID": "userA" } ] } ] } } }
Verify the GET request for the proxy. In the JSON response, you should see
policies.apikey-authn
in theproxyConfig
section.GET https://{{NMS-FQDN}}/api/acm/v1/services/workspaces/{{proxyworkspacename}}/proxies/petstore-proxy?hostname={{environmentHostname}}&version=v1&includes=sensitivedata
Example GET JSON response: Proxy with apikey-authn policy configured
{ "configs": [ { "configState": { "proxy": { "configState": { "action": "DEPLOY", "createTime": "2023-06-28T13:24:45Z", "instanceGroupRef": "acm-test", "jobID": "f7bc9bef-3969-4f73-97ef-ca498b7103d3", "jobLink": "/api/acm/v1/services/workspaces/service-ws/proxies/petstore-proxy/jobs/f7bc9bef-3969-4f73-97ef-ca498b7103d3", "status": "SUCCESS", "updateTime": "2023-06-28T13:24:48Z" }, "id": 1 } }, "metadata": { "kind": "proxy", "link": { "rel": "/api/acm/v1/services/workspaces/service-ws/proxies/petstore-proxy" }, "ref": "/api/acm/v1/services/workspaces/service-ws", "tags": [] }, "name": "petstore-proxy", "proxyConfig": { "backends": [ { "enableSRVRecordLookUp": false, "label": { "targetName": "default" }, "serviceContextRoot": "/", "serviceName": "petstore-svc", "serviceTargets": [ { "failTimeout": "10s", "hostname": "petstore3.swagger.io", "listener": { "enableTLS": true, "port": 443, "transportProtocol": "HTTP" }, "maxConnections": 0, "maxFails": 1, "serverDown": false } ] } ], "hostname": "apim-devenv-agent", "ingress": { "basePath": "/api/v3", "basePathVersionAppendRule": "NONE", "matchRule": "PREFIX", "routes": [ { "description": "Add a new pet to the store", "httpMethod": "POST", "parameters": [], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet" }, { "description": "Update an existing pet by Id", "httpMethod": "PUT", "parameters": [], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet" }, { "description": "Multiple status values can be provided with comma separated strings", "httpMethod": "GET", "parameters": [ { "description": "Status values that need to be considered for filter", "in": "QUERY", "name": "status", "required": false, "schema": { "type": "STRING" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/findByStatus" }, { "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "httpMethod": "GET", "parameters": [ { "description": "Tags to filter by", "in": "QUERY", "name": "tags", "required": false, "schema": { "type": "ARRAY" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/findByTags" }, { "description": "", "httpMethod": "POST", "parameters": [ { "description": "ID of pet that needs to be updated", "in": "PATH", "name": "petId", "required": true, "schema": { "type": "INTEGER" } }, { "description": "Name of pet that needs to be updated", "in": "QUERY", "name": "name", "required": false, "schema": { "type": "STRING" } }, { "description": "Status of pet that needs to be updated", "in": "QUERY", "name": "status", "required": false, "schema": { "type": "STRING" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/{petId}" }, { "description": "", "httpMethod": "DELETE", "parameters": [ { "description": "", "in": "HEADER", "name": "api_key", "required": false, "schema": { "type": "STRING" } }, { "description": "Pet id to delete", "in": "PATH", "name": "petId", "required": true, "schema": { "type": "INTEGER" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/{petId}" }, { "description": "Returns a single pet", "httpMethod": "GET", "parameters": [ { "description": "ID of pet to return", "in": "PATH", "name": "petId", "required": true, "schema": { "type": "INTEGER" } } ], "targetBackendServiceLabel": "default", "targetPolicyLabel": "default", "uri": "/pet/{petId}" } ], "stripBasePathVersion": false }, "policies": { "apikey-authn": [ { "action": { "apiKeyName": "X-API-Key", "credentialForward": true, "errorReturnConditions": { "noMatch": { "grpcStatusCode": 0, "returnCode": 403 }, "notSupplied": { "grpcStatusCode": 0, "returnCode": 401 } }, "suppliedIn": "header" }, "data": [ { "apiKey": "<API Key>", "clientID": "userA", "source": "ACM" } ], "systemMetadata": { "appliedOn": "inbound", "context": "proxy" } } ], "proxy-request-headers": [ { "action": { "proxyHeaders": { "proxyCustomHeadersToBackend": [ { "isSensitive": false, "key": "Host", "value": "stringValue.petstore3.swagger.io" } ], "proxyDefaultHeadersToBackend": false } }, "systemMetadata": { "appliedOn": "backend" } } ] } }, "specRef": "swagger-petstore-openapi-3-0-1-0-17", "status": "LATEST", "version": "v2", "_links": [ { "hostname": "apim-devenv-agent", "href": "/api/acm/v1/services/workspaces/service-ws/proxies/petstore-proxy?hostname=apim-devenv-agent", "rel": "SELF", "runtime": "GATEWAY-PROXY", "type": "API-Proxy" } ] } ], "metadata": { "pagination": { "links": { "next": {}, "prev": {} }, "pageCount": 1, "pageToken": "1687958759", "totalItems": 1 } } }
curl -X GET -H "apiKey:<API key>" -H "Content-Type: application/json" http://<ip address>/api/v3/pet/4 {"id":4,"category":{"id":1,"name":"{{$$randomFirstName}}"},"name":"<name>","photoUrls":["http://example.com/640/480/cats"],"tags":[{"id":0,"name":"string"}],"status":"available"}
If the request lacks proper authentication, the response is “Unauthorized” with a status code of
401
curl -X GET -H "Content-Type: application/json" http://<ip address>/v1/pet { "message": "Unauthorized", "status": "401" }