JWT Validation policy suddently not valid when changed to a different audience with Azure API Management - azure

I have a strange problem changing the value of audience element below for JWT validation within APIM, ref link below
https://learn.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#ValidateJWT
1 new link:
https://new.onelogin.com/oidc/token
I only changed the value of audience element from the old version in 2. But I get the validation error below from APIM portal when I tried to save the policy:
The element 'validate-jwt' has invalid child element 'openid-config'. List of possible elements expected: 'required-claims'.
Please note that the old version in 2 doesn’t need the 'required-claims' element.
client_id=new xxx
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Error: expired token or invalid token" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
<audiences>
<audience>new xxx</audience>
</audiences>
<issuers>
<issuer>https://openid-connect-eu.onelogin.com/oidc</issuer>
</issuers>
<openid-config url="https://openid-connect-eu.onelogin.com/oidc/.well-known/openid-configuration" />
</validate-jwt>
2 The old url and jwt validation, and it works.
https://old.onelogin.com/oidc/token
client_id=old xxx
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Error: expired token or invalid token" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
<audiences>
<audience>old xxx</audience>
</audiences>
<issuers>
<issuer>https://openid-connect-eu.onelogin.com/oidc</issuer>
</issuers>
<openid-config url="https://openid-connect-eu.onelogin.com/oidc/.well-known/openid-configuration" />
</validate-jwt>
Any idea?
Updates:
Now even the original policy that was working has the issue even nothing has been changed:
The element 'validate-jwt' has invalid child element 'openid-config'. List of possible elements expected: 'required-claims'.

You need to move the openid-configup in the xml and keep it just under validate-jwtopening tag. Please see below:
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
<openid-config url="" />
<issuer-signing-keys>
<key>Base64 Encoded Key</key>
</issuer-signing-keys>
<audiences>
<audience></audience>
</audiences>
<issuers>
<issuer></issuer>
</issuers>
</validate-jwt>

Related

Azure APIM policy with dynamic claims in token validation

I am using APIM to expose multiple endpoints from a range of different Azure Functions as operations in the same API. Different operations in this API might have different audiences (aud) in the token based on which function is behind that particular operation.
What might differ though, is that the authorized party (azp) is not always the same. According to the OIDC documentation, this should then also be verified. In some cases, requests originate from a single page application where the token is issued on behalf of a user that signed in. In other cases, the request originates from someone integrating towards my APIs using a principal instead and a client_credentials grant type.
What I want to achieve is to have as simple a policy as possible, and still allow a range of different azp claims for a single aud claim. This is how I'm currently doing this:
<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/{{tenantId}}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all">
<value>{{target-api-clientId}}</value>
</claim>
<claim name="azp" match="any">
<value>{{spa-clientId}}</value>
<value>{{integrator-clientId1}}</value>
<value>{{integrator-clientId2}}</value>
</claim>
</required-claims>
</validate-jwt>
However, this doesn't scale very well and forces me to keep duplicate policies between environments and for each new allowed authorized party I must add a new row in the azp claim section as a well as new named values.
The documentation specifies that there is a separator attribute for the claim element, but as far as I can tell, this only splits the claim in the incoming token on that separator character and I always only have a single aud and azp claim in the incoming tokens.
Is there any way for me to achieve a single API baseline policy, where each operation could set its expected required parameters? Something along these lines:
API base policy
<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/{{tenantId}}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all">
<value>#((string)context.Variables["aud"])</value>
</claim>
<claim name="azp" match="any">
#((List<string>)context.Variables["azp"])
</claim>
</required-claims>
</validate-jwt>
Operation specific policy
<set-variable name="aud" value="{{allowed-aud-for-this-operation}}" />
<set-variable name="azp" value="{{list-of-allowed-authorized-parties-for-this-operation}}" />
<base />
To further clarify what the infrastructure looks like and where requests might originate from, this might shine some light:
Is there any way to achieve dynamic token evaluation like this? If not, what are my other options? Am I approaching this problem the right way, or should I rethink?

Azure B2C - SAML - The service provider is not a valid audience of the assertion

I set up a SAML Provider for Azure B2C (Single Sign On). The IDP is happy and sends a SAML Assertion back, but Azure B2C complains.
It looks like Azure B2C cannot process the SAML Answer. The Error Message extracted via AppInsights is simple "The service provider is not a valid audience of the assertion".
The URLs seem to fit, but im not sure if the AudienceRestriction should point to the base policy or maybe the sign-in policy, as teh metadata can only be accessed via the SIGN-UP Policy as part of the URL and not the BASE:
"https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/samlp/metadata?idptp={IDP-NAME}-SAML2"
Configuration (updated according to the comments of this question).
Configuration
<EntityDescriptor
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
ID="https://{IDP-URL}/saml/2.0/idp/"
entityID="https://{IDP-URL}/saml/2.0/idp/"
validUntil="2099-12-31T23:59:59Z">
<SPSSODescriptor
AuthnRequestsSigned="false"
WantAssertionsSigned="false"
ResponsesSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<AssertionConsumerService
index="0"
isDefault="true"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_TrustFrameworkBase/samlp/sso/assertionconsumer" />
</SPSSODescriptor>
<IDPSSODescriptor
WantAuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://{IDP-URL}/saml/2.0/idp/" />
</IDPSSODescriptor>
</EntityDescriptor>
SAML Response
<saml2p:Response Destination="https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_TrustFrameworkBase/samlp/sso/assertionconsumer"
ID="Response_6793aae6a9cc629a6be69a270731961695dad50e"
InResponseTo="_719e3407-dbad-4761-8e8a-7e7272b2a67b"
IssueInstant="2022-08-17T08:34:29.112Z"
Version="2.0"
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
>
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://{IDP-URL}/saml/2.0/idp/</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</saml2p:Status>
<saml2:Assertion ID="Assertion_16dc5f9b7c67ed241c79436c20296a2fd514ea87"
IssueInstant="2022-08-17T08:34:29.111Z"
Version="2.0"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<saml2:Issuer>https://{IDP-URL}/saml/2.0/idp/</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<ds:Reference URI="#Assertion_16dc5f9b7c67ed241c79436c20296a2fd514ea87">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="xs"
xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"
/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>/4OAX07/scGvFyDCT3BBzlHJQ7q65Ak0uGlTvE0z904=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>{removed}</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>{removed}</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">71747</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData InResponseTo="_719e3407-dbad-4761-8e8a-7e7272b2a67b"
NotOnOrAfter="2022-08-17T08:34:39.112Z"
Recipient="https://{my-tenant-name}/{my-tenant-name}.onmicrosoft.com/B2C_1A_TrustFrameworkBase/samlp/sso/assertionconsumer"
/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2022-08-17T08:34:29.111Z"
NotOnOrAfter="2022-08-17T08:34:59.111Z"
>
<saml2:AudienceRestriction>
<saml2:Audience>https://{my-tenant-name}/{my-tenant-name}.onmicrosoft.com/B2C_1A_TrustFrameworkBase/samlp/sso/assertionconsumer</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2022-08-17T08:34:29.111Z"
SessionNotOnOrAfter="2022-08-17T10:34:29.111Z"
>
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
My SAML METADATA
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" ID="_7865bef4-f0d2-4062-9e58-a145e1beb91f" entityID="https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_TrustFrameworkBase">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="#_7865bef4-f0d2-4062-9e58-a145e1beb91f">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="saml samlp xenc xs"/>
</Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>bSqD69uUzX0swY6hAKkzLEaWJeMyV2UIoyFBTxLhHi0=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>AiyK11or3hAGSnHADidHKp2XCcH0amBaU8xA6xQrxgKNZBqaKFF0rdbiRdVp4YFbqlQeCruYAEbT1JnAAwRyKLn6IZOJIP7iA3PeSr8bguus58+LGmb30YsYcbpAMxLjhQbmAu1t25v4huaOViZQwNAafkdjKAyhJRa7P8ihEBbl4CUQjYJ0eIASxWZuD6j1tg4afMv9GH809lFGl7KRER8oUp9P6VF5xdJbavpX623eRZRAeqV++CBXKTrFUnMOHrp1eI68IaobKOx/xkN59FX7SabdqpIVE+L9rnPtYYwG0LLpqmAOaSZEhmGKp2y27OA1ZxJZittwFMTmWmtoqA==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>{removed}</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
<SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>{removed}</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_signup_signin/samlp/sso/logout" ResponseLocation="https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_signup_signin/samlp/sso/logout/response"/>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://{my-tenant-name}.b2clogin.com/{my-tenant-name}.onmicrosoft.com/B2C_1A_TrustFrameworkBase/samlp/sso/assertionconsumer" index="0" isDefault="true"/>
</SPSSODescriptor>
</EntityDescriptor>
Please check below points:
Subject contains a NameID element, which represents the authenticated user. The NameID value is a targeted identifier that is directed only to the service provider that is the audience for the token. It is persistent - it can be revoked, but is never reassigned.
nameidpolicys element requests a particular name ID format in the response
ex:
<NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
The Format attribute can have only one of the following values;
Persistent, emailAddress, unspecified, transient
For urn:oasis:names:tc:SAML:2.0:nameid-format:persistent : Azure
Active Directory issues the NameID claim as a pairwise identifier.
For urn:oasis:names:tc:SAML:2.0:nameid-format:transient: Azure Active
Directory issues the NameID claim as a randomly generated value that
is unique to the current SSO operation. This means that the value is
temporary and cannot be used to identify the authenticating user.
But as seen from the configuration that you provided , it seems to be transient:
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
And response has it persistent. Please check with that.
Audience in audience restriction contains a URI that identifies an intended audience. Azure AD sets the value of this element to the value of Issuer element of the AuthnRequest that initiated the sign- on. Please check and Configure Azure Active Directory B2C as a SAML IdP to your applications | Microsoft Docs
Conditions element specifies conditions that define the acceptable use of SAML assertions.Please check the allowed audience is valid.
References:
using-azure-ad-b2c-as-a-saml-idp-with-the-sp-initiated-flow(medium.com)
audience restriction and proxy restriction-saml-core-2.0-os.pdf

Using Managed Identity to call APIM endpoint

I am trying to make a call to the APIM endpoint in Azure from the function app using the Managed Identity of the function app. Not sure if there is any article I could take a reference from?
I believe you can do it using validate-jwt policy. The policy will look like this:
<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/{aad-tenant}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>{backend-api-application-client-id}</value>
</claim>
</required-claims>
</validate-jwt>
more info: 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
You should apply this policy type: https://learn.microsoft.com/en-us/azure/api-management/api-management-authentication-policies#ManagedIdentity

Handle JWT Token validation failute in Azure API Management Service

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.

JWT validation failure error in azure apim

I am currently trying to implement Oauth2.0 to protect API using below documentation
https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad
And currently using the DEMO CONFERENCE API provide by azure apim to test the implementation.
And currently receiving error during test in developer portal as :
"message": "JWT Validation Failed: Claim value mismatch: aud=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxx.."
Compared the token passed with the claim value by decoding it and its matching.
I have the jwt token validation policy as below
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-expiration-time="false" require-signed-tokens="false">
<openid-config url="https://login.microsoftonline.com/xxxxxxxxx-07c8-xxxxx-xxxx-xxxxxxxxx/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all" separator="-">
<value>xxxxxxxx-xxxxx-489e-a26e-xxxxxxxx</value>
</claim>
</required-claims>
</validate-jwt>
</inbound>
First, you need to validate your JWT token. Then when we register an application its getting registered with version V1 and Access token issuer comes with sts url and if we try to pass Access Token with V2 its failed V2 issuer is login.microsoft.com.
So fix is to go in manifest file "accessTokenAcceptedVersion": 2 for registered applications in AD. Refer to this issue.

Resources