Handle JWT Token validation failute in Azure API Management Service - azure

Hi I have added the jwt validate tag to one of the operation in my api management service as shown below
<!-- validate the bearerToken !-->
<validate-jwt header-name="Authorization" output-token-variable-name="jwtOutput">
<openid-config url="{{OPENIDConfigURL}}" />
<issuers>
<issuer>{{tokenIssuer}}</issuer>
</issuers>
</validate-jwt>
while testing the same with invalid JWT token I am getting internal server error but its not getting captured in my tag. Am i missing something here.
<on-error>
<base />
<return-response>
<set-status code="200" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
{{FailResponse}};
}</set-body>
</return-response>
</on-error>
Any leads on how to capture the error when the provided jwt token is invalid (for any reason)

I believe you're getting an internal server error because your policy on-error is invalid.
Have a look to this thread and adjust your policy on-error. You can basically use the sample they have available in that link. The errors list shows you the list of errors you can expect from the validate-jwt policy.

Related

Azure APIM with certificate authentication error using .pfx certificate

Helle, everyone.
I am experiencing issue with configuring APIm operation with InBound policy, which handled calls to remote endpoint. I have saved certificate which was provided to me by external service and using thumbprint in policie. Remote endpoint validates requests using certificate.
<policies>
<inbound>
<base />
<send-request mode="new" response-variable-name="result" timeout="300" ignore-error="false">
<set-url>https://ip:port/path</set-url>
<set-method>POST</set-method>
<set-header name="Accept" exists-action="override">
<value>*/*</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/xml</value>
</set-header>
<set-body>#(context.Request.Body.As<string>())</set-body>
<authentication-certificate thumbprint="thubprint" password="password" />
</send-request>
<return-response response-variable-name="result" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
But as a response I am getting 500 error
send-request (259.918 ms)
{
"messages": [
"Error occured while calling backend service.",
"The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.",
"The remote certificate is invalid according to the validation procedure."
]
}
Thanks beforehands. Would appreciate any help.
As discussed in the comments, adding gist as a community wiki answer to help community members who might face a similar issue.
But as a response I am getting 500 error
If you are using self-signed certificates, you will need to disable certificate chain validation for API Management to communicate with the backend system. Otherwise, it will return a 500 error code.
Try the following code snippet taken from the document:
$context = New-AzApiManagementContext -resourcegroup 'ContosoResourceGroup' -servicename 'ContosoAPIMService'
New-AzApiManagementBackend -Context $context -Url 'https://contoso.com/myapi' -Protocol http -SkipCertificateChainValidation $true
Note: As of now disabling certificate chain validation is only possible for backend policy.
You can refer to Azure API Management - Validate incoming client certificate and Send cert to backend, Is disabling Validate certificate chain safe? and Protect your APIs with Azure API Management

Accessing API Management API from Excel using organizational account

In our organization we have some people that uses Excel to access our APIs. We would like the APIs to be exposed by API Management and to be authenticated using Azure Active Directory.
In excel this can be achieved by using an "Organizational account", but currently I get the message "We couldn't authenticate with the credentials provided. Please try again"
I have followed this guide in order to set up an Active Directory Registered application and configured API Management accordingly.
One additional step I have done is to add the "WWW-Authenticate" header in following in API management configuration, in order to trigger Excel to do authentication against our AD tenant:
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>expected-aud</value>
</claim>
</required-claims>
</validate-jwt>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<choose>
<when condition="#(context.Response.StatusCode == 401)">
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer realm="<tenant-id>", error="invalid_token", error_description="Access Token missing or malformed.", authorization_uri="https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize", client_id="<client-id>"</value>
</set-header>
</when>
</choose>
<base />
</on-error>
When looking at the HTTP requests in Fiddler, the a request made to login.microsoft.com actually retrieves a token. This is the body of the response:
{
"token_type": "Bearer",
"scope": "some-scope user_impersonation",
"expires_in": "3599",
"ext_expires_in": "3599",
"expires_on": "1600932873",
"not_before": "1600928973",
"resource": "https://api-management-host",
"access_token": "<access-token>",
"refresh_token": "<refresh-token>",
"id_token": "<id-token>"
}
But the following request to API management does not contain the token - the authorization header only has the keyword Bearer:
Authorization: Bearer
Anyone out there that have any experience with this scenario - or can help me solve this issue?

How to pass the inbound request body to the backend service in Azure API Gateway

I am trying to generate the Active Directory Token via the Azure API gateway. For that i have created a API operation on my Azure API Gateway which accepts the following body parama.
{
"client_id" :"****************",
"scope":"https://graph.windows.net/.default",
"client_secret":"****************",
"grant_type":"client_credentials"
}
Whenever I try to test this the body is set for the inboud process but not able to forward the same to the backend service which is https://login.microsoftonline.com/{{ID}}/oauth2/v2.0/token/ so I modified my inboud policy as below but still no luck.
<set-method>POST</set-method>
<set-variable name="requestBodyData" value="#(context.Request.Body.As<string>(preserveContent: true))" />
<set-header name="Content-Type" exists-action="override">
<value>"application/x-www-form-urlencoded"</value>
</set-header>
<rewrite-uri template="/" />
<set-body>#{
return "client_id=*******&scope=https://graph.windows.net/.default&client_secret=*******&grant_type=client_credentials";
}</set-body>
<!-- Don't expose APIM subscription key to the backend. -->
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
Any leads would be appriciated.
Set <set-backend-service base-url="https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token" /> under inbound attribute.
Here is a similar issue you could refer to.
Got it , removed the headed and the method from the inbound policy and updated the inbound policy as :-
<set-variable name="client_id" value="********" />
<set-variable name="scope" value="********"" />
<set-variable name="client_secret" value="********" />
<set-variable name="grant_type" value="********" />
<rewrite-uri template="/" />
<set-body>#{
return "client_id="+(context.Variables["client_id"])+"&scope="+(context.Variables["scope"])+"&client_secret="+(context.Variables["client_secret"])+"&grant_type="+(context.Variables["grant_type"]);
}</set-body>
<!-- Don't expose APIM subscription key to the backend. -->
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />

Validating Authorization token in incoming requests with Azure API management and third party Authorization Server

I have to implement OAuth 2.0 in my project with a third party Authorization Server. Both my client and server are already registered on the AS. I have created an API management instance on Azure and imported the swagger APIs. I want every incoming request to be validated against my AS, so I just need to redirect the request to https://my-as.com/as/introspect.oauth2, and have the token validated. If the token is valid then let it proceed or else send 401. I was trying to implement this using "inbound processing" and referred to the following doc: https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad#configure-a-jwt-validation-policy-to-pre-authorize-requests.
The only problem is that instead of Azure AD I'm using a third party AS. I tried replacing the URL in sample XML code with my URL, but its not working.
How do I redirect requests to the Authorization Server for validating access token?
Adding following inbound policy worked:
<inbound>
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="#(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
<!-- Send request to Token Server to validate token (see RFC 7662) -->
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://my-as.com/as/introspect.oauth2</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>#($"grant_type=urn:pingidentity.com:oauth2:grant_type:validate_bearer&client_id=UoM&client_secret=somesecret&token={(string)context.Variables["token"]}")</set-body>
</send-request>
<choose>
<!-- Check active property in response -->
<when condition="#((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<!-- Return 401 Unauthorized with http-problem payload -->
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</when>
</choose>
<base />
</inbound>

Is it possible to use the subscription-key query string parameter with Azure API Management SOAP-passthrough?

We use API Management to expose several API's. One of the API's we expose is configured as a SOAP-passthrough API but we are facing some issues with it regarding authentication of APIM.
When we use the Ocp-Apim-Subscription-Key header for passing the query string it all works correct and the API is returning it's content correct.
When we use the subscription-key query string parameter the API is returning a 401 Unauthorized. I tested this behavior in Postman and changing the way of sending the subscription key is resulting in this behavior.
An implementation detail of this API is that it exposes an existing WSDL and routes this SOAPAction to an Azure Function via the policy. In the Application Insights of the function I can verify that the function is never invoked when I get a 401 but it is invoked when I get a successful call (using the header).
Is this normal behavior? Am I doing things wrong? Or is it a bug in APIM?
This might be an issue with the way we do routing for SOAP Passthrough. You will notice in the API setup that we add on a query parameter to identify the SoapAction that an operation will be matched to. It may be that your the api key query parameter is getting overwritten when adding the SoapAction parameter to the inbound request. I will investigate and let your know.
We currently use a workaround around this problem with the following policy. Instead of changing the backend-server url in the policy we send a request and set the response of that request as a response for this api. Below you can find our policy which is working with the subscription-key in the query string.
<policies>
<inbound>
<base />
<send-request mode="copy" response-variable-name="response" timeout="20" ignore-error="false">
<set-url>{{BackendServer_URL}}</set-url>
</send-request>
<!--return-response response-variable-name="reponse" /-->
<choose>
<!-- If StatusCode is not OK, return Unauthorized with the reason. -->
<when condition="#(((IResponse)context.Variables["response"]).StatusCode != 200)">
<return-response response-variable-name="reponse">
<set-status code="401" reason="Unauthorized" />
<set-body>#(((IResponse)context.Variables["response"]).Body.As<string>())</set-body>
</return-response>
</when>
<otherwise>
<return-response response-variable-name="reponse">
<set-status code="200" />
<set-header name="Content-Type" exists-action="override">
<value>text/xml; charset=utf-8</value>
</set-header>
<set-body>#(((IResponse)context.Variables["response"]).Body.As<string>())</set-body>
</return-response>
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

Resources